- O RV64 DynaRec do Box64 evoluiu, em um ano, de um estágio em que rodava apenas jogos nativos simples de Linux para um ponto em que consegue executar The Witcher 3 em um PC RISC-V
- O avanço foi impulsionado pela possibilidade de usar placas de vídeo AMD, o que reduziu as limitações de OpenGL e permitiu testar mais programas x86 e corrigir bugs
- O backend RV64 implementa menos instruções x86 do que o backend ARM64, e as instruções AVX ainda seguem como um grande desafio no lado RISC-V
- O RISC-V não tem extração/inserção de intervalos de bits nem instruções atômicas de 16 bytes, o que aumenta o custo de tradução na emulação de x86 em comparação com AArch64 e LoongArch64
- The Witcher 3 realmente roda com o box64, chegando a 15 fps no jogo e a velocidade total no menu principal
Até The Witcher 3 rodar no RISC-V
- Há 1 ano, o RV64 DynaRec só conseguia rodar jogos nativos de Linux relativamente fáceis de executar, como Stardew Valley e World of Goo
- Na época, havia dois grandes gargalos
- Muitos bugs no DynaRec ainda restavam no processo de adicionar rapidamente instruções x86_64 ao backend RISC-V
- As GPUs integradas IMG do VisionFive 2 e do LicheePi 4A suportavam apenas OpenGL ES, e não OpenGL
- Com o gl4es, foi possível obter algum suporte a OpenGL e rodar jogos como Stardew Valley, mas isso não bastava para jogos Linux mais pesados nem para jogos Windows em geral
- O Milk-V Pioneer da Sophgo é um PC RISC-V de 64 núcleos com slot PCIe para instalar placa de vídeo
- Outro colaborador, xctan, encontrou uma forma de “conectar” uma placa de vídeo AMD ao VisionFive 2 por meio da interface M.2
- Com o uso de placas AMD, a variedade de programas x86 que podiam ser testados aumentou, e houve uma grande leva de correções de bugs no RV64 DynaRec e adição de instruções x86
- Como resultado, The Witcher 3 funcionou logo na primeira vez em que foi executado
Estado atual do RV64 DynaRec
- O conjunto de instruções x86 é muito grande, e o volume de implementação varia conforme o backend
- O backend ARM64 implementa, no total, mais de 1.600 instruções x86
- O backend RV64 implementa cerca de 1.000 instruções x86
- Dessas, mais de 300 são instruções AVX recém-suportadas, que ainda não foram implementadas no RISC-V
- Mesmo na implementação de instruções SSE, o RISC-V fica em desvantagem de desempenho
- O backend RV64 implementa instruções SSE com instruções escalares
- O AArch64 usa a extensão Neon, e o LoongArch64 usa a extensão LSX
- Por causa dessa diferença, o desempenho é consideravelmente inferior ao dos outros dois backends
- O RISC-V tem a extensão vetorial RVV
- O Milk-V Pioneer suporta a extensão xtheadvector, uma variante do RVV 0.7.1
- O SoC SpacemiT K1/M1 suporta o RVV 1.0 ratificado
- Banana Pi F3 e Milk-V Jupiter, com esse SoC, já podem ser comprados
- O box64 recebeu recentemente suporte básico a RVV e implementação de algumas instruções SSE comuns
- O trabalho com RVV ainda está em estágio muito inicial, então por enquanto não ajuda no desempenho
Instruções RISC-V especialmente ausentes para emulação x86
- Do ponto de vista da emulação x86, o RISC-V é a arquitetura de menor expressividade entre as três suportadas
- Em comparação com AArch64 e LoongArch64, faltam instruções convenientes, o que exige mais instruções para emular a mesma operação
- Faltam principalmente duas capacidades importantes
- Selecionar um determinado intervalo de bits de um registrador e levá-lo para outro registrador
- Inserir parte dos bits de um registrador em uma faixa específica de outro registrador
- LoongArch64 e AArch64 têm instruções equivalentes para isso
- O LoongArch64 usa
BSTRPICK.DeBSTRINS.D - O ARM64 usa os opcodes
UBFXeBFI
- O LoongArch64 usa
- O RISC-V não tem instruções correspondentes nem em extensões oficiais nem em extensões de fabricantes
O custo de tradução mostrado pelo exemplo ADD AH, BL
- A ISA x86 tende a preservar os bits que não são alterados, por isso a manipulação de registradores parciais é importante
- No caso de
ADD AH, BL, o box64 precisa fazer o seguinte- Extrair o byte menos significativo de
RBX - Somá-lo ao segundo byte menos significativo de
RAX - Reinserir o resultado no segundo byte menos significativo de
RAX - Manter os demais bytes de
RAXinalterados
- Extrair o byte menos significativo de
- No LoongArch64, isso pode ser implementado de forma simples e direta com
BSTRPICK.D,ADDeBSTRINS.D - No RISC-V, a mesma operação exige combinar shifts, máscaras,
AND,ORetc., chegando a 10 instruções - Esse tipo de caso não é isolado; o x86 tem muitas instruções semelhantes, o que torna a implementação em RISC-V mais trabalhosa
Limitações das instruções atômicas de 16 bytes
- O x86 tem instruções com prefixo LOCK para operações atômicas lock-free
- O box64 as emula principalmente com sequências LR/SC
- LR/SC significa Load-Reserved / Store-Conditionally
- Por exemplo,
LOCK ADD [RAX], RCXé gerado na formaLR.D,ADD,SC.De desvio condicional
- Se o endereço em
RAXnão estiver alinhado, a situação fica mais complexa, mas em geral essa abordagem funciona bem - O problema é o
LOCK CMPXCHG16B- Essa instrução compara
RDX:RAXcom 16 bytes da memória - Dependendo da condição, troca
RCX:RBXcom esse endereço de memória
- Essa instrução compara
- AArch64 e LoongArch64 têm algumas instruções atômicas de 16 bytes que podem ser usadas na implementação
- O RISC-V não tem instruções equivalentes, então não consegue uma implementação tão completa quanto as outras arquiteturas
- Muitos programas, incluindo jogos em Unity, usam
LOCK CMPXCHG16B
Resultado prático
- Apesar das limitações restantes, The Witcher 3 roda em RISC-V com o box64
- O desempenho chega a 15 fps dentro do jogo
- No menu principal, ele roda em velocidade total
- Para um resultado obtido em uma máquina que não foi projetada para rodar jogos AAA, o nível não é ruim
1 comentários
Opiniões do Hacker News
Como alguém que não trabalha com chips, tenho curiosidade sobre o que um engenheiro de software precisa fazer de diferente ao criar software voltado para RISC-V
Fico pensando se o tamanho dos executáveis aumenta a ponto de exigir uma otimização agressiva da localidade de cache, e também se existem tipos de software, como jogos ou servidores web, que se encaixam melhor em CISC ou em RISC
Não há muita coisa essencial a mudar na abordagem de software; a maior diferença em relação ao x86-64 é que há 32 registradores, então é possível manter mais valores intermediários antes que eles precisem ser empurrados para a pilha, mas ARM também tem 32, então é parecido. Em geral, a menos que você esteja fazendo micro-otimizações, não é algo com que precise se preocupar muito
Indo mais aos detalhes, a extensão vetorial (V/RVV) não faz parte da ISA rv64gc básica, então, dependendo do alvo, talvez você não consiga se beneficiar de otimizações SIMD; popcount e contagem de zeros à esquerda/à direita também não existem no rv64gc básico e exigem Zbb. Além disso, uma seleção sem desvio como
a ? b : cprecisa de 4 a 5 instruções no rv64gc básico e de 3 instruções com Zicond, enquanto em x86-64 e aarch64 dá para fazer com 1Os perfis RISC-V resolvem em certa medida os dois primeiros problemas. Por exemplo, o Android exige rva23, que requer RVV, Zbb, Zicond etc. Mas, se uma distribuição Linux mirar rva20/rv64gc, essas extensões acabam ficando, na prática, inutilizáveis por muito tempo em código pré-compilado que não inclua despacho dinâmico. O x86-64 tem um problema parecido, mas no ARM há bem menos extensões, então isso pesa menos; a maior exceção é SVE, que ainda não tem suporte amplo
A maior diferença é o modelo de memória fraco, uma característica também presente na maioria das arquiteturas não x86, como ARM, e, para começo de conversa, o código não deveria depender de um modelo de memória forte
A densidade do código executável no x86 não é tão boa por motivos históricos, então o tamanho dos executáveis não aumenta tanto quanto se poderia imaginar. RISC-V com a extensão de instruções comprimidas e ARM de 32 bits com a extensão Thumb são bastante densos
O importante não é CISC versus RISC, mas sim a existência e a qualidade de instruções vetoriais e extensões criptográficas. Codificação/decodificação de vídeo depende bastante de instruções vetoriais para bom desempenho, e criptografia de disco inteiro ou hashing pode se beneficiar de instruções dedicadas que aceleram algoritmos específicos como AES e SHA256
O ponto central é mais se livrar das patentes da ARM e fazer um recomeço com base nas lições aprendidas
Isso me lembra um russo famoso que rodou Atomic Heart no Elbrus 8S
O Elbrus tem um tradutor nativo e, pelo que sei, ele é bem razoável. Atomic Heart ficava meio jogável, por volta de 15 a 25 fps
O texto deixa um pouco a desejar na explicação “básica”. Achei que tivesse sido executado usando algo como um port do Wine, mas na verdade parece que eles implementaram de alguma forma a ISA x86_64 sobre um chip RISC-V
Seria bom se alguém pudesse explicar melhor como essa parte funciona
Embora seja um emulador, algumas bibliotecas “de sistema”, como libc, libm, SDL e OpenGL, usam versões nativas, por isso é fácil integrá-lo à maioria das aplicações e, em alguns casos, o desempenho pode ser surpreendentemente alto. O Wine também pode ser compilado e executado de forma nativa
É um resultado impressionante. É uma quantidade enorme de trabalho e, em alguns casos, parece estar batendo nos limites do RISC-V
Instruções de coleta/dispersão de bits parecem algo que deveria entrar como extensão
No contexto da emulação x86, é interessante a parte em que, entre as três arquiteturas suportadas, RISC-V é a menos expressiva
Nas aulas de história da ciência da computação, aprendi que RISC significa computador com conjunto reduzido de instruções, mas, ao ver propostas de perfis RISC-V e textos recentes, há muitos argumentos do tipo “só precisamos de mais algumas instruções para equivalência funcional”. Entendo que RISC-V seja, para muita gente, uma alternativa conveniente a outras plataformas, mas também fico curioso se isso significa que o sonho do RISC morreu
Pelo que lembro de ter lido da especificação RISC-V, ela era bem rigorosa em não adicionar instruções “combo”, já que sequências comuns de instruções podem ser fundidas no front-end
A insuficiência do RISC-V em comparação com x86/ARM parece vir menos de fundamentalismo RISC e mais do fato de a especificação ter começado por chips embarcados bem básicos e, com o tempo, ter adicionado extensões para CPUs de aplicação. O RV32I básico nem sequer tem multiplicação de inteiros. Infelizmente, levou tempo demais para encerrar as discussões sobre manipulação de bits e extensões SIMD/vetoriais, e o resultado é a lacuna de recursos de que se fala agora
Só que, na mesma medida, algumas instruções convenientes para alto desempenho ficam de fora
Um pipeline simples também tem a vantagem de consumir menos recursos de engenharia das equipes que fazem projetos de alto desempenho, permitindo gastar mais tempo com otimização
RISC é, em geral, uma filosofia de simplificação, mas o grau varia. MIPS é tão simplificado quanto RISC-V, mas ARM e POWER são mais pragmáticos, e parecem não ter grandes problemas em enfrentar x86 no segmento de alto desempenho
O mercado de processadores tem vários nichos além de executar aplicações, como embarcados e aceleradores. No nicho específico de núcleos de aplicação sou um pouco pessimista em relação ao RISC-V, mas, olhando de forma mais ampla, ele tem muito potencial, pode até dominar alguns nichos comerciais, e é excelente como ferramenta de ensino e pesquisa
As características clássicas do RISC são que a maioria das instruções de manipulação de dados opera apenas em registradores, e as instruções de memória são, em geral, apenas load/store para registradores, exigindo muitos registradores. Como é preciso manipular a pilha diretamente para passar parâmetros, a pilha também é construída diretamente, e chamadas são implementadas com instruções básicas de load/store diretamente no registrador de ponteiro de instrução, sem instruções CALL/JSR. A codificação das instruções é previsível e todas as instruções têm o mesmo tamanho. Várias arquiteturas RISC também tinham um registrador que sempre lia 0 e no qual não era possível escrever, usado para definir valores como 0
Essa abordagem funcionou, mas depois a execução fora de ordem e SIMD reduziram sua importância. O fluxo bruto de instruções é mais uma declaração do caminho para chegar ao resultado desejado; não significa que a CPU vá de fato executá-lo literalmente. Por trás disso há execução especulativa, predição de desvios e renomeação de registradores. SIMD se aproxima de um grande espaço de registradores e de instruções que operam sobre todos os valores dentro dele. No fim, execução fora de ordem e SIMD assumiram o protagonismo
Em teoria, se o código-fonte original fosse compilado para RISC, o binário resultante seria completamente diferente e talvez essas instruções específicas não fossem necessárias
Na prática, não parece provável que alguém vá compilar esses jogos de fato para RISC-V
Na captura de tela, a RAM aparece como 31 GB, o que certamente é mais do que a configuração máxima da placa de desenvolvimento mencionada. Estão usando outra coisa aqui?
Hoje em dia, seria melhor usar uma das opções mais recentes, com vários núcleos mais rápidos que implementam RVA22 e RVV 1.0
Isso é o 86Box? Foi divertido lembrar da época em que comprei um Amstrad PC1512
Ele ficou muito mais interessante depois que adicionei duas placas de disco rígido de 500 MB e uma expansão de memória de 128 KB, levando-o a 640 KB. No começo, havia apenas dois disquetes de 360 KB, e alguns anos depois adicionei uma placa de disco rígido de 32 MB. Também tinha Borland TurboPascal e Zortech C. Bons tempos
Ainda assim, lembro da época de usar um Amstrad PC1512
Fico imaginando se algum dia veremos um sistema com algumas CPUs RISC-V grandes e uma “GPU” implementada com um monte de CPUs RISC-V pequenas
Imagino que seria algo com recursos vetoriais adequados; como pergunta paralela, também fico curioso se uma abordagem vetorial clássica, em vez de SIMD empacotado, poderia ser útil em GPUs
Entre os feitos tecnicamente impressionantes de The Witcher 3 também houve o port para Switch, que rodava muito bem
Ele mostra o quanto é possível fazer com otimização, e quantos recursos são desperdiçados no PC por causa de otimização ruim
Não é uma comparação equivalente; a qualidade do que é exibido na tela é muito diferente, então é difícil concluir que se trata de má otimização no PC
Seria bom se esse tipo de feedback no nível da ISA chegasse ao pessoal do RVI
Conferi ontem [1], e o exemplo do texto já pode ser feito com 4 instruções RISC-V. Só que é um pouco complicado de imaginar
# a0 = rax, a1 = rbxslli t0, a1, 64-8rori a0, a0, 16add a0, a0, t0rori a0, a0, 64-16[1] https://www.reddit.com/r/RISCV/comments/1f1mnxf/box64_and_ri...
Na verdade, a ausência de extração de campos de bits é um erro tão evidente que é meu exemplo favorito para mostrar como a ISA RISC-V é absurda. O segundo é a falta de modos de endereçamento decentes
Alguns projetos melhores de RISC-V de fato implementam instruções customizadas para isso. Por exemplo, há a BEXTM do Hazard3: https://github.com/Wren6991/Hazard3/blob/stable/doc/hazard3....