significado septima procesadores procesador numeros nomenclatura los generacion caracteristicas performance assembly x86 intel fma

performance - septima - Significativa anomalía de rendimiento de FMA experimentada en el procesador Intel Broadwell



procesadores intel caracteristicas (2)

Actualizado

No tengo ninguna explicación para usted, ya que estoy en Haswell, pero tengo un código para compartir que podría ayudarlo a usted o a alguien más con hardware de Broadwell o Skylake a aislar su problema. Si pudiera ejecutarlo en su máquina y compartir los resultados, podríamos obtener una idea de lo que le está sucediendo a su máquina.

Introducción

Los últimos procesadores Intel Core i7 tienen 7 monitores de monitoreo de rendimiento (PMC), 3 de función fija y 4 de uso general, que se pueden usar para codificar el perfil. Los PMCs de función fija son:

  • Instrucciones retiradas
  • Ciclos de núcleo no curados (marcas de reloj que incluyen los efectos de TurboBoost)
  • Ciclos de referencia no estabilizados (tics de reloj de frecuencia fija)

La relación entre el núcleo y los ciclos de reloj de referencia determina la aceleración o desaceleración relativa de la escala de frecuencia dinámica.

Aunque existe software (ver comentarios a continuación) que accede a estos contadores, no los conozco y aún los considero que no son lo suficientemente precisos.

Por lo tanto, en los últimos días me escribí un módulo kernel de Linux, perfcount , para concederme acceso a los monitores de rendimiento Intel y un banco de pruebas y biblioteca de espacio de usuario para su código que envuelve su código FMA en llamadas a mi LKM. Las instrucciones para cómo reproducir mi configuración seguirán.

El código fuente de mi banco de pruebas está debajo. Se calienta, luego ejecuta su código varias veces y lo prueba en una larga lista de métricas. Cambié tu recuento de bucles a mil millones. Debido a que solo se pueden programar 4 PMCs de uso general a la vez, realizo las mediciones de 4 en 4.

perfcountdemo.c

