Anomalía significativa en el rendimiento de FMA experimentada en el procesador Intel Broadwell

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

Código2:

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

Código3 (igual que Código2 pero con prefijo VEX largo):

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

Código4 (igual que Código1 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 que no es 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

Código6b: (revisado, operandos de memoria 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

Código8: (igual 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 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 

¿Alguien puede explicar qué sucede con Code1 en Broadwell?Supongo que Broadwell de alguna manera contamina el Puerto 1 con vpaddds en el caso del Código 1, sin embargo, Haswell puede usar el Puerto 5 solo si el Puerto 0 y el Puerto 1 están llenos;

¿Tienes alguna idea para lograr el ~ 5000000 clk en Broadwell con instrucciones de FMA?

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

Usé Windows 8.1 y Win 10;

Actualizar:

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

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

Subió un código de muestra de comunidad + MASM VS2015aquí

Actualización2:

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

Actualización3:

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 muestra actualizado con Code4 y Code5.

Actualización4:

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

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

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

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 verifiqué los contadores de pérdida 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 Code1 y Code2 proviene del puesto RS. Comentario 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:

Code6 cambió, cuando Peter Cordes me llamó la atención, solo los vpaddds usan operandos de memoria. Sin efecto sobre HSW y SKL, BDW empeora.

Tal como lo midió Marat Dukhan, no solo vpadd / vpsub / vpand / vpandn / vpxor se vieron afectados, sino también otras instrucciones limitadas de Port5 como vmovaps, vblendps, vpermps, vshufps, vbroadcastss;

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 BDW ~ 5000000 clks, pero solo por un tiempo. Después de ~ 6 millones de pares de FMA, alcanza los ~ 7730000 clks habituales:

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

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 diferencias significativas entre un i5-5250U de 1.6GHz y un i7-5775C de 3.7GHz.

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

Respuestas a la pregunta(2)

Su respuesta a la pregunta