El diseño de structs y el conjunto de trabajo marcan el rendimiento en CPU

Fuentes: How struct layout and working set size shape CPU performance
Imagen generada por IA con el prompt: Stacked glowing blue CPU cache levels (L1, L2, L3) flowing down into a DRAM block, abstract data streams between layers, dark technical background, editorial illustration
Imagen generada con IA

En el desarrollo de software, el rendimiento suele evaluarse desde un punto de vista algorítmico (análisis asintótico), pero el hardware impone restricciones adicionales. Este artículo explica cómo el tamaño de la línea de caché (64 bytes) y la forma en que se organizan las estructuras de datos en memoria influyen decisivamente en la velocidad de ejecución.

El texto parte de una configuración concreta: en la máquina del autor, la caché L1d dispone de 352 KiB repartidos en 10 instancias (unos 35 KiB por núcleo, aproximadamente 560 líneas de caché), la L2 suma 10 MiB y la L3, 12 MiB. Cada vez que la CPU lee un único byte, el hardware carga los 64 bytes contiguos en la línea de caché, una estrategia basada en la localidad temporal y espacial de los datos.

Para ilustrar el impacto se define un struct Monster de 64 bytes con campos como id, posición, velocidad, hp, ataque, defensa, un booleano is_alive y un nombre. Si los monstruos se almacenan como Array of Structs (AoS), cada línea de caché contiene un único monstruo, de modo que iterar solo sobre is_alive desaprovecha 63 bytes por acceso. La alternativa, Struct of Arrays (SoA), agrupa los campos del mismo tipo en arrays contiguos, lo que permite empaquetar hasta 64 valores is_alive en una sola línea de caché. En pruebas con structs de 1 KiB, la disposición SoA logra mejoras de hasta 30 veces.

El artículo también aborda los accesos aleatorios (tablas hash, árboles, grafos), donde el pre-fetcher de la CPU no puede anticipar direcciones y el rendimiento depende del tamaño total del conjunto de trabajo. Una tabla de mediciones muestra que duplicar el struct de 64 B a 128 B duplica el working set: con 512 monstruos, el de 64 B cabe en L1d (~3 ns), pero el de 128 B ya cae a L2 (~11 ns); con 131 072 elementos, la latencia ronda los 163 ns. La conclusión práctica es que conocer el tamaño real de los structs y mantener el conjunto de trabajo dentro de los niveles rápidos de caché permite diferencias de rendimiento muy significativas, tanto en patrones secuenciales como aleatorios.