/* Includes */ #include "libperfcount.h" #include <ctype.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> /* Function prototypes */ void code1(void); void code2(void); void code3(void); void code4(void); void code5(void); /* Global variables */ void ((*FN_TABLE[])(void)) = { code1, code2, code3, code4, code5 }; /** * Code snippets to bench */ void code1(void){ asm volatile( ".intel_syntax noprefix/n/t" "vzeroall/n/t" "mov rcx, 1000000000/n/t" "LstartLabel1:/n/t" "vfmadd231ps %%ymm0, %%ymm0, %%ymm0/n/t" "vfmadd231ps ymm1, ymm1, ymm1/n/t" "vfmadd231ps ymm2, ymm2, ymm2/n/t" "vfmadd231ps ymm3, ymm3, ymm3/n/t" "vfmadd231ps ymm4, ymm4, ymm4/n/t" "vfmadd231ps ymm5, ymm5, ymm5/n/t" "vfmadd231ps ymm6, ymm6, ymm6/n/t" "vfmadd231ps ymm7, ymm7, ymm7/n/t" "vfmadd231ps ymm8, ymm8, ymm8/n/t" "vfmadd231ps ymm9, ymm9, ymm9/n/t" "vpaddd ymm10, ymm10, ymm10/n/t" "vpaddd ymm11, ymm11, ymm11/n/t" "vpaddd ymm12, ymm12, ymm12/n/t" "vpaddd ymm13, ymm13, ymm13/n/t" "vpaddd ymm14, ymm14, ymm14/n/t" "dec rcx/n/t" "jnz LstartLabel1/n/t" ".att_syntax noprefix/n/t" : /* No outputs we care about */ : /* No inputs we care about */ : "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", "rcx", "memory" ); } void code2(void){ } void code3(void){ } void code4(void){ } void code5(void){ } /* Test Schedule */ const char* const SCHEDULE[] = { /* Batch */ "uops_issued.any", "uops_issued.any<1", "uops_issued.any>=1", "uops_issued.any>=2", /* Batch */ "uops_issued.any>=3", "uops_issued.any>=4", "uops_issued.any>=5", "uops_issued.any>=6", /* Batch */ "uops_executed_port.port_0", "uops_executed_port.port_1", "uops_executed_port.port_2", "uops_executed_port.port_3", /* Batch */ "uops_executed_port.port_4", "uops_executed_port.port_5", "uops_executed_port.port_6", "uops_executed_port.port_7", /* Batch */ "resource_stalls.any", "resource_stalls.rs", "resource_stalls.sb", "resource_stalls.rob", /* Batch */ "uops_retired.all", "uops_retired.all<1", "uops_retired.all>=1", "uops_retired.all>=2", /* Batch */ "uops_retired.all>=3", "uops_retired.all>=4", "uops_retired.all>=5", "uops_retired.all>=6", /* Batch */ "inst_retired.any_p", "inst_retired.any_p<1", "inst_retired.any_p>=1", "inst_retired.any_p>=2", /* Batch */ "inst_retired.any_p>=3", "inst_retired.any_p>=4", "inst_retired.any_p>=5", "inst_retired.any_p>=6", /* Batch */ "idq_uops_not_delivered.core", "idq_uops_not_delivered.core<1", "idq_uops_not_delivered.core>=1", "idq_uops_not_delivered.core>=2", /* Batch */ "idq_uops_not_delivered.core>=3", "idq_uops_not_delivered.core>=4", "rs_events.empty", "idq.empty", /* Batch */ "idq.mite_all_uops", "idq.mite_all_uops<1", "idq.mite_all_uops>=1", "idq.mite_all_uops>=2", /* Batch */ "idq.mite_all_uops>=3", "idq.mite_all_uops>=4", "move_elimination.int_not_eliminated", "move_elimination.simd_not_eliminated", /* Batch */ "lsd.uops", "lsd.uops<1", "lsd.uops>=1", "lsd.uops>=2", /* Batch */ "lsd.uops>=3", "lsd.uops>=4", "ild_stall.lcp", "ild_stall.iq_full", /* Batch */ "br_inst_exec.all_branches", "br_inst_exec.0x81", "br_inst_exec.0x82", "icache.misses", /* Batch */ "br_misp_exec.all_branches", "br_misp_exec.0x81", "br_misp_exec.0x82", "fp_assist.any", /* Batch */ "cpu_clk_unhalted.core_clk", "cpu_clk_unhalted.ref_xclk", "baclears.any" }; const int NUMCOUNTS = sizeof(SCHEDULE)/sizeof(*SCHEDULE); /** * Main */ int main(int argc, char* argv[]){ int i; /** * Initialize */ pfcInit(); if(argc <= 1){ pfcDumpEvents(); exit(1); } pfcPinThread(3); /** * Arguments are: * * perfcountdemo #codesnippet * * There is a schedule of configuration that is followed. */ void (*fn)(void) = FN_TABLE[strtoull(argv[1], NULL, 0)]; static const uint64_t ZERO_CNT[7] = {0,0,0,0,0,0,0}; static const uint64_t ZERO_CFG[7] = {0,0,0,0,0,0,0}; uint64_t cnt[7] = {0,0,0,0,0,0,0}; uint64_t cfg[7] = {2,2,2,0,0,0,0}; /* Warmup */ for(i=0;i<10;i++){ fn(); } /* Run master loop */ for(i=0;i<NUMCOUNTS;i+=4){ /* Configure counters */ const char* sched0 = i+0 < NUMCOUNTS ? SCHEDULE[i+0] : ""; const char* sched1 = i+1 < NUMCOUNTS ? SCHEDULE[i+1] : ""; const char* sched2 = i+2 < NUMCOUNTS ? SCHEDULE[i+2] : ""; const char* sched3 = i+3 < NUMCOUNTS ? SCHEDULE[i+3] : ""; cfg[3] = pfcParseConfig(sched0); cfg[4] = pfcParseConfig(sched1); cfg[5] = pfcParseConfig(sched2); cfg[6] = pfcParseConfig(sched3); pfcWrConfigCnts(0, 7, cfg); pfcWrCountsCnts(0, 7, ZERO_CNT); pfcRdCountsCnts(0, 7, cnt); /* ^ Should report 0s, and launch the counters. */ /************** Hot section **************/ fn(); /************ End Hot section ************/ pfcRdCountsCnts(0, 7, cnt); pfcWrConfigCnts(0, 7, ZERO_CFG); /* ^ Should clear the counter config and disable them. */ /** * Print the lovely results */ printf("Instructions Issued : %20llu/n", cnt[0]); printf("Unhalted core cycles : %20llu/n", cnt[1]); printf("Unhalted reference cycles : %20llu/n", cnt[2]); printf("%-35s: %20llu/n", sched0, cnt[3]); printf("%-35s: %20llu/n", sched1, cnt[4]); printf("%-35s: %20llu/n", sched2, cnt[5]); printf("%-35s: %20llu/n", sched3, cnt[6]); } /** * Close up shop */ pfcFini(); }

En mi máquina, obtuve los siguientes resultados:

Haswell Core i7-4700MQ

