que he visto en este blog:problema de optimización Odd bajo MSVC
http://igoro.com/archive/gallery-of-processor-cache-effects/
El "rareza", en parte 7 es lo que captó mi interés.
Mi primer pensamiento fue "Eso es solo que C# es raro".
No es que haya escrito el siguiente código de C++.
volatile int* p = (volatile int*)_aligned_malloc(sizeof(int) * 8, 64);
memset((void*)p, 0, sizeof(int) * 8);
double dStart = t.GetTime();
for (int i = 0; i < 200000000; i++)
{
//p[0]++;p[1]++;p[2]++;p[3]++; // Option 1
//p[0]++;p[2]++;p[4]++;p[6]++; // Option 2
p[0]++;p[2]++; // Option 3
}
double dTime = t.GetTime() - dStart;
El momento llego en mi 2.4 Ghz Core 2 Quad ir de la siguiente manera:
Option 1 = ~8 cycles per loop.
Option 2 = ~4 cycles per loop.
Option 3 = ~6 cycles per loop.
Ahora esto es confuso. Mi razonamiento detrás de la diferencia se reduce a la latencia de escritura de la memoria caché (3 ciclos) en mi chip y la suposición de que la memoria caché tiene un puerto de escritura de 128 bits (esto es pura conjetura por mi parte).
Sobre esta base en la Opción 1: Incrementará p [0] (1 ciclo) y luego aumentará p [2] (1 ciclo) y luego tendrá que esperar 1 ciclo (para el caché) y luego p [1] (1 ciclo) luego espere 1 ciclo (para caché) luego p [3] (1 ciclo). Finalmente 2 ciclos para incrementos y saltos (aunque usualmente se implementan como decrementos y saltos). Esto da un total de 8 ciclos.
En la Opción 2: Puede incrementar p [0] yp [4] en un ciclo y luego incrementar p [2] yp [6] en otro ciclo. Luego 2 ciclos para restar y saltar. No se necesitan esperas en la memoria caché. Total 4 ciclos
En la opción 3: puede aumentar p [0] luego tiene que esperar 2 ciclos, luego incrementar p [2], luego restar y saltar. El problema es si configura el caso 3 para incrementar p [0] yp [4] TODAVÍA toma 6 ciclos (lo que hace que mi puerto de lectura/escritura de 128 bits salga del agua).
Entonces ... ¿alguien puede decirme qué demonios está pasando aquí? ¿Por qué el caso 3 toma más tiempo? También me gustaría saber lo que tengo mal en mi opinión anterior, ya que obviamente tengo algo mal. ¡Cualquier idea será altamente apreciada! :)
¡También sería interesante ver cómo GCC o cualquier otro compilador lo hace también!
Editar: La idea de Jerry Coffin me dio algunas ideas.
que he hecho algunas pruebas más (en una máquina diferente, por lo perdona el cambio en la frecuencia de administración) con y sin NOP y con diferentes cargos de NOP
case 2 - 0.46 00401ABD jne (401AB0h)
0 nops - 0.68 00401AB7 jne (401AB0h)
1 nop - 0.61 00401AB8 jne (401AB0h)
2 nops - 0.636 00401AB9 jne (401AB0h)
3 nops - 0.632 00401ABA jne (401AB0h)
4 nops - 0.66 00401ABB jne (401AB0h)
5 nops - 0.52 00401ABC jne (401AB0h)
6 nops - 0.46 00401ABD jne (401AB0h)
7 nops - 0.46 00401ABE jne (401AB0h)
8 nops - 0.46 00401ABF jne (401AB0h)
9 nops - 0.55 00401AC0 jne (401AB0h)
que he incluido los statetements salto para que pueda ver que la fuente y el destino están en una línea de caché. También puede ver que empezamos a notar la diferencia cuando tenemos 13 bytes o más de diferencia. Hasta que lleguemos a 16 ... entonces todo sale mal.
Así que Jerry no está bien (aunque su sugerencia SÍ ayuda un poco), sin embargo algo está sucediendo. Estoy cada vez más intrigado por intentar descubrir qué es ahora. Parece ser más un tipo de rareza de alineación de memoria en lugar de algún tipo de rareza de rendimiento de instrucción.
¿Alguien quiere explicar esto para una mente inquisitiva? : D
Editar 3: Interjay tiene un punto en el desenrollamiento que saca la edición anterior del agua. Con un bucle desenrollado, el rendimiento no mejora.Necesita agregar un nop para hacer que la brecha entre la fuente de salto y el destino sea la misma que para mi conteo de nop bueno arriba. El rendimiento todavía apesta. Es interesante que necesito 6 nudos para mejorar el rendimiento. Me pregunto cuántos nudos puede emitir el procesador por ciclo. Si es 3 entonces esa cuenta para la caché escribe latencia ... Pero, si eso es todo, ¿por qué está ocurriendo la latencia?
curioso y más curioso ...
Fwiw, es fácil de conseguir GCC se ejecuta en casi cualquier sistema operativo para comparar, y se puede obtener libremente Compilador de Intel para algunos. La instalación de icc fue muy simple para mí en Ubuntu, solo recuerda que debes tener un chip Intel para aprovechar sus optimizaciones. –
¿Qué es un GCi32? – jalf
Lo único que se me ocurre es algún error en la programación de instrucciones. Dado que el ciclo es más corto, la CPU puede tener que detener algunos ciclos entre iteraciones para esperar a que se complete la escritura, lo que por alguna razón debe causar la desaceleración ** adicional **, haciéndolo más lento que el ciclo más largo.La latencia de caché parece afectar a todos los casos por igual y, como usted dice, el ancho del puerto R/W tampoco parece serlo. El único factor que puedo imaginar que podría causar que el ciclo más corto tome * más tiempo * es algún tipo de limitación de programación en la CPU. – jalf