1 pontos por GN⁺ 2025-03-05 | 1 comentários | Compartilhar no WhatsApp
  • Em um IBM PS/1 486-DX2 66MHz, que era um PC de alto desempenho em 1993, o DOOM original rodava demo1 a 21,5 fps, enquanto o fastDOOM chegava a 30,1 fps nas mesmas condições, mostrando a diferença trazida pela otimização do port para DOS
  • O fastDOOM partiu do PCDOOM v2, que combinava o núcleo do Linux DOOM, I/O do Heretic, APODMX e I/O gráfico em Mode Y baseado em desassemblagem reversa de DOOM.EXE
  • Após compilar e testar 52 releases e 3.042 commits, concluiu-se que o ganho de desempenho não veio de um compilador moderno sozinho, mas do acúmulo de pequenas otimizações
  • Entre as grandes melhorias estão omitir a renderização da barra de status, inline de FixedDiv, otimização da travessia de BSP, omissão da renderização de visplane, remoção de indireção por ponteiros e separação de executáveis por renderizador
  • Os modos gráficos têm vantagens e desvantagens dependendo da CPU e do barramento: em CPUs lentas, o Mode Y leva vantagem; em CPUs rápidas e ambientes VLB/PCI, Mode 13h ou VESA direct podem ser melhores, embora ainda existam restrições como VESA 2.0

Diferença de desempenho exposta no 486-DX2

  • No IBM PS/1 486-DX2 66MHz “Mini-Tower” modelo 2168, ao executar o DOOM original com doom.exe -timedemo demo1, o resultado é 1710 gametics in 2783 realtics
    • Como o DOOM não mostra fps diretamente, é preciso calcular 1710/2783*35, o que resulta em 21,5 fps
  • Na mesma máquina, ao executar fdoom.exe -timedemo demo1, aparece 1710 gametics in 1988 realtics, confirmando 30,1 fps
  • Em mapas mais pesados, como demo1 de doom2, o desempenho sobe de 16,8 fps no original para 24,9 fps no fastDOOM, ficando 48% mais rápido
  • Porém, o suporte a joystick e network gameplay foi removido, então não é um port que preserva todos os recursos originais

A linhagem de código de onde veio o fastDOOM

  • O DOOM foi desenvolvido originalmente em uma NeXT Workstation e tinha uma estrutura fácil de portar, dividida entre a maior parte do código principal e um pequeno subsistema de I/O
  • A versão comercial para DOS usava o I/O para DOS escrito pela id Software, mas por depender da biblioteca de som proprietária DMX, ela não pôde ser aberta como open source em 1997 do mesmo jeito
  • O código publicado foi a versão Linux organizada por Bernd Kreimeier enquanto ele trabalhava em um projeto de livro para explicar o engine
  • A versão DOS PCDOOM v2 foi reconstruída com a seguinte combinação
    • o núcleo do Linux DOOM
    • o I/O do Heretic
    • o APODMX para imitar o DMX
    • o I/O gráfico de i_ibm.c, obtido por engenharia reversa a partir da desassemblagem de DOOM.EXE
  • O fastDOOM adotou esse PCDOOM v2 como ponto de partida

Mudanças de desempenho rastreadas por releases e commits

  • Victor “Viti95” Nieto lançava o fastDOOM com frequência, marcava cada release com tags e mantinha a regra de um commit para cada tarefa
  • O histórico Git do fastDOOM é composto por 3.042 commits, o que permitiu rastrear a variação de desempenho commit a commit
  • Foram baixados 52 releases do fastDOOM, o PCDOOM v2 e o DOOM.EXE original, e um programa em Go gerou um RUN.BAT para executar -timedemo demo1, com os testes montados via mTCP NETDRIVE
    • As condições eram DOOM.WAD, som ligado e screen size 10
    • A suíte inteira foi executada 5 vezes e a média de fps foi plotada em gráfico
  • O PCDOOM v2 foi compilado com OpenWatcom 2, mas ainda assim melhorou pouco em relação ao DOOM.EXE original, então o avanço do fastDOOM é difícil de explicar apenas pelo uso de compilador moderno
  • No gráfico de tamanho dos arquivos, o trabalho inicial seguia a direção de deixá-los mais leves por meio de limpeza e remoção de código
    • bf0e983: remoção de sound recording
    • 5f38323: remoção das strings de error code
    • 8b9cac5: troca de TASM por NASM