> ./perfcountdemo 0 Instructions Issued : 17000001807 Unhalted core cycles : 5305920785 Unhalted reference cycles : 4245764952 uops_issued.any : 16000811079 uops_issued.any<1 : 1311417889 uops_issued.any>=1 : 4000292290 uops_issued.any>=2 : 4000229358 Instructions Issued : 17000001806 Unhalted core cycles : 5303822082 Unhalted reference cycles : 4243345896 uops_issued.any>=3 : 4000156998 uops_issued.any>=4 : 4000110067 uops_issued.any>=5 : 0 uops_issued.any>=6 : 0 Instructions Issued : 17000001811 Unhalted core cycles : 5314227923 Unhalted reference cycles : 4252020624 uops_executed_port.port_0 : 5016261477 uops_executed_port.port_1 : 5036728509 uops_executed_port.port_2 : 5282 uops_executed_port.port_3 : 12481 Instructions Issued : 17000001816 Unhalted core cycles : 5329351248 Unhalted reference cycles : 4265809728 uops_executed_port.port_4 : 7087 uops_executed_port.port_5 : 4946019835 uops_executed_port.port_6 : 1000228324 uops_executed_port.port_7 : 1372 Instructions Issued : 17000001816 Unhalted core cycles : 5325153463 Unhalted reference cycles : 4261060248 resource_stalls.any : 1322734589 resource_stalls.rs : 844250210 resource_stalls.sb : 0 resource_stalls.rob : 0 Instructions Issued : 17000001814 Unhalted core cycles : 5327823817 Unhalted reference cycles : 4262914728 uops_retired.all : 16000445793 uops_retired.all<1 : 687284798 uops_retired.all>=1 : 4646263984 uops_retired.all>=2 : 4452324050 Instructions Issued : 17000001809 Unhalted core cycles : 5311736558 Unhalted reference cycles : 4250015688 uops_retired.all>=3 : 3545695253 uops_retired.all>=4 : 3341664653 uops_retired.all>=5 : 1016 uops_retired.all>=6 : 1 Instructions Issued : 17000001871 Unhalted core cycles : 5477215269 Unhalted reference cycles : 4383891984 inst_retired.any_p : 17000001871 inst_retired.any_p<1 : 891904306 inst_retired.any_p>=1 : 4593972062 inst_retired.any_p>=2 : 4441024510 Instructions Issued : 17000001835 Unhalted core cycles : 5377202052 Unhalted reference cycles : 4302895152 inst_retired.any_p>=3 : 3555852364 inst_retired.any_p>=4 : 3369559466 inst_retired.any_p>=5 : 999980244 inst_retired.any_p>=6 : 0 Instructions Issued : 17000001826 Unhalted core cycles : 5349373678 Unhalted reference cycles : 4280991912 idq_uops_not_delivered.core : 1580573 idq_uops_not_delivered.core<1 : 5354931839 idq_uops_not_delivered.core>=1 : 471248 idq_uops_not_delivered.core>=2 : 418625 Instructions Issued : 17000001808 Unhalted core cycles : 5309687640 Unhalted reference cycles : 4248083976 idq_uops_not_delivered.core>=3 : 280800 idq_uops_not_delivered.core>=4 : 247923 rs_events.empty : 0 idq.empty : 649944 Instructions Issued : 17000001838 Unhalted core cycles : 5392229041 Unhalted reference cycles : 4315704216 idq.mite_all_uops : 2496139 idq.mite_all_uops<1 : 5397877484 idq.mite_all_uops>=1 : 971582 idq.mite_all_uops>=2 : 595973 Instructions Issued : 17000001822 Unhalted core cycles : 5347205506 Unhalted reference cycles : 4278845208 idq.mite_all_uops>=3 : 394011 idq.mite_all_uops>=4 : 335205 move_elimination.int_not_eliminated: 0 move_elimination.simd_not_eliminated: 0 Instructions Issued : 17000001812 Unhalted core cycles : 5320621549 Unhalted reference cycles : 4257095280 lsd.uops : 15999287982 lsd.uops<1 : 1326629729 lsd.uops>=1 : 3999821996 lsd.uops>=2 : 3999821996 Instructions Issued : 17000001813 Unhalted core cycles : 5320533147 Unhalted reference cycles : 4257105096 lsd.uops>=3 : 3999823498 lsd.uops>=4 : 3999823498 ild_stall.lcp : 0 ild_stall.iq_full : 3468 Instructions Issued : 17000001813 Unhalted core cycles : 5323278281 Unhalted reference cycles : 4258969200 br_inst_exec.all_branches : 1000016626 br_inst_exec.0x81 : 1000016616 br_inst_exec.0x82 : 0 icache.misses : 294 Instructions Issued : 17000001812 Unhalted core cycles : 5315098728 Unhalted reference cycles : 4253082504 br_misp_exec.all_branches : 5 br_misp_exec.0x81 : 2 br_misp_exec.0x82 : 0 fp_assist.any : 0 Instructions Issued : 17000001819 Unhalted core cycles : 5338484610 Unhalted reference cycles : 4271432976 cpu_clk_unhalted.core_clk : 5338494250 cpu_clk_unhalted.ref_xclk : 177976806 baclears.any : 1 : 0

