Por qué fastDOOM es tan rápido
(fabiensanglard.net)- En una IBM PS/1 486-DX2 de 66 MHz, que en 1993 era una PC de gama alta, el DOOM original lograba 21.5 fps en
demo1, mientras que fastDOOM subía hasta 30.1 fps en las mismas condiciones, mostrando la diferencia que puede hacer la optimización de un port para DOS - fastDOOM nació a partir de PCDOOM v2, que combinaba el núcleo de Linux DOOM, el I/O de Heretic, APODMX y un I/O gráfico en Mode Y basado en el desensamblado inverso de
DOOM.EXE - Tras compilar y medir 52 releases y 3,042 commits, quedó claro que la mejora de rendimiento no vino de un compilador moderno por sí solo, sino de la acumulación de pequeñas optimizaciones
- Entre las mejoras más grandes están omitir el renderizado de la barra de estado, hacer inline de
FixedDiv, optimizar el recorrido del BSP, omitir renderizado de visplanes, eliminar indirecciones de punteros y separar ejecutables por renderer - El modo gráfico más conveniente depende de la CPU y del bus: en CPUs lentos conviene Mode Y, mientras que en CPUs más rápidas y entornos VLB/PCI pueden rendir mejor Mode 13h o VESA direct, aunque siguen existiendo limitaciones como VESA 2.0
La diferencia de rendimiento que se ve en una 486-DX2
- En una IBM PS/1 486-DX2 66 MHz “Mini-Tower” modelo 2168, al ejecutar el DOOM original con
doom.exe -timedemo demo1, el resultado es1710 gametics in 2783 realtics- DOOM no muestra los fps directamente, así que hay que calcular
1710/2783*35, lo que da como resultado 21.5 fps
- DOOM no muestra los fps directamente, así que hay que calcular
- En la misma máquina, al ejecutar
fdoom.exe -timedemo demo1, se obtiene1710 gametics in 1988 realtics, es decir, 30.1 fps - En mapas más pesados, como
demo1dedoom2, sube de 16.8 fps en el original a 24.9 fps en fastDOOM, lo que lo hace 48% más rápido - Eso sí, se eliminó el soporte para joystick y network gameplay, así que no es un port que conserve todas las funciones intactas
El linaje del código del que salió fastDOOM
- DOOM se desarrolló originalmente en una NeXT Workstation y estaba dividido en la mayor parte del código central y un pequeño subsistema de I/O, con una estructura fácil de portar
- La versión comercial para DOS usaba I/O para DOS escrito por id Software, pero no pudo liberarse tal cual como open source en 1997 por su dependencia de DMX, una biblioteca de sonido propietaria
- El código que se publicó fue la versión para Linux, organizada por Bernd Kreimeier mientras trabajaba en un proyecto de libro para explicar el motor
- La versión DOS PCDOOM v2 se reconstruyó con la siguiente combinación
- el núcleo de Linux DOOM
- el I/O de Heretic
- APODMX para imitar a DMX
i_ibm.c, el I/O gráfico reconstruido por ingeniería inversa a partir del desensamblado deDOOM.EXE
- fastDOOM tomó como punto de partida este PCDOOM v2
Seguimiento del cambio de rendimiento por releases y commits
- Victor “Viti95” Nieto publicaba fastDOOM con frecuencia, etiquetaba cada release y administraba los commits para que cada uno hiciera una sola cosa
- El historial Git de fastDOOM está compuesto por 3,042 commits, lo que permitió rastrear el cambio de rendimiento commit por commit
- Se descargaron 52 releases de fastDOOM, PCDOOM v2 y el
DOOM.EXEoriginal, y se generó con un programa en Go unRUN.BATpara ejecutar-timedemo demo1, montándolo con mTCPNETDRIVEpara las pruebas- Las condiciones fueron
DOOM.WAD, sound on y screen size 10 - Se ejecutó toda la suite 5 veces y se graficó el promedio de fps
- Las condiciones fueron
- Aunque PCDOOM v2 fue compilado con OpenWatcom 2, la mejora frente al
DOOM.EXEoriginal no fue tan grande, así que el avance de fastDOOM es difícil de explicar solo por usar un compilador moderno - El gráfico del tamaño de archivos muestra que el trabajo inicial fue orientado a aligerar el código mediante limpieza y eliminación
Cambios por release que elevaron el rendimiento
- Hacer timedemo a los 3,042 builds completos podía tomar unos 9 días, así que el benchmark a nivel commit se centró en
v0.1,v0.6,v0.8,v0.9.2yv0.9.7, donde los aumentos de velocidad fueron mayores -
fastDOOM v0.1
v0.1está compuesto por 220 commits- El parche más importante es e16bab8 del build 36
- La “Crispy optimization” convierte en no-op el renderizado cuando el porcentaje de la barra de estado no cambió, evitando el renderizado del scrap buffer y el blit a pantalla
- Ese único cambio elevó el rendimiento en 2 fps
- a9359d5 del build 167 hace inline de
FixedDivcomo macro - 9bd3f20 del build 207 toma optimizaciones de PSX Doom para mejorar el recorrido del BSP
- dc0f48e del build 212 hace inline de la función de renderizado de superficies horizontales
R_MakeSpans - De todos los commits, 100 fueron commits de eliminación, o sea, cerca de la mitad fue borrado de código
-
fastDOOM v0.6
-
fastDOOM v0.8
v0.8está compuesto por 282 commits- Como el sistema de sonido era inestable, el timedemo se hizo sin sonido y luego se corrigieron los fps
- Este release estaba enfocado en el text-mode renderer, y hubo regresiones en los builds 670 y 730 al perder la Crispy optimization
- Las mejoras principales fueron
-
fastDOOM v0.9.2 y v0.9.7
v0.9.2tiene 110 commits, y sus cambios principales fueron la optimización de la comparación de skyflatnum, la optimización deR_DrawColumnpara Mode Y y la limpieza del código deR_DrawSpanv0.9.7fue presentado como un release de 293 commits, pero la salida del comando muestra 294- En este release, ni siquiera múltiples rondas de benchmark lograron reducir el ruido
- Los cambios principales incluyen pruebas de cambios en x86 ASM, selección de CPU para 386SX y optimización de CR2, optimización de ESP para
R_DrawSpan386SX, código basado en renderizado ASM fuzz column y eliminación de comparaciones CMP por iteración de loop
Cuándo elegir Mode 13h, Mode Y o VESA direct
- fastDOOM explora múltiples optimizaciones para distintas combinaciones de CPU y buses de video, incluyendo 386, 486, Pentium, Cyrix e ISA, VLB y PCI
- En la IBM PS/1 486-DX2 de 66 MHz, una optimización que usaba Mode 13h en vez de Mode Y resultó más lenta
-
Mode 13h
- El hardware se encarga de distribuir los datos entre los cuatro bancos de VRAM de VGA, así que para la CPU se ve como un framebuffer lineal único de 320x200
- Como no se puede hacer double buffering en la VRAM, hay que usar buffering en RAM y luego copiar otra vez a VRAM, así que se escriben los bytes dos veces
- El motor debe bloquearse en VSYNC
-
Mode Y
- Permite acceder a cada banco VGA por separado, así que se puede hacer triple buffering en VRAM y escribir cada byte directamente en VRAM una sola vez
- La selección de banco requiere la instrucción lenta
OUT - Es posible replicar píxeles horizontales escribiendo a dos bancos VGA a la vez mediante latch, lo que permite un modo low-detail
- El renderizado del Specter invisible requiere leer de vuelta desde VRAM, por lo que es mucho más lento
- Según la explicación de John Carmack, DOOM usaba un modo planar entrelazado similar a Mode X en el modo VGA de 320×200×256, y rotaba entre tres display pages
- Hacer texture mapping directo sobre video memory podía dar entre 10% y 15% más velocidad en muchas tarjetas de video
- También permitía page flip sin tearing, a diferencia del buffering en memoria principal
- Heretic salió en 1994 y, con los cambios de hardware de ese momento, Mode 13h se volvió una opción más atractiva, así que Raven modificó el motor de DOOM en esa dirección
- fastDOOM ofrece varios ejecutables al usuario
FDOOM.EXEFDOOM13H.EXEFDOOMVBD.EXE
-
El Mode 13h y VESA direct de fastDOOM
- El Mode 13h de fastDOOM renderiza primero sobre un framebuffer lineal en RAM y, cuando termina toda la escena, lo copia a VRAM
- Como no fuerza VSYNC, puede haber flickering
- En buses ISA lentos de 8 bits se usa copia diferencial para transferir solo los píxeles modificados
- En buses más rápidos, como ISA de 16 bits, VLB o PCI, se copia todo el backbuffer con
REP MOVS - Según las pruebas de Viti95, el mejor modo para CPUs 486 es el VESA direct mode de 320×200, es decir,
FDOOMVBD.EXE- Combina las ventajas de Mode Y con el código de renderizado optimizado de Heretic
- Evita la instrucción
OUTsalvo una vez por frame al cambiar de buffer - Requiere una tarjeta gráfica VLB o PCI con LFB habilitado y soporte para VESA 2.0, y es más lento en modos low-detail y potato-detail
- La IBM 2168 no soporta VESA 2.0, así que al ejecutar
FDOOMVBP.EXEyFDOOMVBDaparece un error
Intentos que no funcionaron y la impresión general
- También se probaron los flags por procesador de OpenWatcom,
4r/4sy3r/3s, pero terminaron siendo descartados- Se probaron tanto los flags para 386 como para 486 de wcc386, y al final la versión para 386 siempre parecía ser más rápida
- Viti95 también tiene como objetivo cambiar el compilador de fastDOOM de OpenWatcom v2 a DJGPP, es decir, GCC
- Porque en el mismo código fuente, GCC parece generar código más rápido
- O bien sería deseable que OpenWatcom v2 mejorara para cerrar esa brecha de rendimiento
- El rendimiento de fastDOOM es resultado de aprovechar mejoras previas de Crispy, PSX, GBA y Lee Killough, y de sumar una gran cantidad de optimizaciones nuevas
- Algunas ideas y parte del código de Ken Silverman también llegaron a funciones de renderizado para CPUs UMC Green, logrando grandes mejoras de velocidad en ese hardware
- fastDOOM es un caso donde no hubo un cambio mágico único, sino miles de pequeñas optimizaciones acumuladas que lo convirtieron en un port para DOS más rápido que el DOOM original
1 comentarios
Opiniones en Hacker News
Es un buen ejemplo de que los cuellos de botella casi nunca están donde uno espera, así que hacen falta profiling y mediciones.
¿El renderizado del porcentaje en la barra de estado? Para alguien experto que conoce bien la arquitectura de Doom quizá era relativamente obvio, pero antes de medir jamás habría imaginado que fuera el cuello de botella.
https://www.granola.ai/blog/dont-animate-height
En el peor caso, mostrar el puntaje en la barra de estado consumía casi 1/6 de todo el frame, bajando el umbral a partir del cual se notaba la lentitud; el parche cambió la forma de guardar y mostrar el puntaje para que fuera casi de tiempo constante, haciéndolo incluso un poco más rápido que el mejor caso anterior.
https://www.smwcentral.net/?p=section&a=details&id=35746
El renderizado de la UI puede ser crítico por la transparencia, las capas, el redibujado y, sobre todo, por provocar asignaciones de memoria; comparar el valor anterior con el nuevo antes de redibujar ayuda muchísimo.
También hubo un proyecto en CSS donde las capas y la transparencia eran el cuello de botella, y en ese caso lo clave fue reducir la cantidad de capas.
Redujo una carga de 6 minutos a menos de 2 minutos.
https://nee.lv/2021/02/28/How-I-cut-GTA-Online-loading-times...
https://news.ycombinator.com/item?id=10974929
Quizá no sea el público objetivo real, pero NETDRIVE de mTCP me pareció interesante.
No sabía que incluso en una época tan antigua hubiera opciones útiles de almacenamiento en red, y al buscar encontré https://www.brutman.com/mTCP/mTCP_NetDrive.html, que está buenísimo.
Según dicen, NetDrive es un controlador de dispositivo que permite acceder desde DOS a una imagen de disco remota alojada en otra máquina como si fuera un dispositivo local montado con una letra de unidad.
Era lentísimo, así que los estudiantes perdían entre 5 y 10 minutos al comienzo de la clase esperando a que abriera el procesador de textos; las Xerox Alto también usaban montajes de red para sus unidades.
Si hay red, tarde o temprano alguien va a querer copiar archivos, y la forma más cómoda es hacer que parezca un sistema de archivos local.
DOS no tenía redes integradas, así que en ese aspecto estaba atrasado y había mucho que hacer a mano.
Permite acceso de lectura y escritura a un dispositivo de bloques en red sin controladores ni configuración del lado de DOS, aunque si se usa como si fuera un recurso compartido se rompe rápido; se parece más a un dispositivo de bloques.
Se siente como magia, pero parece que iPXE parchea el BIOS para enviar los accesos a disco vía iSCSI.
FTP y telnet obviamente existían, pero me da curiosidad si los montajes remotos se usaban en la práctica o si eran inviables por el bajo ancho de banda.
El hilo de GitHub en el que participa Ken Silverman es un tesoro.
Es sorprendente ver al autor de FastDOOM y a Ken profundizar en la eficiencia de registros de 486 y ciclos de reloj tan esotéricos.
Da gusto que todavía haya gente ocupándose de que Doom reciba mejoras de rendimiento.
Ese scripting fue literalmente mi primera “programación” y, en cierto sentido, le debo a KenS toda mi carrera y mi patrimonio, lo cual es genial.
En particular, me gustó la idea de usar CR2 y CR3 como registros scratchpad en el 386SX, donde el acceso a memoria es muy lento, y en el 386DX sin caché.
La técnica de usar ESP como contador de bucle sin desactivar interrupciones también es genial: garantiza que siempre apunte a una ubicación válida de la pila.
Una función de FastDOOM que no se trató mucho aquí son todos esos modos de video raros
Modo texto IBM MDA: https://www.youtube.com/watch?v=Op2tr2lGK6Y
EGA y Plantronics ColorPlus: https://www.youtube.com/watch?v=gxx6lJvrITk
CGA clásico azul y rosa: https://youtu.be/rD0UteHi2qM
CGA 320x200x16 usando el hack “ANSI from Hell”: https://www.youtube.com/watch?v=ut0V1nGcTf8
Hercules: https://www.youtube.com/watch?v=EEumutuyBBo
La mayoría parece correr más lento que VGA por cosas como el remapeo de colores
No me gustaría tener que correrlo en CGA y, si todavía la tuviera, probablemente necesitaría una build 286 para mi Tandy 1000 TL/2
Me recuerda a la Clean Architecture “moderna” de las aplicaciones backend
Me llamó la atención la parte de “IBM PS/1 486-DX2 66Mhz, ‘Mini-Tower’, modelo 2168. La computadora que siempre quise de adolescente pero no podía pagar”
Para 1992 más o menos estaba usando mi cuarta PC armada por mí, y las ferias de computación de KCS en Marlborough, MA, eran un recurso enorme para los que nos gustaba cacharrear
Compraba piezas, armaba una PC, la usaba un tiempo, la vendía y volvía a comprar piezas
A fines de 1992 estaba corriendo una 486-DX3 100 con un coprocesador matemático ULSI 487, y durante un tiempo fue la PC más rápida del campus, quizá la computadora más rápida
Era más rápida que varios modelos Pentium y no tenía errores matemáticos
Justifiqué la última build para mi tesis de honores, porque estaba simulando una planta de cogeneración a gas/diésel en una hoja de Excel de 21 páginas y los tiempos de recálculo eran demasiado largos
Mi carrera fue Ciencias Ambientales, pero toda mi trayectoria profesional terminó siendo en computación
¿Hacía falta ponérselo difícil a alguien que no pudo comprar una computadora así en 1992?
Además, no existió un “DX3”, y el DX4, el primer 486 de 100 MHz, salió en marzo de 1994, así que eso de haberlo tenido corriendo a fines de 1992 no parece correcto
La primera computadora de mi casa, sin contar una XT que nos dieron alrededor de 1992 y que ya estaba demasiado vieja, fue una 486-DX2 de 66MHz comprada a principios de 1995
Incluso décadas después, me hiere un poco el orgullo ver a alguien presumir de una computadora imposible que era más rápida que la mía pese a tener una ventaja de tres años
Por 1992 era un estudiante universitario pobre y pedí un préstamo de unos 2,000 dólares en la cooperativa de crédito para comprar una 486 DX2-50
En dinero de hoy, fue como gastar más de 4,000 dólares en una computadora bastante básica, y en esa máquina tenía arranque dual con DOS y Linux
Buscando, parece que el 487 estándar en realidad era un 486DX completo que desactivaba y reemplazaba al 486SX
¿Habrá sido otro coprocesador increíble que yo no conocía?
Les ganó por goleada
Además de lanzar versiones con frecuencia, Viti95 mostró una excelente disciplina con Git: un commit hace una sola cosa y cada release estaba etiquetada
https://fabiensanglard.net/fastdoom/#:~:text=one%20commit%20...
No entiendo qué significa Ibuprofen en “hasta conocer fastDOOM, me había resignado a tener que jugar tomando Ibuprofen”
Si el autor lo ve, en todo el documento el apellido de John Carmack está mal escrito como “Carnmack”
Una forma no cínica de explicar por qué el software moderno es lento y no recibe este tipo de optimizaciones es la hipótesis de estandarización/optimización
Cuando algo se vuelve estándar, le siguen las optimizaciones
Porque quieres ser el más rápido sin dejar de pasar todas las pruebas del estándar
Doom ya se volvió un juego estándar para portar a cualquier cosa, sea una CPU nueva o una tostadora, y pasa algo parecido con protocolos de email o estándares de navegador como WebRTC, QUIC, etc.
Las apps web modernas o las apps Electron no son rápidas porque están en una etapa de exploración
Se actualizan todos los días para nuevas necesidades de los usuarios y, en rendimiento, basta con que sean lo suficientemente rápidas como para no estorbar
Por eso las apps de IRC son muy rápidas, pero Slack y Teams inevitablemente siempre serán lentas
No es trivial volver a versiones anteriores y revisar mejoras y regresiones
Algunas optimizaciones pueden introducir bugs que se descubren después, y una función necesaria también puede empeorar el rendimiento
Por eso, tener pruebas de rendimiento que se ejecuten automáticamente antes de cada release hace la vida más fácil; si se detecta un problema de rendimiento, escribes una prueba de regresión como de costumbre
Lo que quiero decir, en definitiva, es que hay que hacer pruebas de rendimiento