Mudanças por release que aumentaram o desempenho

  • Fazer timedemo de todas as 3.042 builds levaria cerca de 9 dias, então o benchmark no nível de commit se concentrou em v0.1, v0.6, v0.8, v0.9.2 e v0.9.7, onde os ganhos de velocidade foram maiores
  • fastDOOM v0.1

    • v0.1 é composto por 220 commits
    • O maior patch é o e16bab8 da build 36
      • A “Crispy optimization” transforma a renderização em no-op quando a porcentagem da barra de status não mudou, evitando a renderização do scrap buffer e o blit para a tela
      • Só essa mudança trouxe um ganho de 2 fps
    • O a9359d5 da build 167 faz inline de FixedDiv como macro
    • O 9bd3f20 da build 207 incorpora uma otimização do PSX Doom e melhora a forma de percorrer o BSP
    • O dc0f48e da build 212 faz inline da função de renderização de superfícies horizontais R_MakeSpans
    • Entre todos os commits, 100 eram commits de remoção, ou seja, cerca de metade foi exclusão de código
  • fastDOOM v0.6

    • v0.6 é composto por 33 commits
    • O 22819fd da build 342 pula renderizações de visplane desnecessárias
    • O 40e0d4b da build 359 e o ccd296f da build 360 reduzem a indireção por player pointer
    • O f29e665 da build 369 faz inline do screenspace line splitter
  • fastDOOM v0.8

    • v0.8 é composto por 282 commits
    • Como o sistema de som estava instável, o timedemo foi feito sem som e depois o fps foi corrigido
    • Essa release tinha foco no renderizador em modo texto, e houve regressão nas builds 670 e 730 quando a Crispy optimization saiu
    • As principais melhorias foram as seguintes
      • f279b7d: separação de executáveis por renderizador, como FDOOM.EXE e FDOOM13H.EXE
      • 1874ee8: desativação do debugging do compilador
      • 6aae724: restauração da Crispy optimization
      • 1366ebf: compilar menos código sempre que possível
  • fastDOOM v0.9.2 e v0.9.7

    • v0.9.2 tem 110 commits, com destaque para a otimização da comparação de skyflatnum, otimização de R_DrawColumn para Mode Y e limpeza de código em R_DrawSpan
    • v0.9.7 foi apresentado como tendo 293 commits, mas a saída do comando mostra 294
    • Mesmo com vários benchmarks, essa release não conseguiu reduzir o ruído dos resultados
    • As principais mudanças incluem testes de alterações em ASM x86, seleção de CPU para 386SX e otimização de CR2, otimização de ESP para R_DrawSpan386SX, código baseado em renderização ASM fuzz column e remoção de comparações CMP por loop

Como escolher entre Mode 13h, Mode Y e VESA direct

  • O fastDOOM explora várias otimizações para combinações diversas de CPU e barramento de vídeo, incluindo 386, 486, Pentium, Cyrix e ISA, VLB, PCI
  • No IBM PS/1 486-DX2 66MHz, a otimização usando Mode 13h foi mais lenta do que o Mode Y
  • Mode 13h

    • O hardware distribui os dados entre os quatro bancos de VRAM da VGA, então para a CPU ele parece um framebuffer linear único de 320x200
    • Não é possível fazer double buffering na VRAM, então é preciso renderizar em RAM e depois copiar de novo para a VRAM, escrevendo os bytes duas vezes
    • O engine precisa ficar bloqueado no VSYNC
  • Mode Y

    • Permite acessar individualmente os bancos da VGA, tornando possível triple buffering em VRAM e escrita direta dos bytes na VRAM uma única vez
    • A seleção de banco exige a instrução lenta OUT
    • Escrevendo simultaneamente em dois bancos de VGA via latch, é possível replicar pixels horizontais e obter low-detail mode
    • A renderização do Specter invisível exige readback da VRAM, ficando muito mais lenta
    • Segundo a explicação de John Carmack, o DOOM usava no modo VGA 320×200×256 um modo planar intercalado semelhante ao Mode X e alternava entre três display pages
    • Fazer texture mapping diretamente na video memory podia render 10% a 15% mais velocidade em muitas placas de vídeo
    • Também permitia page flip sem tearing, algo que o buffering em main memory não conseguia
    • Heretic saiu em 1994, e com a mudança do hardware na época o Mode 13h se tornou uma opção mais atraente, levando a Raven a adaptar o engine nessa direção
    • O fastDOOM oferece vários executáveis ao usuário
    • FDOOM.EXE
    • FDOOM13H.EXE
    • FDOOMVBD.EXE
  • Mode 13h e VESA direct no fastDOOM

    • No Mode 13h do fastDOOM, a renderização é feita em um framebuffer linear único na RAM e, quando a cena inteira termina, ela é copiada para a VRAM
    • Como o VSYNC não é forçado, pode haver flickering
    • Em barramentos ISA 8-bit lentos, usa-se cópia diferencial, enviando apenas os pixels alterados
    • Em barramentos mais rápidos, como ISA 16-bit, VLB e PCI, o backbuffer inteiro é copiado com REP MOVS
    • Nos testes de Viti95, o melhor modo para CPUs 486 é o VESA direct mode de 320×200, FDOOMVBD.EXE
      • Ele combina as vantagens do Mode Y com o código de renderização otimizado do Heretic
      • Evita a instrução OUT, exceto por uma vez por frame na troca de buffer
      • Exige placa gráfica VLB ou PCI com LFB ativado e suporte a VESA 2.0, além de ficar lento nos modos low-detail e potato-detail
    • O IBM 2168 não suporta VESA 2.0, então FDOOMVBP.EXE e FDOOMVBD geram erro ao executar

