⚙️ Lab: Ingeniería avanzada

⬅️ 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.

🎯 La demostración

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:

📦 Qué hay dentro

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

✅ Qué verifica la CI

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.

🧪 El proyecto de partida

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.

🔍 Qué NO cubre