Anomalia significativa no desempenho de FMA experimentada no processador 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

Code3 (o mesmo que Code2, mas com prefixo VEX longo):

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 (o mesmo que Code1, mas com 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

Código5 (o mesmo que o Código1, mas com vpsubd`s sem zeros):

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: (revisado, operandos de memória apenas 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

Código7: (igual ao Código1, mas o 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 ao Código7, mas usa xmm em vez 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

Relógios TSC medidos com Turbo e C1E desativados:

          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 

Alguém pode explicar o que acontece com o Code1 em Broadwell?Meu palpite é que Broadwell de alguma forma contamina a Porta1 com vpaddds no caso do Código1, no entanto, o Haswell pode usar a Porta5 apenas se a Porta0 e a Porta1 estiverem cheias.;

Você tem alguma ideia de realizar o ~ 5000000 clk em Broadwell com instruções FMA?

Eu tentei reordenar. Comportamento semelhante experimentado com double e qword;

Eu usei o Windows 8.1 e o Win 10;

Atualizar:

Adicionado Code3 como a idéia de Marat Dukhan com VEX longo;

Ampliou a tabela de resultados com as experiências Skylake;

Carregou um código de amostra Comunidade VS2015 + MASMaqui

Update2:

Eu tentei com registros xmm em vez de ymm (código 4). Mesmo resultado em Broadwell.

Update3:

Adicionei o Code5 como ideia de Peter Cordes (substitua o vpaddd por outras instruções (vpxor, vpor, vpand, vpandn, vpsubd)). Se a nova instrução não for um idioma de zeragem (vpxor, vpsubd com o mesmo registro), o resultado será o mesmo no BDW. Projeto de amostra atualizado com o Code4 e o Code5.

Update4:

Adicionei Code6 como ideia de Stephen Canon (operandos de memória). O resultado é ~ 8200000 clks. Projeto de amostra atualizado com o Code6;

Eu verifiquei o freq da CPU e a possível limitação com o Teste de Estabilidade do Sistema da AIDA64. A frequência é estável e nenhum sinal de estrangulamento;

Análise de rendimento 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

Segui a idéia do jcomeau_ictx e modifiquei o testp.zip do Agner Fog (publicado em 2015-12-22) O uso da porta no 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

A distribuição portuária quase perfeita como no Haswell. Depois verifiquei os contadores de paralisação 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

Parece-me a diferença de Code1 e Code2 que vem da parada do RS. Observação do Intel SDM: "Ciclos interrompidos devido a nenhuma entrada RS qualificada disponível".

Como evitar essa parada com as FMA?

Update5:

O Code6 mudou, como Peter Cordes chamou minha atenção, apenas os vpaddds usam operandos de memória. Nenhum efeito no HSW e SKL, o BDW piora.

Como Marat Dukhan mediu, não apenas o vpadd / vpsub / vpand / vpandn / vpxor afetou, mas outras instruções limitadas do Port5 como vmovaps, vblendps, vpermps, vshufps, vbroadcastss;

Como não existirei sugerir, tentei com outros operandos. Uma modificação bem-sucedida é o Code7, onde todos os vpaddds usam ymm15. Esta versão pode produzir em BDWs ~ 5000000 clks, mas apenas por um tempo. Após ~ 6 milhões de pares de FMA, atinge os ~ 7730000 clks usuais:

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

Eu tentei a versão xmm do Code7 como Code8. O efeito é semelhante, mas o tempo de execução mais rápido dura mais tempo. Não encontrei diferença significativa entre o i5-5250U de 1.6GHz e o i7-5775C de 3.7GHz.

16 e 17 foram feitas com o HyperThreading desativado. Com HTT ativado, o efeito é menor.

questionAnswers(2)

yourAnswerToTheQuestion