Podemos ver que en Haswell, todo está bien engrasado. Tomaré algunas notas de las estadísticas anteriores:

  • Las instrucciones emitidas son increíblemente consistentes para mí. Siempre es alrededor de 17000001800 , lo que es una buena señal: significa que podemos hacer una estimación muy buena de nuestros gastos generales. Idem para los otros contadores de función fija. El hecho de que todos coincidan razonablemente bien significa que las pruebas en lotes de 4 son comparaciones de manzanas con manzanas.
  • Con una relación de núcleo: ciclos de referencia de alrededor de 5305920785/4245764952, obtenemos una escala de frecuencia promedio de ~ 1.25; Esto funciona bien con mis observaciones de que mi núcleo aumentó de 2.4 GHz a 3.0 GHz. cpu_clk_unhalted.core_clk/(10.0*cpu_clk_unhalted.ref_xclk) da justo por debajo de 3 GHz también.
  • La relación entre las instrucciones emitidas para los ciclos centrales proporciona al IPC, 17000001807/5305920785 ~ 3.20, que también es correcto: 2 FMA + 1 VPADDD cada ciclo de reloj para 4 ciclos de reloj, y 2 instrucciones de control de ciclo adicionales cada 5º ciclo de reloj que entran paralela.
  • uops_issued.any : El número de instrucciones emitidas es ~ 17B, pero el número de uops emitidos es ~ 16B. Eso es porque las dos instrucciones para el control de bucle se fusionan juntas; Buena señal. Además, alrededor de 1.3B ciclos de reloj de 5.3B (25% del tiempo), no se emitieron uops, mientras que la casi totalidad del resto del tiempo (4B ciclos de reloj), 4 uops emitidos a la vez.
  • uops_executed_port.port_[0-7] : Saturación del puerto. Estamos en buena salud. De los uops 16B post-fusión, los Puertos 0, 1 y 5 comieron 5B uops cada uno durante 5.3B ciclos (lo que significa que se distribuyeron de manera óptima: Flotante, flotante, int respectivamente), el Puerto 6 comió 1B (el fundido dec-branch op) , y los puertos 2, 3, 4 y 7 comieron cantidades insignificantes en comparación.
  • resource_stalls : 1.3B de ellos ocurrieron, 2/3 de los cuales se debieron a la estación de reserva (RS) y el otro tercio a causas desconocidas.
  • De la distribución acumulativa que creamos con nuestras comparaciones en uops_retired.all e inst_retired.all , sabemos que estamos retirando 4 uops 60% del tiempo, 0 uops 13% de las veces y 2 uops el resto del tiempo, con cantidades insignificantes de otra manera.
  • (Numerosos *idq* ): el IDQ solo rara vez nos detiene.
  • lsd : The Loop Stream Detector está funcionando; Se suministraron casi 16B uops fusionados a la interfaz.
  • ild : La decodificación de la longitud de la instrucción no es el cuello de botella, y no se encuentra un único prefijo que cambie la longitud.
  • br_inst_exec/br_misp_exec : la predicción br_inst_exec/br_misp_exec es un problema insignificante.
  • icache.misses : icache.misses .
  • fp_assist : fp_assist . Denormales no encontrados. (Creo que sin DOR denormals-are-zero flushing, requerirían una asistencia, que debería registrarse aquí)

Por lo tanto, con Intel Haswell navega sin problemas. Si pudieras ejecutar mi suite en tus máquinas, sería genial.

