⬅️ Volver a los laboratorios · 📚 Parte 21 · 🎓 Clase 352 (capstone)
Banco de pruebas de las cuatro técnicas que sostienen un motor moderno: disposición de datos, gestión de memoria, particionamiento espacial y paralelismo. Cada una está implementada dos veces —la forma natural y la forma rápida— y el proyecto mide las dos y comprueba que dan el mismo resultado.
Ese es el punto entero del laboratorio. Una optimización que cambia el resultado no es una optimización, es un bug rápido; y una optimización sin medición no es ingeniería, es superstición. Aquí no hay ninguna cifra escrita a mano: todas las que verás las produce tu máquina.
godot --headless --path labs/advanced-engineering/solucion --script res://pruebas/ingenieria_test.gd
DOD 100000 partículas: SoA 16.14 ms · AoS 61.74 ms · ×3.8
Memoria 10k objetos: pool 22.49 ms · nuevos 32.56 ms · ×1.4
Espacial 5000 objetos, 200 consultas: fuerza bruta 85.05 ms · rejilla 1.73 ms · ×49.2
Jobs 200000 elementos (8 hilos lógicos): secuencial 189.49 ms · paralelo 115.36 ms · ×1.64
== 33 comprobaciones, 0 fallos ==
Tus números serán distintos, y eso es correcto. Dependen de tu CPU, de tu caché y de cuántos hilos tengas disponibles de verdad. Lo que no cambia es el orden de magnitud de cada mejora, y es informativo por sí mismo:
| Pieza | Archivo | Clase |
|---|---|---|
| AoS frente a SoA, con datos fríos separados | dod/particulas.gd |
339 |
| Swap-remove para compactar sin huecos | dod/particulas.gd |
339 |
| Pool de objetos con conteo de desbordes | memoria/pool.gd |
340 |
| Arena / stack allocator con marcas | memoria/arena.gd |
340 |
Reparto en grupos sobre WorkerThreadPool |
jobs/paralelo.gd |
341 |
| Grafo de tareas con detección de ciclos | jobs/grafo_tareas.gd |
341 |
| Rejilla uniforme + fuerza bruta de referencia | espacial/rejilla.gd |
342 |
| Hash espacial para mundos sin límites | espacial/hash_espacial.gd |
342 |
| Medición con calentamiento y mediana | pruebas/ayuda.gd |
243 |
33 comprobaciones, y ninguna exige una velocidad concreta. Los milisegundos se imprimen; lo que se comprueba es la corrección. La única comprobación de rendimiento es una guardia de regresión —que repartir el trabajo no cueste más que hacerlo entero— porque exigir una aceleración concreta convierte el test en una lotería del hardware, y un test que falla por motivos ajenos al código acaba desactivado. Un test desactivado no protege nada.
inicio/ es el mismo proyecto con seis TODO numerados. Compila y arranca desde el primer momento; lo que no hace es funcionar:
godot --headless --path labs/advanced-engineering/inicio --quit-after 60
correcto: SoA==AoS=true · rejilla==fuerza_bruta=false (29 objetos en el radio)
| TODO | Dónde | Qué se aprende |
|---|---|---|
| 1 | dod/particulas.gd · compactar |
Swap-remove sin dejar huecos, y por qué no se avanza el índice tras intercambiar |
| 2 | memoria/pool.gd · obtener / devolver |
Reutilizar de verdad, e indexar por identidad y no por contenido |
| 3 | espacial/rejilla.gd · consultar_radio |
La celda es una aproximación: falta la comprobación exacta de distancia |
| 4 | espacial/rejilla.gd · _celda |
int() trunca hacia cero: el fallo solo aparece en coordenadas negativas |
| 5 | jobs/paralelo.gd · trabajo_paralelo |
Repartir en grupos y esperar: sin la espera el fallo es intermitente |
| 6 | jobs/paralelo.gd · _contar_trozo |
Acumulador local: escribir en el array compartido produce falso compartido |
El TODO 4 merece atención especial. Es el único cuyo síntoma no aparece en una prueba rápida: con coordenadas positivas todo funciona, y el bug se manifiesta meses después en la mitad negativa del mapa. Ese es el perfil típico de los fallos de esta parte del programa.