Obtaining peak bandwidth on Haswell in the L1 cache: only getting 62%
我正在尝试在L1高速缓存中获取完整带宽,以用于Intel处理器上的以下功能
好的。
1 2 3 4 5 6 | float triad(float *x, float *y, float *z, const int n) { float k = 3.14159f; for(int i=0; i<n; i++) { z[i] = x[i] + k*y[i]; } } |
这是来自STREAM的三合会功能。
好的。
使用具有此功能的SandyBridge / IvyBridge处理器(与NASM配合使用),可以得到约95%的峰。但是,使用Haswell只能达到峰值的62%,除非展开循环。如果我解开16次,我将得到92%。我不明白
好的。
我决定使用NASM在汇编中编写函数。汇编中的主循环如下所示。
好的。
1 2 3 4 5 6 | .L2: vmovaps ymm1, [rdi+rax] vfmadd231ps ymm1, ymm2, [rsi+rax] vmovaps [rdx+rax], ymm1 add rax, 32 jne .L2 |
在Agner Fog的"优化组装"手册的示例12.7-12.11中,他对Pentium M,Core 2,Sandy Bridge,FMA4和FMA3所做的操作几乎相同(但对于
好的。
1 2 3 4 5 6 7 8 9 | ports size μops-fused 0 1 2 3 4 5 6 7 vmovaps 5 1 ? ? vfmadd231ps 6 1 ? ? ? ? vmovaps 5 1 1 1 add 4 ? ? jne 2 ? ? -------------------------------------------------------------- total 22 4 ? ? 1 1 1 0 1 1 |
大小是指指令长度(以字节为单位)。
好的。
根据该表以及所有Core2处理器每个时钟周期都可以执行4μop的事实,看来该循环应该在每个时钟周期都可以实现,但是我还没有设法获得它。有人可以向我解释为什么我不能不展开就无法接近Haswell上此功能的峰值带宽吗?如果不展开就可以吗?如果可以,怎么办?让我清楚一点,我实际上是在尝试为此功能最大化ILP(我不仅想要最大的带宽),所以这就是我不想展开的原因。
好的。
编辑:
这是自Iwillnotexist Idonotexist使用IACA表示商店从未使用端口7以来的更新。我设法在不展开的情况下突破了66%的限制,并且在每次迭代中都在一个时钟周期内完成了理论上的展开(理论上)。让我们首先解决商店问题。
好的。
Stephen Canon在评论中提到,端口7中的地址生成单元(AGU)只能处理诸如
好的。
Port7 AGU can only work on stores with simple memory address (no index register).
Ok.
Stephen Canon建议"将存储地址用作加载操作数的偏移量"。我已经这样尝试过
好的。
1 2 3 4 5 6 | vmovaps ymm1, [rdi + r9 + 32*i] vfmadd231ps ymm1, ymm2, [rsi + r9 + 32*i] vmovaps [r9 + 32*i], ymm1 add r9, 32*unroll cmp r9, rcx jne .L2 |
这确实导致商店使用端口7。但是,另一个问题是
好的。
但是我找到了一种方法来使
好的。
1 2 3 4 5 | vmovaps ymm1, [src1_end + rax] vfmadd231ps ymm1, ymm2, [src2_end + rax] vmovaps [dst_end + rax], ymm1 add rax, 32 jl .L2 |
其中
好的。
这重现了我所问问题的表格,其中包含我期望的四个融合微操作。如果将此内容放入IACA,则报告的块吞吐量为1.0。从理论上讲,这应该与SSE和AVX版本一样好。实际上,它达到峰值的72%。这打破了66%的壁垒,但距离我展开16次的92%的差距还有很长的路要走。因此,在Haswell上接近峰顶的唯一选择是展开。通过Ivy Bridge在Core2上不需要这样做,但在Haswell上则不需要。
好的。
结束编辑:
好的。
这是C / C ++ Linux代码进行测试。 NASM代码在C / C ++代码之后发布。您唯一需要更改的是频率编号。在
好的。
编译
好的。
1 2 3 4 5 6 | nasm -f elf64 triad_sse_asm.asm nasm -f elf64 triad_avx_asm.asm nasm -f elf64 triad_fma_asm.asm g++ -m64 -lrt -O3 -mfma tests.cpp triad_fma_asm.o -o tests_fma g++ -m64 -lrt -O3 -mavx tests.cpp triad_avx_asm.o -o tests_avx g++ -m64 -lrt -O3 -msse2 tests.cpp triad_sse_asm.o -o tests_sse |
C / C ++代码
好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | #include <x86intrin.h> #include <stdio.h> #include <string.h> #include <time.h> #define TIMER_TYPE CLOCK_REALTIME extern"C" float triad_sse_asm_repeat(float *x, float *y, float *z, const int n, int repeat); extern"C" float triad_sse_asm_repeat_unroll16(float *x, float *y, float *z, const int n, int repeat); extern"C" float triad_avx_asm_repeat(float *x, float *y, float *z, const int n, int repeat); extern"C" float triad_avx_asm_repeat_unroll16(float *x, float *y, float *z, const int n, int repeat); extern"C" float triad_fma_asm_repeat(float *x, float *y, float *z, const int n, int repeat); extern"C" float triad_fma_asm_repeat_unroll16(float *x, float *y, float *z, const int n, int repeat); #if (defined(__FMA__)) float triad_fma_repeat(float *x, float *y, float *z, const int n, int repeat) { float k = 3.14159f; int r; for(r=0; r<repeat; r++) { int i; __m256 k4 = _mm256_set1_ps(k); for(i=0; i<n; i+=8) { _mm256_store_ps(&z[i], _mm256_fmadd_ps(k4, _mm256_load_ps(&y[i]), _mm256_load_ps(&x[i]))); } } } #elif (defined(__AVX__)) float triad_avx_repeat(float *x, float *y, float *z, const int n, int repeat) { float k = 3.14159f; int r; for(r=0; r<repeat; r++) { int i; __m256 k4 = _mm256_set1_ps(k); for(i=0; i<n; i+=8) { _mm256_store_ps(&z[i], _mm256_add_ps(_mm256_load_ps(&x[i]), _mm256_mul_ps(k4, _mm256_load_ps(&y[i])))); } } } #else float triad_sse_repeat(float *x, float *y, float *z, const int n, int repeat) { float k = 3.14159f; int r; for(r=0; r<repeat; r++) { int i; __m128 k4 = _mm_set1_ps(k); for(i=0; i<n; i+=4) { _mm_store_ps(&z[i], _mm_add_ps(_mm_load_ps(&x[i]), _mm_mul_ps(k4, _mm_load_ps(&y[i])))); } } } #endif double time_diff(timespec start, timespec end) { timespec temp; if ((end.tv_nsec-start.tv_nsec)<0) { temp.tv_sec = end.tv_sec-start.tv_sec-1; temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; } else { temp.tv_sec = end.tv_sec-start.tv_sec; temp.tv_nsec = end.tv_nsec-start.tv_nsec; } return (double)temp.tv_sec + (double)temp.tv_nsec*1E-9; } int main () { int bytes_per_cycle = 0; double frequency = 1.3; //Haswell //double frequency = 3.6; //IB //double frequency = 2.66; //Core2 #if (defined(__FMA__)) bytes_per_cycle = 96; #elif (defined(__AVX__)) bytes_per_cycle = 48; #else bytes_per_cycle = 24; #endif double peak = frequency*bytes_per_cycle; const int n =2048; float* z2 = (float*)_mm_malloc(sizeof(float)*n, 64); char *mem = (char*)_mm_malloc(1<<18,4096); char *a = mem; char *b = a+n*sizeof(float); char *c = b+n*sizeof(float); float *x = (float*)a; float *y = (float*)b; float *z = (float*)c; for(int i=0; i<n; i++) { x[i] = 1.0f*i; y[i] = 1.0f*i; z[i] = 0; } int repeat = 1000000; timespec time1, time2; #if (defined(__FMA__)) triad_fma_repeat(x,y,z2,n,repeat); #elif (defined(__AVX__)) triad_avx_repeat(x,y,z2,n,repeat); #else triad_sse_repeat(x,y,z2,n,repeat); #endif while(1) { double dtime, rate; clock_gettime(TIMER_TYPE, &time1); #if (defined(__FMA__)) triad_fma_asm_repeat(x,y,z,n,repeat); #elif (defined(__AVX__)) triad_avx_asm_repeat(x,y,z,n,repeat); #else triad_sse_asm_repeat(x,y,z,n,repeat); #endif clock_gettime(TIMER_TYPE, &time2); dtime = time_diff(time1,time2); rate = 3.0*1E-9*sizeof(float)*n*repeat/dtime; printf("unroll1 rate %6.2f GB/s, efficency %6.2f%%, error %d\ ", rate, 100*rate/peak, memcmp(z,z2, sizeof(float)*n)); clock_gettime(TIMER_TYPE, &time1); #if (defined(__FMA__)) triad_fma_repeat(x,y,z,n,repeat); #elif (defined(__AVX__)) triad_avx_repeat(x,y,z,n,repeat); #else triad_sse_repeat(x,y,z,n,repeat); #endif clock_gettime(TIMER_TYPE, &time2); dtime = time_diff(time1,time2); rate = 3.0*1E-9*sizeof(float)*n*repeat/dtime; printf("intrinsic rate %6.2f GB/s, efficency %6.2f%%, error %d\ ", rate, 100*rate/peak, memcmp(z,z2, sizeof(float)*n)); clock_gettime(TIMER_TYPE, &time1); #if (defined(__FMA__)) triad_fma_asm_repeat_unroll16(x,y,z,n,repeat); #elif (defined(__AVX__)) triad_avx_asm_repeat_unroll16(x,y,z,n,repeat); #else triad_sse_asm_repeat_unroll16(x,y,z,n,repeat); #endif clock_gettime(TIMER_TYPE, &time2); dtime = time_diff(time1,time2); rate = 3.0*1E-9*sizeof(float)*n*repeat/dtime; printf("unroll16 rate %6.2f GB/s, efficency %6.2f%%, error %d\ ", rate, 100*rate/peak, memcmp(z,z2, sizeof(float)*n)); } } |
使用System V AMD64 ABI的NASM代码。
好的。
triad_fma_asm.asm:
好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | global triad_fma_asm_repeat ;RDI x, RSI y, RDX z, RCX n, R8 repeat ;z[i] = y[i] + 3.14159*x[i] pi: dd 3.14159 ;align 16 section .text triad_fma_asm_repeat: shl rcx, 2 add rdi, rcx add rsi, rcx add rdx, rcx vbroadcastss ymm2, [rel pi] ;neg rcx align 16 .L1: mov rax, rcx neg rax align 16 .L2: vmovaps ymm1, [rdi+rax] vfmadd231ps ymm1, ymm2, [rsi+rax] vmovaps [rdx+rax], ymm1 add rax, 32 jne .L2 sub r8d, 1 jnz .L1 vzeroupper ret global triad_fma_asm_repeat_unroll16 section .text triad_fma_asm_repeat_unroll16: shl rcx, 2 add rcx, rdi vbroadcastss ymm2, [rel pi] .L1: xor rax, rax mov r9, rdi mov r10, rsi mov r11, rdx .L2: %assign unroll 32 %assign i 0 %rep unroll vmovaps ymm1, [r9 + 32*i] vfmadd231ps ymm1, ymm2, [r10 + 32*i] vmovaps [r11 + 32*i], ymm1 %assign i i+1 %endrep add r9, 32*unroll add r10, 32*unroll add r11, 32*unroll cmp r9, rcx jne .L2 sub r8d, 1 jnz .L1 vzeroupper ret |
triad_ava_asm.asm:
好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | global triad_avx_asm_repeat ;RDI x, RSI y, RDX z, RCX n, R8 repeat pi: dd 3.14159 align 16 section .text triad_avx_asm_repeat: shl rcx, 2 add rdi, rcx add rsi, rcx add rdx, rcx vbroadcastss ymm2, [rel pi] ;neg rcx align 16 .L1: mov rax, rcx neg rax align 16 .L2: vmulps ymm1, ymm2, [rdi+rax] vaddps ymm1, ymm1, [rsi+rax] vmovaps [rdx+rax], ymm1 add rax, 32 jne .L2 sub r8d, 1 jnz .L1 vzeroupper ret global triad_avx_asm_repeat2 ;RDI x, RSI y, RDX z, RCX n, R8 repeat ;pi: dd 3.14159 align 16 section .text triad_avx_asm_repeat2: shl rcx, 2 vbroadcastss ymm2, [rel pi] align 16 .L1: xor rax, rax align 16 .L2: vmulps ymm1, ymm2, [rdi+rax] vaddps ymm1, ymm1, [rsi+rax] vmovaps [rdx+rax], ymm1 add eax, 32 cmp eax, ecx jne .L2 sub r8d, 1 jnz .L1 vzeroupper ret global triad_avx_asm_repeat_unroll16 align 16 section .text triad_avx_asm_repeat_unroll16: shl rcx, 2 add rcx, rdi vbroadcastss ymm2, [rel pi] align 16 .L1: xor rax, rax mov r9, rdi mov r10, rsi mov r11, rdx align 16 .L2: %assign unroll 16 %assign i 0 %rep unroll vmulps ymm1, ymm2, [r9 + 32*i] vaddps ymm1, ymm1, [r10 + 32*i] vmovaps [r11 + 32*i], ymm1 %assign i i+1 %endrep add r9, 32*unroll add r10, 32*unroll add r11, 32*unroll cmp r9, rcx jne .L2 sub r8d, 1 jnz .L1 vzeroupper ret |
triad_sse_asm.asm:
好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | global triad_sse_asm_repeat ;RDI x, RSI y, RDX z, RCX n, R8 repeat pi: dd 3.14159 ;align 16 section .text triad_sse_asm_repeat: shl rcx, 2 add rdi, rcx add rsi, rcx add rdx, rcx movss xmm2, [rel pi] shufps xmm2, xmm2, 0 ;neg rcx align 16 .L1: mov rax, rcx neg rax align 16 .L2: movaps xmm1, [rdi+rax] mulps xmm1, xmm2 addps xmm1, [rsi+rax] movaps [rdx+rax], xmm1 add rax, 16 jne .L2 sub r8d, 1 jnz .L1 ret global triad_sse_asm_repeat2 ;RDI x, RSI y, RDX z, RCX n, R8 repeat ;pi: dd 3.14159 ;align 16 section .text triad_sse_asm_repeat2: shl rcx, 2 movss xmm2, [rel pi] shufps xmm2, xmm2, 0 align 16 .L1: xor rax, rax align 16 .L2: movaps xmm1, [rdi+rax] mulps xmm1, xmm2 addps xmm1, [rsi+rax] movaps [rdx+rax], xmm1 add eax, 16 cmp eax, ecx jne .L2 sub r8d, 1 jnz .L1 ret global triad_sse_asm_repeat_unroll16 section .text triad_sse_asm_repeat_unroll16: shl rcx, 2 add rcx, rdi movss xmm2, [rel pi] shufps xmm2, xmm2, 0 .L1: xor rax, rax mov r9, rdi mov r10, rsi mov r11, rdx .L2: %assign unroll 8 %assign i 0 %rep unroll movaps xmm1, [r9 + 16*i] mulps xmm1, xmm2, addps xmm1, [r10 + 16*i] movaps [r11 + 16*i], xmm1 %assign i i+1 %endrep add r9, 16*unroll add r10, 16*unroll add r11, 16*unroll cmp r9, rcx jne .L2 sub r8d, 1 jnz .L1 ret |
好。
IACA分析
使用IACA(英特尔架构代码分析器)表明,确实发生了宏运算融合,而这并不是问题所在。是Mysticial谁是正确的:问题是商店根本没有使用Port 7。
好的。
IACA报告以下内容:
好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | Intel(R) Architecture Code Analyzer Version - 2.1 Analyzed File - ../../../tests_fma Binary Format - 64Bit Architecture - HSW Analysis Type - Throughput Throughput Analysis Report -------------------------- Block Throughput: 1.55 Cycles Throughput Bottleneck: FrontEnd, PORT2_AGU, PORT3_AGU Port Binding In Cycles Per Iteration: --------------------------------------------------------------------------------------- | Port | 0 - DV | 1 | 2 - D | 3 - D | 4 | 5 | 6 | 7 | --------------------------------------------------------------------------------------- | Cycles | 0.5 0.0 | 0.5 | 1.5 1.0 | 1.5 1.0 | 1.0 | 0.0 | 1.0 | 0.0 | --------------------------------------------------------------------------------------- N - port number or number of cycles resource conflict caused delay, DV - Divider pipe (on port 0) D - Data fetch pipe (on ports 2 and 3), CP - on a critical path F - Macro Fusion with the previous instruction occurred * - instruction micro-ops not bound to a port ^ - Micro Fusion happened # - ESP Tracking sync uop was issued @ - SSE instruction followed an AVX256 instruction, dozens of cycles penalty is expected ! - instruction not supported, was not accounted in Analysis | Num Of | Ports pressure in cycles | | | Uops | 0 - DV | 1 | 2 - D | 3 - D | 4 | 5 | 6 | 7 | | --------------------------------------------------------------------------------- | 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [rdi+rax*1] | 2 | 0.5 | 0.5 | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [rsi+rax*1] | 2 | | | 0.5 | 0.5 | 1.0 | | | | CP | vmovaps ymmword ptr [rdx+rax*1], ymm1 | 1 | | | | | | | 1.0 | | | add rax, 0x20 | 0F | | | | | | | | | | jnz 0xffffffffffffffec Total Num Of Uops: 6 |
特别是,报告的周期(1.5)中的块吞吐量非常好,效率为66%。
好的。
英特尔员工在
好的。
Port7 AGU can only work on stores with simple memory address (no index register). This is why the above analysis doesn't show port7 utilization.
Ok.
blockquote>
这可以确定为什么不使用Port 7。
好的。
现在,将上面的内容与32x展开循环进行对比(事实证明
unroll16 shoudl实际上被称为unroll32 ):好的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 Intel(R) Architecture Code Analyzer Version - 2.1
Analyzed File - ../../../tests_fma
Binary Format - 64Bit
Architecture - HSW
Analysis Type - Throughput
Throughput Analysis Report
--------------------------
Block Throughput: 32.00 Cycles Throughput Bottleneck: PORT2_AGU, Port2_DATA, PORT3_AGU, Port3_DATA, Port4, Port7
Port Binding In Cycles Per Iteration:
---------------------------------------------------------------------------------------
| Port | 0 - DV | 1 | 2 - D | 3 - D | 4 | 5 | 6 | 7 |
---------------------------------------------------------------------------------------
| Cycles | 16.0 0.0 | 16.0 | 32.0 32.0 | 32.0 32.0 | 32.0 | 2.0 | 2.0 | 32.0 |
---------------------------------------------------------------------------------------
N - port number or number of cycles resource conflict caused delay, DV - Divider pipe (on port 0)
D - Data fetch pipe (on ports 2 and 3), CP - on a critical path
F - Macro Fusion with the previous instruction occurred
* - instruction micro-ops not bound to a port
^ - Micro Fusion happened
# - ESP Tracking sync uop was issued
@ - SSE instruction followed an AVX256 instruction, dozens of cycles penalty is expected
! - instruction not supported, was not accounted in Analysis
| Num Of | Ports pressure in cycles | |
| Uops | 0 - DV | 1 | 2 - D | 3 - D | 4 | 5 | 6 | 7 | |
---------------------------------------------------------------------------------
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9]
| 2^ | 1.0 | | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x20]
| 2^ | | 1.0 | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x20]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x20], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x40]
| 2^ | 1.0 | | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x40]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x40], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x60]
| 2^ | | 1.0 | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x60]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x60], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x80]
| 2^ | 1.0 | | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x80]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x80], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0xa0]
| 2^ | | 1.0 | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0xa0]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0xa0], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0xc0]
| 2^ | 1.0 | | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0xc0]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0xc0], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0xe0]
| 2^ | | 1.0 | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0xe0]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0xe0], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x100]
| 2^ | 1.0 | | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x100]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x100], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x120]
| 2^ | | 1.0 | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x120]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x120], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x140]
| 2^ | 1.0 | | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x140]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x140], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x160]
| 2^ | | 1.0 | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x160]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x160], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x180]
| 2^ | 1.0 | | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x180]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x180], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x1a0]
| 2^ | | 1.0 | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x1a0]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x1a0], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x1c0]
| 2^ | 1.0 | | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x1c0]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x1c0], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x1e0]
| 2^ | | 1.0 | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x1e0]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x1e0], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x200]
| 2^ | 1.0 | | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x200]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x200], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x220]
| 2^ | | 1.0 | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x220]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x220], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x240]
| 2^ | 1.0 | | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x240]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x240], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x260]
| 2^ | | 1.0 | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x260]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x260], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x280]
| 2^ | 1.0 | | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x280]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x280], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x2a0]
| 2^ | | 1.0 | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x2a0]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x2a0], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x2c0]
| 2^ | 1.0 | | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x2c0]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x2c0], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x2e0]
| 2^ | | 1.0 | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x2e0]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x2e0], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x300]
| 2^ | 1.0 | | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x300]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x300], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x320]
| 2^ | | 1.0 | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x320]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x320], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x340]
| 2^ | 1.0 | | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x340]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x340], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x360]
| 2^ | | 1.0 | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x360]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x360], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x380]
| 2^ | 1.0 | | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x380]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x380], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x3a0]
| 2^ | | 1.0 | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x3a0]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x3a0], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x3c0]
| 2^ | 1.0 | | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x3c0]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x3c0], ymm1
| 1 | | | 1.0 1.0 | | | | | | CP | vmovaps ymm1, ymmword ptr [r9+0x3e0]
| 2^ | | 1.0 | | 1.0 1.0 | | | | | CP | vfmadd231ps ymm1, ymm2, ymmword ptr [r10+0x3e0]
| 2^ | | | | | 1.0 | | | 1.0 | CP | vmovaps ymmword ptr [r11+0x3e0], ymm1
| 1 | | | | | | 1.0 | | | | add r9, 0x400
| 1 | | | | | | | 1.0 | | | add r10, 0x400
| 1 | | | | | | 1.0 | | | | add r11, 0x400
| 1 | | | | | | | 1.0 | | | cmp r9, rcx
| 0F | | | | | | | | | | jnz 0xfffffffffffffcaf
Total Num Of Uops: 164我们在这里看到微融合,并正确安排了到端口7的存储调度。
好的。
手动分析(请参见上面的编辑)
现在,我可以回答您的第二个问题:是否可以在不展开的情况下实现,如果可以,该怎么办?答案是不。
好的。
我为左右实验在数组左右分别填充了
x ,y 和z 数组,并将内部循环更改为以下内容:好的。
1
2
3
4
5
6 .L2:
vmovaps ymm1, [rdi+rax] ; 1L
vmovaps ymm0, [rsi+rax] ; 2L
vmovaps [rdx+rax], ymm2 ; S1
add rax, 32 ; ADD
jne .L2 ; JMP这有意不使用FMA(仅加载和存储),并且所有加载/存储指令都没有依赖性,因为因此,无论有什么危险,都可以防止将它们发布到任何执行端口中。
好的。
然后,我测试了第一个和第二个加载(
1L 和2L ),存储(S1 )和加(A )的每个单个排列,同时最后保留了条件跳转(J ) ,对于每一种,我都测试了x ,y 和z 的偏移量每种可能组合的0或-32字节(以纠正在r+r 之一之前重新排列add rax, 32 的事实索引会导致加载或存储定位到错误的地址)。循环对齐为32个字节。这些测试在Linux上通过echo '0' > /sys/devices/system/cpu/cpufreq/boost 禁用了TurboBoost的2.4GHz i7-4700MQ上运行,并且频率常数使用2.4。以下是效率结果(最多24个):好的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 Cases: 0 1 2 3 4 5 6 7
L1 L2 S L1 L2 S L1 L2 S L1 L2 S L1 L2 S L1 L2 S L1 L2 S L1 L2 S
-0 -0 -0 -0 -0 -32 -0 -32 -0 -0 -32 -32 -32 -0 -0 -32 -0 -32 -32 -32 -0 -32 -32 -32
________________________________________________________________________________________________
12SAJ: 65.34% 65.34% 49.63% 65.07% 49.70% 65.05% 49.22% 65.07%
12ASJ: 48.59% 64.48% 48.74% 49.69% 48.75% 49.69% 48.99% 48.60%
1A2SJ: 49.69% 64.77% 48.67% 64.06% 49.69% 49.69% 48.94% 49.69%
1AS2J: 48.61% 64.66% 48.73% 49.71% 48.77% 49.69% 49.05% 48.74%
1S2AJ: 49.66% 65.13% 49.49% 49.66% 48.96% 64.82% 49.02% 49.66%
1SA2J: 64.44% 64.69% 49.69% 64.34% 49.69% 64.41% 48.75% 64.14%
21SAJ: 65.33%* 65.34% 49.70% 65.06% 49.62% 65.07% 49.22% 65.04%
21ASJ: Hypothetically =12ASJ
2A1SJ: Hypothetically =1A2SJ
2AS1J: Hypothetically =1AS2J
2S1AJ: Hypothetically =1S2AJ
2SA1J: Hypothetically =1SA2J
S21AJ: 48.91% 65.19% 49.04% 49.72% 49.12% 49.63% 49.21% 48.95%
S2A1J: Hypothetically =S1A2J
SA21J: Hypothetically =SA12J
SA12J: 64.69% 64.93% 49.70% 64.66% 49.69% 64.27% 48.71% 64.56%
S12AJ: 48.90% 65.20% 49.12% 49.63% 49.03% 49.70% 49.21%* 48.94%
S1A2J: 49.69% 64.74% 48.65% 64.48% 49.43% 49.69% 48.66% 49.69%
A2S1J: Hypothetically =A1S2J
A21SJ: Hypothetically =A12SJ
A12SJ: 64.62% 64.45% 49.69% 64.57% 49.69% 64.45% 48.58% 63.99%
A1S2J: 49.72% 64.69% 49.72% 49.72% 48.67% 64.46% 48.95% 49.72%
AS21J: Hypothetically =AS21J
AS12J: 48.71% 64.53% 48.76% 49.69% 48.76% 49.74% 48.93% 48.69%我们可以从表中注意到一些事情:
好的。
结果达到几个平稳状态,但只有两个主要结果:略低于50%和大约65%。 L1和L2可以在彼此之间自由置换,而不会影响结果。 将访问偏移-32字节可以改变效率。 我们感兴趣的模式(加载1,加载2,存储1和跳转,并在它们周围的任意位置加上-32正确应用的偏移量)都相同,并且都位于较高的高原地区: 12SAJ 情况0(未应用偏移),效率为65.34%(最高)12ASJ 情况1(S-32 ),效率64.48%1A2SJ 情况3(2L-32 ,S-32 ),效率64.06%A12SJ 情况7(1L-32 ,2L-32 ,S-32 ),效率为63.99%好的。
对于每个排列,总是存在至少一个"情况",从而可以在更高的效率平台上执行。特别是案例1(其中 S-32 )似乎可以保证这一点。案例2、4和6保证了在较低的高原上执行。它们的共同点是,其中一个或两个负载偏移了-32,而商店却没有。 对于情况0、3、5和7,取决于排列。 好的。
从那里我们至少可以得出一些结论:
好的。
执行端口2和3确实不在乎它们生成和加载哪个加载地址。 add 和jmp 的宏操作融合似乎不受指令的任何排列影响(特别是在情况1偏移的情况下),使我相信@Evgeny Kluev的结论是错误的:add 与jne 似乎不影响它们的融合。我现在可以肯定地说,Haswell ROB可以正确处理此问题。Evgeny看到的结果(在情况0中,从 12SAJ 效率为65%转换为效率为49%的其他变量)仅仅是由于从中加载和存储的地址的值,而不是由于内核的无效宏融合add和branch。此外,由于平均循环时间为1.5 CC,因此必须至少在某些时间进行宏运算融合。如果没有发生宏视融合,则这将是最低2CC。 好的。
在未展开的循环中测试了指令的所有有效和无效排列之后,没有发现高于65.34%的内容了。这凭经验以"否"回答了是否有可能在不展开的情况下使用全部带宽的问题。 好的。
我将假设几种可能的解释:
好的。
由于地址相对于彼此的值,我们看到了一些奇怪的变态。 如果是这样,那么将存在一组偏移量 x ,y 和z ,这些偏移量将允许最大吞吐量。我的快速随机测试似乎不支持这一点。好的。
我们看到循环以两步模式运行;循环迭代以一个时钟周期交替运行,然后是两个时钟周期。
好的。
这可能是宏运算融合受到解码器的影响。从Agner Fog:
好的。
不能在Sandy Bridge和Ivy Bridge处理器的四个解码器中的最后一个中解码可熔的算术/逻辑指令。我还没有测试过这是否也适用于Haswell。
好的。
或者,每隔一个时钟周期向"错误"端口发出一条指令,从而在一个额外的时钟周期内阻止下一次迭代。这种情况将在下一个时钟周期内自动纠正,但仍会保持振荡。 如果有人可以访问英特尔性能计数器,则应查看事件 UOPS_EXECUTED_PORT.PORT_[0-7] 。如果没有发生振荡,则在相关的时间段内,所有使用的端口都将被固定。否则,如果发生振荡,将有50%的分裂。尤其重要的是查看Mystical指出的端口(0、1、6和7)。好的。
好的。
好的。
这是我认为没有发生的事情:
好的。
我不认为融合的算术+分支uop会通过转到端口0来阻止执行,因为预测采用的分支仅发送到端口6(请参见 Haswell -> Control transfer instructions 下的Agner Fog的指令表)。经过上面循环的几次迭代之后,分支预测器将得知该分支是一个循环,并且始终按所采用的那样进行预测。好的。
我相信这是英特尔性能计数器可以解决的问题。
好的。
好。