Instrucciones para la reproducción

  • Regla # 1: Inspeccione todo mi código antes de hacer algo con él. Nunca confíes ciegamente en extraños en Internet.
  • Agarre perfcountdemo.c , libperfcount.c y libperfcount.h , colóquelos en el mismo directorio y compínelos juntos.
  • Agarra perfcount.c y Makefile , perfcount.c en el mismo directorio y perfcount.c el módulo kernel.
  • Reinicie su máquina con los indicadores de arranque de GRUB nmi_watchdog=0 modprobe.blacklist=iTCO_wdt,iTCO_vendor_support . El perro guardián de NMI manipulará el contador de ciclo sin núcleo de lo contrario.
  • insmod perfcount.ko el módulo. dmesg | tail -n 10 dmesg | tail -n 10 debería decir que se cargó correctamente y decir que hay 3 contadores de Ff y contadores de 4 Gp, o bien da una razón para no hacerlo.
  • Ejecuta mi aplicación, preferiblemente mientras el resto del sistema no está bajo carga. Intenta también cambiar en perfcountdemo.c el núcleo al que restringes tu afinidad cambiando el argumento a pfcPinThread() .
  • Edite aquí los resultados.
  • Código1:

    vzeroall mov rcx, 1000000 startLabel1: vfmadd231ps ymm0, ymm0, ymm0 vfmadd231ps ymm1, ymm1, ymm1 vfmadd231ps ymm2, ymm2, ymm2 vfmadd231ps ymm3, ymm3, ymm3 vfmadd231ps ymm4, ymm4, ymm4 vfmadd231ps ymm5, ymm5, ymm5 vfmadd231ps ymm6, ymm6, ymm6 vfmadd231ps ymm7, ymm7, ymm7 vfmadd231ps ymm8, ymm8, ymm8 vfmadd231ps ymm9, ymm9, ymm9 vpaddd ymm10, ymm10, ymm10 vpaddd ymm11, ymm11, ymm11 vpaddd ymm12, ymm12, ymm12 vpaddd ymm13, ymm13, ymm13 vpaddd ymm14, ymm14, ymm14 dec rcx jnz startLabel1

  • Code2:

    vzeroall mov rcx, 1000000 startLabel2: vmulps ymm0, ymm0, ymm0 vmulps ymm1, ymm1, ymm1 vmulps ymm2, ymm2, ymm2 vmulps ymm3, ymm3, ymm3 vmulps ymm4, ymm4, ymm4 vmulps ymm5, ymm5, ymm5 vmulps ymm6, ymm6, ymm6 vmulps ymm7, ymm7, ymm7 vmulps ymm8, ymm8, ymm8 vmulps ymm9, ymm9, ymm9 vpaddd ymm10, ymm10, ymm10 vpaddd ymm11, ymm11, ymm11 vpaddd ymm12, ymm12, ymm12 vpaddd ymm13, ymm13, ymm13 vpaddd ymm14, ymm14, ymm14 dec rcx jnz startLabel2

  • Code3 (igual que Code2 pero con prefijo largo VEX):

    vzeroall mov rcx, 1000000 startLabel3: byte 0c4h, 0c1h, 07ch, 059h, 0c0h ;long VEX form vmulps ymm0, ymm0, ymm0 byte 0c4h, 0c1h, 074h, 059h, 0c9h ;long VEX form vmulps ymm1, ymm1, ymm1 byte 0c4h, 0c1h, 06ch, 059h, 0d2h ;long VEX form vmulps ymm2, ymm2, ymm2 byte 0c4h, 0c1h, 06ch, 059h, 0dbh ;long VEX form vmulps ymm3, ymm3, ymm3 byte 0c4h, 0c1h, 05ch, 059h, 0e4h ;long VEX form vmulps ymm4, ymm4, ymm4 byte 0c4h, 0c1h, 054h, 059h, 0edh ;long VEX form vmulps ymm5, ymm5, ymm5 byte 0c4h, 0c1h, 04ch, 059h, 0f6h ;long VEX form vmulps ymm6, ymm6, ymm6 byte 0c4h, 0c1h, 044h, 059h, 0ffh ;long VEX form vmulps ymm7, ymm7, ymm7 vmulps ymm8, ymm8, ymm8 vmulps ymm9, ymm9, ymm9 vpaddd ymm10, ymm10, ymm10 vpaddd ymm11, ymm11, ymm11 vpaddd ymm12, ymm12, ymm12 vpaddd ymm13, ymm13, ymm13 vpaddd ymm14, ymm14, ymm14 dec rcx jnz startLabel3

  • Code4 (igual que Code1 pero con registros xmm):

    vzeroall mov rcx, 1000000 startLabel4: vfmadd231ps xmm0, xmm0, xmm0 vfmadd231ps xmm1, xmm1, xmm1 vfmadd231ps xmm2, xmm2, xmm2 vfmadd231ps xmm3, xmm3, xmm3 vfmadd231ps xmm4, xmm4, xmm4 vfmadd231ps xmm5, xmm5, xmm5 vfmadd231ps xmm6, xmm6, xmm6 vfmadd231ps xmm7, xmm7, xmm7 vfmadd231ps xmm8, xmm8, xmm8 vfmadd231ps xmm9, xmm9, xmm9 vpaddd xmm10, xmm10, xmm10 vpaddd xmm11, xmm11, xmm11 vpaddd xmm12, xmm12, xmm12 vpaddd xmm13, xmm13, xmm13 vpaddd xmm14, xmm14, xmm14 dec rcx jnz startLabel4

  • Code5 (igual que Code1 pero con vpsubd`s no cero):

    vzeroall mov rcx, 1000000 startLabel5: vfmadd231ps ymm0, ymm0, ymm0 vfmadd231ps ymm1, ymm1, ymm1 vfmadd231ps ymm2, ymm2, ymm2 vfmadd231ps ymm3, ymm3, ymm3 vfmadd231ps ymm4, ymm4, ymm4 vfmadd231ps ymm5, ymm5, ymm5 vfmadd231ps ymm6, ymm6, ymm6 vfmadd231ps ymm7, ymm7, ymm7 vfmadd231ps ymm8, ymm8, ymm8 vfmadd231ps ymm9, ymm9, ymm9 vpsubd ymm10, ymm10, ymm11 vpsubd ymm11, ymm11, ymm12 vpsubd ymm12, ymm12, ymm13 vpsubd ymm13, ymm13, ymm14 vpsubd ymm14, ymm14, ymm10 dec rcx jnz startLabel5

  • Code6b: (operandos de memoria revisados ​​solo para vpaddds)

    vzeroall mov rcx, 1000000 startLabel6: vfmadd231ps ymm0, ymm0, ymm0 vfmadd231ps ymm1, ymm1, ymm1 vfmadd231ps ymm2, ymm2, ymm2 vfmadd231ps ymm3, ymm3, ymm3 vfmadd231ps ymm4, ymm4, ymm4 vfmadd231ps ymm5, ymm5, ymm5 vfmadd231ps ymm6, ymm6, ymm6 vfmadd231ps ymm7, ymm7, ymm7 vfmadd231ps ymm8, ymm8, ymm8 vfmadd231ps ymm9, ymm9, ymm9 vpaddd ymm10, ymm10, [mem] vpaddd ymm11, ymm11, [mem] vpaddd ymm12, ymm12, [mem] vpaddd ymm13, ymm13, [mem] vpaddd ymm14, ymm14, [mem] dec rcx jnz startLabel6

  • Code7: (igual que Code1 pero vpaddds usa ymm15)

    vzeroall mov rcx, 1000000 startLabel7: vfmadd231ps ymm0, ymm0, ymm0 vfmadd231ps ymm1, ymm1, ymm1 vfmadd231ps ymm2, ymm2, ymm2 vfmadd231ps ymm3, ymm3, ymm3 vfmadd231ps ymm4, ymm4, ymm4 vfmadd231ps ymm5, ymm5, ymm5 vfmadd231ps ymm6, ymm6, ymm6 vfmadd231ps ymm7, ymm7, ymm7 vfmadd231ps ymm8, ymm8, ymm8 vfmadd231ps ymm9, ymm9, ymm9 vpaddd ymm10, ymm15, ymm15 vpaddd ymm11, ymm15, ymm15 vpaddd ymm12, ymm15, ymm15 vpaddd ymm13, ymm15, ymm15 vpaddd ymm14, ymm15, ymm15 dec rcx jnz startLabel7

  • Code8: (lo mismo que Code7 pero usa xmm en lugar de ymm)

    vzeroall mov rcx, 1000000 startLabel8: vfmadd231ps xmm0, ymm0, ymm0 vfmadd231ps xmm1, xmm1, xmm1 vfmadd231ps xmm2, xmm2, xmm2 vfmadd231ps xmm3, xmm3, xmm3 vfmadd231ps xmm4, xmm4, xmm4 vfmadd231ps xmm5, xmm5, xmm5 vfmadd231ps xmm6, xmm6, xmm6 vfmadd231ps xmm7, xmm7, xmm7 vfmadd231ps xmm8, xmm8, xmm8 vfmadd231ps xmm9, xmm9, xmm9 vpaddd xmm10, xmm15, xmm15 vpaddd xmm11, xmm15, xmm15 vpaddd xmm12, xmm15, xmm15 vpaddd xmm13, xmm15, xmm15 vpaddd xmm14, xmm15, xmm15 dec rcx jnz startLabel8

Relojes de TSC medidos con Turbo y C1E desactivados:

Haswell Broadwell Skylake CPUID 306C3, 40661 306D4, 40671 506E3 Code1 ~5000000 ~7730000 ->~54% slower ~5500000 ->~10% slower Code2 ~5000000 ~5000000 ~5000000 Code3 ~6000000 ~5000000 ~5000000 Code4 ~5000000 ~7730000 ~5500000 Code5 ~5000000 ~7730000 ~5500000 Code6b ~5000000 ~8380000 ~5500000 Code7 ~5000000 ~5000000 ~5000000 Code8 ~5000000 ~5000000 ~5000000

  1. ¿Alguien puede explicar lo que sucede con Code1 en Broadwell? Creo que Broadwell contamina de alguna manera Port1 con vpaddds en el caso de Code1, sin embargo, Haswell solo puede usar Port5 si Port0 y Port1 están llenos ;

  2. ¿Tiene alguna idea de lograr el ~ 5000000 clk en Broadwell con las instrucciones de FMA?

  3. Traté de reordenar. Comportamiento similar experimentado con double y qword;

  4. Usé Windows 8.1 y Win 10;

    Actualizar:

  5. Se agregó Code3 como la idea de Marat Dukhan con VEX largo;

  6. Extendió la tabla de resultados con las experiencias de Skylake;

  7. Cargó un código de muestra VS2015 Community + MASM here

    Actualización2:

  8. Intenté con registros xmm en lugar de ymm (Código 4). Mismo resultado en Broadwell.

    Actualización3:

  9. Agregué Code5 como idea de Peter Cordes (sustituya vpaddd`s con otras instrucciones (vpxor, vpor, vpand, vpandn, vpsubd)). Si la nueva instrucción no es una expresión de puesta a cero (vpxor, vpsubd con el mismo registro), el resultado es el mismo en BDW. Proyecto de ejemplo actualizado con Code4 y Code5.

    Actualización4:

  10. Agregué Code6 como la idea de Stephen Canon (operandos de memoria). El resultado es ~ 8200000 clks. Proyecto de ejemplo actualizado con Code6;

  11. Comprobé la frecuencia de la CPU y la posible limitación con la Prueba de estabilidad del sistema de AIDA64. La frecuencia es estable y no hay signos de estrangulamiento;

  12. Análisis de rendimiento de Intel IACA 2.1 Haswell:

    Intel(R) Architecture Code Analyzer Version - 2.1 Analyzed File - Assembly.obj Binary Format - 64Bit Architecture - HSW Analysis Type - Throughput Throughput Analysis Report -------------------------- Block Throughput: 5.10 Cycles Throughput Bottleneck: Port0, Port1, Port5 Port Binding In Cycles Per Iteration: --------------------------------------------------------------------------------------- | Port | 0 - DV | 1 | 2 - D | 3 - D | 4 | 5 | 6 | 7 | --------------------------------------------------------------------------------------- | Cycles | 5.0 0.0 | 5.0 | 0.0 0.0 | 0.0 0.0 | 0.0 | 5.0 | 1.0 | 0.0 | --------------------------------------------------------------------------------------- | Num Of | Ports pressure in cycles | | | Uops | 0 - DV | 1 | 2 - D | 3 - D | 4 | 5 | 6 | 7 | | --------------------------------------------------------------------------------- | 1 | 1.0 | | | | | | | | CP | vfmadd231ps ymm0, ymm0, ymm0 | 1 | | 1.0 | | | | | | | CP | vfmadd231ps ymm1, ymm1, ymm1 | 1 | 1.0 | | | | | | | | CP | vfmadd231ps ymm2, ymm2, ymm2 | 1 | | 1.0 | | | | | | | CP | vfmadd231ps ymm3, ymm3, ymm3 | 1 | 1.0 | | | | | | | | CP | vfmadd231ps ymm4, ymm4, ymm4 | 1 | | 1.0 | | | | | | | CP | vfmadd231ps ymm5, ymm5, ymm5 | 1 | 1.0 | | | | | | | | CP | vfmadd231ps ymm6, ymm6, ymm6 | 1 | | 1.0 | | | | | | | CP | vfmadd231ps ymm7, ymm7, ymm7 | 1 | 1.0 | | | | | | | | CP | vfmadd231ps ymm8, ymm8, ymm8 | 1 | | 1.0 | | | | | | | CP | vfmadd231ps ymm9, ymm9, ymm9 | 1 | | | | | | 1.0 | | | CP | vpaddd ymm10, ymm10, ymm10 | 1 | | | | | | 1.0 | | | CP | vpaddd ymm11, ymm11, ymm11 | 1 | | | | | | 1.0 | | | CP | vpaddd ymm12, ymm12, ymm12 | 1 | | | | | | 1.0 | | | CP | vpaddd ymm13, ymm13, ymm13 | 1 | | | | | | 1.0 | | | CP | vpaddd ymm14, ymm14, ymm14 | 1 | | | | | | | 1.0 | | | dec rcx | 0F | | | | | | | | | | jnz 0xffffffffffffffaa Total Num Of Uops: 16

  13. Seguí la idea de jcomeau_ictx y modifiqué el TestP.zip de Agner Fog (publicado el 22/12/2015) El uso del puerto en el BDW 306D4:

    Clock Core cyc Instruct uop p0 uop p1 uop p5 uop p6 Code1: 7734720 7734727 17000001 4983410 5016592 5000001 1000001 Code2: 5000072 5000072 17000001 5000010 5000014 4999978 1000002

    La distribución del puerto casi perfecta como en Haswell. Luego revisé los contadores del puesto de recursos (evento 0xa2)

    Clock Core cyc Instruct res.stl. RS stl. SB stl. ROB stl. Code1: 7736212 7736213 17000001 3736191 3736143 0 0 Code2: 5000068 5000072 17000001 1000050 999957 0 0

    Me parece que la diferencia entre Code1 y Code2 proviene del puesto de RS. Observación de Intel SDM: "Ciclos estancados debido a que no hay una entrada RS elegible disponible".

    ¿Cómo puedo evitar este puesto con FMA?

    Actualización5:

  14. El código 6 cambió, ya que Peter Cordes me llamó la atención, solo los vpaddds usan operandos de memoria. Sin efectos en HSW y SKL, BDW empeora.

  15. Como Marat Dukhan midió, no solo vpadd / vpsub / vpand / vpandn / vpxor resultó afectado, sino que otras instrucciones limitadas por Port5 como vmovaps, vblendps, vpermps, vshufps, vbroadcastss;

  16. Como IwillnotexistIdonotexist sugirió, probé con otros operandos. Una modificación exitosa es Code7, donde todos los vpaddds usan ymm15. Esta versión puede producir en BDWs ~ 5000000 clks, pero solo por un tiempo. Después de ~ 6 millones de pares de FMA, alcanza los clics habituales de ~ 7730000:

    Clock Core cyc Instruct res.stl. RS stl. SB stl. ROB stl. 5133724 5110723 17000001 1107998 946376 0 0 6545476 6545482 17000001 2545453 1 0 0 6545468 6545471 17000001 2545437 90910 0 0 5000016 5000019 17000001 999992 999992 0 0 7671620 7617127 17000003 3614464 3363363 0 0 7737340 7737345 17000001 3737321 3737259 0 0 7802916 7747108 17000003 3737478 3735919 0 0 7928784 7796057 17000007 3767962 3676744 0 0 7941072 7847463 17000003 3781103 3651595 0 0 7787812 7779151 17000005 3765109 3685600 0 0 7792524 7738029 17000002 3736858 3736764 0 0 7736000 7736007 17000001 3735983 3735945 0 0

  17. Probé la versión xmm de Code7 como Code8. El efecto es similar, pero el tiempo de ejecución más rápido se mantiene por más tiempo. No he encontrado una diferencia significativa entre un i5-5250U de 1.6GHz y un i7-5775C de 3.7GHz.

  18. 16 y 17 se hizo con HyperThreading deshabilitado. Con HTT habilitado, el efecto es menor.


