Sí, tanto la alineación como la disposición de sus datos pueden hacer una gran diferencia en el rendimiento, no solo un pequeño porcentaje, sino unos pocos a muchos cientos de un porcentaje.
Toma este bucle, dos instrucciones son importantes si ejecutas suficientes bucles.
.globl ASMDELAY
ASMDELAY:
subs r0,r0,#1
bne ASMDELAY
bx lr
Con y sin caché, y con alineación con y sin tirado de caché en la predicción de bifurcación, puede variar el rendimiento de estas dos instrucciones en una cantidad significativa (temporizadores):
min max difference
00016DDE 003E025D 003C947F
Una prueba de rendimiento que puedes hacer tú mismo muy fácilmente. agregue o elimine nops alrededor del código bajo prueba y realice un trabajo preciso de sincronización, mueva las instrucciones bajo prueba a lo largo de un amplio rango de direcciones para tocar los bordes de las líneas de caché, etc.
Lo mismo que con los accesos de datos. Algunas arquitecturas se quejan de accesos no alineados (que realizan una lectura de 32 bits en la dirección 0x1001 por ejemplo), al darle un error de datos. Algunos de ellos pueden deshabilitar la falla y recibir el impacto de rendimiento. Los demás que permiten accesos no alineados solo obtienen el impacto de rendimiento.
A veces son "instrucciones", pero la mayoría de las veces son ciclos de reloj / bus.
Mire las implementaciones de memcpy en gcc para varios destinos. Supongamos que está copiando una estructura de 0x43 bytes, puede encontrar una implementación que copie un byte dejando 0x42 y luego copie 0x40 bytes en grandes porciones eficientes; luego, el último 0x2 puede hacerlo como dos bytes individuales o como una transferencia de 16 bits. La alineación y el objetivo entran en juego si las direcciones de origen y destino están en la misma alineación, por ejemplo, 0x1003 y 0x2003, entonces usted podría hacer un byte, luego 0x40 en grandes segmentos y luego 0x2, pero si uno es 0x1002 y el otro 0x1003, entonces real feo y muy lento.
La mayoría de las veces son ciclos de bus. O peor, el número de transferencias. Tome un procesador con un bus de datos de 64 bits de ancho, como ARM, y realice una transferencia de cuatro palabras (lectura o escritura, LDM o STM) en la dirección 0x1004, que es una dirección de palabras alineadas, y es perfectamente legal, pero si el bus es 64 bits de ancho es probable que la instrucción única se convierta en tres transferencias en este caso a 32 bits a 0x1004, a 64 bits a 0x1008 y a 32 bits a 0x100A. Pero si tuviera la misma instrucción pero en la dirección 0x1008, podría realizar una única transferencia de cuatro palabras en la dirección 0x1008. Cada transferencia tiene un tiempo de configuración asociado. Por lo tanto, la diferencia de dirección de 0x1004 a 0x1008 por sí misma puede ser varias veces más rápida, incluso / esp cuando se usa un caché y todos son aciertos de caché.
Hablando de eso, incluso si haces una lectura de dos palabras en la dirección 0x1000 vs 0x0FFC, la falta de memoria caché 0x0FFC causará dos lecturas de línea caché donde 0x1000 es una línea de caché, tienes la penalización de la lectura de una línea caché de todos modos para un acceso aleatorio (leer más datos que usar) pero luego se duplica. La forma en que se alinean sus estructuras o sus datos en general y la frecuencia con la que accede a esos datos, etc., puede provocar la aglomeración de caché.
Puede terminar dividiendo sus datos de manera tal que al procesar los datos, puede crear desalojos, puede tener una mala suerte y terminar usando solo una fracción de su caché y al saltar a través del siguiente blob de datos colisiona con un blob anterior. Al mezclar sus datos o reorganizar las funciones en el código fuente, etc. puede crear o eliminar colisiones, ya que no todas las memorias caché se crean igual que el compilador no lo va a ayudar. Incluso la detección del impacto o la mejora del rendimiento está en ti.
Todas las cosas que hemos agregado para mejorar el rendimiento, buses de datos más amplios, tuberías, cachés, predicción de bifurcaciones, múltiples unidades / rutas de ejecución, etc. Ayudarán con mayor frecuencia, pero todas tienen puntos débiles, que pueden explotarse intencionalmente o accidentalmente Es muy poco lo que el compilador o las bibliotecas pueden hacer al respecto. Si está interesado en el rendimiento, debe afinarlo y uno de los factores de ajuste más importantes es la alineación del código y los datos, no solo de 32, 64, 128, 256 los límites de bits, pero también cuando las cosas son relativas entre sí, usted quiere que los bucles muy utilizados o los datos reutilizados no se conecten de la misma manera en la memoria caché, cada uno desea el suyo propio. Los compiladores pueden ayudar, por ejemplo, a ordenar las instrucciones para una arquitectura súper escalar, reorganizar las instrucciones que no se importan entre sí, pueden tener una gran ganancia de rendimiento o éxito si no está utilizando de manera eficiente las rutas de ejecución, pero debe informar al compilador en lo que se está ejecutando.
La mayor supervisión es la suposición de que el procesador es el cuello de botella. No ha sido así durante una década o más, el problema es alimentar al procesador y ahí es donde entran en juego problemas como los golpes de rendimiento de alineación, la memoria caché, etc. Con un poco de trabajo, incluso a nivel del código fuente, la reorganización de los datos en una estructura, el orden de las declaraciones de variables / estructura, el orden de las funciones dentro del código fuente y un poco de código adicional para alinear los datos, puede mejorar el rendimiento varias veces o más.