Tentativas que não funcionaram e impressão geral

  • Também foram testadas as flags específicas de processador do OpenWatcom, 4r/4s e 3r/3s, mas a tentativa foi abandonada
    • Foram testadas as flags para 386 e 486 do wcc386, e no fim a versão para 386 sempre parecia mais rápida
  • Viti95 também quer trocar o compilador do fastDOOM de OpenWatcom v2 para DJGPP, ou seja, GCC
    • Isso porque o GCC parece gerar código mais rápido a partir do mesmo source
    • Outra alternativa desejável seria melhorar o OpenWatcom v2 para reduzir essa diferença de desempenho
  • O desempenho do fastDOOM é resultado do reaproveitamento de melhorias já existentes de Crispy, PSX, GBA e Lee Killough, junto com uma grande quantidade de novas otimizações
  • Ideias e parte do código de Ken Silverman também entraram nas funções de renderização para CPU UMC Green, trazendo um grande ganho de velocidade nesse hardware
  • O fastDOOM é um caso em que não houve uma mudança mágica única, mas sim milhares de pequenas otimizações acumuladas até virar um port para DOS mais rápido que o DOOM original

1 comentários

 
GN⁺ 2025-03-05
Comentários do Hacker News
  • Este é um bom exemplo de como gargalos normalmente não estão onde se espera, então profiling e medição são necessários
    Renderização de porcentagem na barra de status — para um especialista que conhece bem a estrutura do Doom isso talvez fosse relativamente óbvio, mas antes disso eu jamais teria imaginado que fosse o gargalo

    • Por exemplo, houve um caso em que um app estava usando 60% de CPU e 25% de GPU sem motivo aparente, e no fim descobriram que era por causa de uma pequena animação em CSS
      https://www.granola.ai/blog/dont-animate-height
    • Isso lembra um caso de otimização em Super Mario World para SNES, em que a exibição da pontuação do jogador era muito ineficiente e consumia cerca de 1/6 do tempo de frame
      No pior caso, quase 1/6 do frame inteiro era gasto mostrando a pontuação na barra de status, o que reduzia o limite antes de o jogo ficar lento; o patch mudou o armazenamento e a exibição da pontuação para algo próximo de tempo constante, ficando até um pouco mais rápido que o melhor caso anterior
      https://www.smwcentral.net/?p=section&a=details&id=35746
    • Do ponto de vista de quem desenvolve jogos, esse tipo de lentidão é comum
      Renderização de UI pode ser crítica por causa de transparência, camadas, redesenho e especialmente por provocar alocação de memória; comparar o valor anterior com o novo antes de redesenhar ajuda bastante
      Também já houve projeto em CSS em que camadas e transparência eram o gargalo, e ali o principal foi reduzir a quantidade de camadas
    • Meu exemplo favorito é o problema do tempo de carregamento do GTA Online, que no fim acontecia por causa do processamento incorreto de um arquivo JSON de 10 MB, e foi alguém de fora da organização que rastreou isso
      O carregamento de 6 minutos caiu para menos de 2 minutos
      https://nee.lv/2021/02/28/How-I-cut-GTA-Online-loading-times...
    • Isso me lembra o caso em que o npm ficou 2x mais lento por causa de uma barra de progresso chamativa no terminal
      https://news.ycombinator.com/item?id=10974929
  • Talvez eu não seja exatamente o público-alvo, mas o NETDRIVE do mTCP foi interessante
    Eu não sabia que existiam opções utilizáveis de armazenamento em rede já naquela época; fui procurar e encontrei https://www.brutman.com/mTCP/mTCP_NetDrive.html, o que é realmente muito legal
    Dizem que o NetDrive é um driver de dispositivo que permite ao DOS acessar uma imagem de disco remota hospedada em outra máquina como se fosse um dispositivo local anexado a uma letra de unidade

    • No laboratório de informática da escola, no começo dos anos 90, havia cerca de 25 Mac Plus conectados em daisy chain a um Mac II por AppleTalk, e todos os Plus montavam o sistema de arquivos do Mac II
      Era extremamente lento, então os alunos perdiam de 5 a 10 minutos no começo da aula só para abrir o processador de texto; o Xerox Alto também usava montagens de rede em drives
      Quando existe rede, alguém logo quer copiar arquivos, e a forma mais conveniente é fazer isso parecer um sistema de arquivos local
      O DOS ficava para trás nisso por não ter rede embutida, então havia muito mais coisa a fazer manualmente
    • Existe um truque elegante para fazer boot de rede do DOS a partir de um alvo iSCSI com iPXE
      Sem drivers nem configuração no lado do DOS, isso permite acesso de leitura e escrita a um dispositivo de bloco em rede; se usar como compartilhamento, quebra rapidamente, e se parece mais com um dispositivo de bloco
      Parece mágica, mas imagino que o iPXE faça patch no BIOS para enviar o acesso a disco via iSCSI
    • Fico curioso se na época do DOS também existiam NAS ou montagens WebDAV
      FTP e telnet obviamente existiam, mas fico pensando se montagens remotas eram realmente usadas ou se eram inviáveis por causa da baixa largura de banda
  • A thread no GitHub com participação de Ken Silverman é um tesouro
    É impressionante ver o autor do FastDOOM e o Ken mergulhando nas complexidades de eficiência de registradores de 486 e ciclos de clock
    É bom saber que ainda existe gente cuidando para que o Doom continue recebendo melhorias de desempenho

    • Fazia muito tempo que eu não lembrava do KenS, mas nos anos 90 ele era bem ativo na cena de modding de Duke3D
      Aquele scripting foi literalmente minha primeira “programação” e, de certo modo, devo minha carreira e meu patrimônio inteiros ao KenS, o que é incrível
    • No ano passado eu estava fazendo um motor de renderização 2.5D parecido e mandei um e-mail para Ken Silverman sobre uma parte pouco conhecida da Build Engine, e ele respondeu como se tivesse trabalhado nisso ontem
    • Há verdadeiras joias ali dentro
      Gostei especialmente da ideia de usar CR2 e CR3 como registradores scratchpad no 386SX, em que acesso à memória é muito lento, e no 386DX sem cache
      A técnica de usar o ESP como contador de loop sem desabilitar interrupções também é genial, porque garante que ele sempre aponte para uma posição válida da pilha
  • Um recurso do FastDOOM que não foi muito abordado aqui são os vários modos de vídeo esquisitos
    Modo texto IBM MDA: https://www.youtube.com/watch?v=Op2tr2lGK6Y
    EGA & Plantronics ColorPlus: https://www.youtube.com/watch?v=gxx6lJvrITk
    CGA clássico azul e rosa: https://youtu.be/rD0UteHi2qM
    CGA 320x200x16 usando o hack “ANSI from Hell”: https://www.youtube.com/watch?v=ut0V1nGcTf8
    Hercules: https://www.youtube.com/watch?v=EEumutuyBBo
    A maioria parece rodar mais devagar que VGA, provavelmente por causa de tarefas como remapeamento de cores

    • Seria legal se também suportasse o Tandy Graphics Adapter
      Se tivesse de rodar em CGA, eu não ia gostar, e se ainda estivesse vivo, provavelmente precisaria de uma build 286 para um Tandy 1000 TL/2
    • É realmente muito legal e mostra bem por que esses aspectos do jogo devem ser separados
      Lembra a Clean Architecture “moderna” de aplicações de backend
    • O modo texto IBM MDA é horrível... por isso é bom
  • A parte “IBM PS/1 486-DX2 66Mhz, ‘Mini-Tower’, model 2168. Um computador que eu sempre quis quando era adolescente, mas nunca pude comprar” chama atenção
    Por volta de 1992, eu já estava usando o quarto PC que montei, e a feira de computadores da KCS em Marlborough, MA, era um recurso enorme para quem gostava de fuçar
    Eu comprava peças, montava um PC, usava por um tempo, vendia e depois comprava peças de novo
    No fim de 1992, eu estava rodando um 486-DX3 100 com um coprocessador matemático ULSI 487 e, por um tempo, dava para dizer que era o PC mais rápido do campus, talvez até o computador mais rápido
    Era mais rápido que vários modelos de Pentium e não cometia erros matemáticos
    A última montagem se justificou porque eu estava simulando uma usina de cogeração a gás/diesel numa planilha Excel de 21 páginas para minha monografia de conclusão, e o tempo de recálculo era longo demais
    Minha formação era em ciências ambientais, mas minha carreira acabou sendo toda em computadores

    • Não sei se isso é para dizer “uau”
      Será que precisava fazer alguém que não pôde comprar um computador desses em 1992 se sentir mal desse jeito
      Além disso, não existia “DX3”, e o DX4, o primeiro 486 de 100MHz, saiu em março de 1994, então não parece bater essa história de estar rodando isso no fim de 1992
      O primeiro computador da minha família, desconsiderando um XT já muito ultrapassado que recebemos por volta de 1992, foi um 66MHz 486-DX2 comprado no começo de 1995
      Mesmo depois de décadas, ver alguém se gabando estranhamente de um computador impossível, que teria sido mais rápido que o meu apesar de um atraso de três anos, ainda mexe com meu orgulho sem motivo
    • Com certeza traz lembranças
      Eu era um estudante universitário pobre por volta de 1992 e peguei um empréstimo de cerca de 2.000 dólares na cooperativa de crédito para comprar um 486 DX2-50
      Em dinheiro de hoje, isso dá mais de 4.000 dólares por um computador bem básico, e eu fazia dual boot de DOS e Linux naquela máquina
    • 486DX com 487? Eu achava que o 487 só servia para chips SX
      Fui pesquisar e o 487 padrão era na verdade um 486DX completo que desativava e substituía o 486SX original
      Será que era algum outro coprocessador incrível que eu desconhecia
    • Se “era mais rápido que vários modelos de Pentium e não cometia erros matemáticos”, então é motivo total para se gabar
      Deu um baile mesmo
  • Além de lançar com frequência, Viti95 mostrou uma excelente disciplina de Git, com cada commit fazendo uma única coisa e cada release recebendo uma tag
    https://fabiensanglard.net/fastdoom/#:~:text=one%20commit%20...

  • Em “antes de conhecer fastDOOM, eu tinha me resignado a jogar tomando Ibuprofen”, não entendo o que Ibuprofen quer dizer

    • Talvez queira dizer dor de cabeça causada pela baixa taxa de quadros
  • Se o autor vir isso, ao longo do documento o sobrenome de John Carmack está escrito errado como “Carnmack”

  • Uma forma não cínica de ver por que softwares modernos são lentos e não recebem esse tipo de otimização é a hipótese da padronização/otimização
    Quando algo vira padrão, a otimização vem em seguida
    Porque você passa a querer ser o mais rápido possível enquanto ainda passa em todos os testes do padrão
    Doom agora também virou um jogo padrão para portar para qualquer coisa, seja uma CPU nova ou uma torradeira, e protocolos de e-mail ou padrões de navegador como WebRTC e QUIC são parecidos
    Apps web modernos ou apps em Electron não são rápidos porque ainda estão na fase de exploração
    Eles são atualizados todos os dias para atender a novas demandas dos usuários e, em termos de desempenho, basta que sejam rápidos o suficiente para não atrapalhar
    É por isso que um app de IRC pode ser muito rápido, enquanto Slack e Teams inevitavelmente estão sempre lentos

  • Voltar pelas versões para verificar melhorias e regressões não é algo trivial
    Algumas otimizações podem introduzir bugs descobertos só depois, e recursos realmente necessários também podem piorar o desempenho
    Por isso, ter testes de desempenho rodando automaticamente antes de cada release facilita a vida, e se um problema de desempenho for encontrado, basta escrever um teste de regressão como de costume
    No fim, o que eu quero dizer é: façam testes de desempenho