Actualización: la versión anterior contenía 6 instrucciones de VPADDD (frente a 5 en la pregunta), y el VPADDD adicional causaba desequilibrio en Broadwell. Una vez arreglado, Haswell, Broadwell y Skylake emiten casi el mismo número de uops a los puertos 0, 1 y 5.

No hay contaminación del puerto, pero los uops están programados subóptimamente, la mayoría de los uops van al puerto 5 en Broadwell y lo convierten en el cuello de botella antes de que los puertos 0 y 1 estén saturados.

Para demostrar lo que está sucediendo, sugiero que (ab) use la demostración en PeachPy.IO :

  1. Abra www.peachpy.io en Google Chrome (no funcionaría en otros navegadores).

  2. Reemplace el código predeterminado (que implementa la función SDOT) con el siguiente código, que es literalmente su ejemplo portado a la sintaxis de PeachPy :

    n = Argument(size_t) x = Argument(ptr(const_float_)) incx = Argument(size_t) y = Argument(ptr(const_float_)) incy = Argument(size_t) with Function("sdot", (n, x, incx, y, incy)) as function: reg_n = GeneralPurposeRegister64() LOAD.ARGUMENT(reg_n, n) VZEROALL() with Loop() as loop: for i in range(15): ymm_i = YMMRegister(i) if i < 10: VFMADD231PS(ymm_i, ymm_i, ymm_i) else: VPADDD(ymm_i, ymm_i, ymm_i) DEC(reg_n) JNZ(loop.begin) RETURN()

  3. Tengo varias máquinas en diferentes microarquitecturas como back-end para PeachPy.io. Elija Intel Haswell, Intel Broadwell o Intel Skylake y presione "Quick Run". El sistema compilará su código, lo cargará en el servidor y visualizará los contadores de rendimiento recopilados durante la ejecución.

  4. Aquí está la distribución de uops sobre los puertos de ejecución en Intel Haswell:

  1. Y esta es la misma trama de Intel Broadwell:

  1. Aparentemente, cualquiera que haya sido el error en el programador de uops, fue corregido en Intel Skylake, porque la presión del puerto en esa máquina es la misma que en Haswell.