Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/72.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 为什么使用AVX2时加速比低于预期?_C_X86_Intrinsics_Avx2 - Fatal编程技术网

C 为什么使用AVX2时加速比低于预期?

C 为什么使用AVX2时加速比低于预期?,c,x86,intrinsics,avx2,C,X86,Intrinsics,Avx2,我已经使用AVX2的intrinsics指令对矩阵加法的内环进行了矢量化,我还有来自的延迟表。我认为加速应该是5倍,因为1024次迭代中几乎有4次延迟发生在128次迭代中的6次延迟上,但是加速是3倍。所以问题是这里还有什么我看不到的。我使用的是gcc,用c编写,内部语言,CPU是skylake 6700hq 这是c和组件从内环中取出 全球数据: int __attribute__(( aligned(32))) a[MAX1][MAX2] ; int __attribute__(( aligne

我已经使用AVX2的intrinsics指令对矩阵加法的内环进行了矢量化,我还有来自的延迟表。我认为加速应该是5倍,因为1024次迭代中几乎有4次延迟发生在128次迭代中的6次延迟上,但是加速是3倍。所以问题是这里还有什么我看不到的。我使用的是gcc,用c编写,内部语言,CPU是skylake 6700hq

这是c和组件从内环中取出

全球数据:

int __attribute__(( aligned(32))) a[MAX1][MAX2] ;
int __attribute__(( aligned(32))) b[MAX2][MAX3] ;
int __attribute__(( aligned(32))) c_result[MAX1][MAX3] ;
顺序:

for( i = 0 ; i < MAX1 ; i++)
        for(j = 0 ; j < MAX2 ; j++)
            c_result[i][j] = a[i][j] + b[i][j];

.L16:
    movl    (%r9,%rax), %edx           // latency : 2  , throughput : 0.5   number of execution unit : 4 ALU 
    addl    (%r8,%rax), %edx           // latency : dont know , throughput :    0.5     number of execution unit : 4 ALU 
    movl    %edx, c_result(%rcx,%rax)  // latency : 2 , throughput : 1  number of execution unit : 4 ALU 
    addq    $4, %rax
    cmpq    $4096, %rax
    jne .L16
(i=0;i 对于(j=0;j AVX2:

(i=0;i{ 对于(j=0;j除了循环计数器,没有循环携带的依赖链。因此,来自不同循环迭代的操作可以同时进行。这意味着延迟不是瓶颈,只是吞吐量(执行单元和前端(每个时钟最多4个融合域UOP))

而且,你的数字简直是疯了
mov
加载不需要4个ALU执行单元!并且加载/存储延迟数字是错误的/没有意义的(参见最后一节)

总计:7个融合域UOP表示循环可以从循环缓冲区以每2c一次迭代的速度发出。(不符合1.75c)。因为我们混合使用了加载、存储和ALU UOP,所以执行端口不是瓶颈,只是融合了域4宽的问题宽度。每2c两次加载和每2c一次存储仅为加载和存储执行单元吞吐量的一半

请注意,2寄存器寻址模式。这对于纯负载来说不是问题,因为即使没有微熔合,它们也是1 uop

对于向量循环,分析是相同的。(
vpaddd
在Skylake和几乎所有其他CPU上的延迟为1c。该表没有列出带有内存操作数的
padd
的延迟列中的任何内容,因为加载的延迟与添加的延迟是分开的。只要加载的延迟为(衣服早就知道了。)


Agner Fog的存储和加载延迟数字也有点虚假。他任意将总负载存储往返延迟(使用存储转发)划分为负载和存储的延迟数。IDK为什么他没有列出通过指针跟踪测试测量的加载延迟(例如重复
mov(%rsi),%rsi
)。这表明Intel SnB系列CPU有4个周期的加载使用延迟

我本想给他写封信的,但还没来得及告诉他


您应该看到AVX2的加速比为32/4,即8倍。您的问题大小只有4096B,这足够小,可以容纳三个大小相同的数组,以装入一级缓存。(编辑:问题是误导性的:显示的循环是嵌套循环的内部循环。请参阅注释:显然,即使使用4k阵列(而不是4M),OP仍然只能看到3倍的加速(而使用4M阵列则是1.5倍),因此AVX版本中存在某种瓶颈。)

所有3个阵列都是对齐的,因此在 不需要对齐的内存操作数(
%r8

我的另一个理论似乎也不太可能,但数组地址之间的偏移量是否正好是4096B?来自Agner Fog的Microach PDF:

从地址同时读写是不可能的 以4千字节的倍数间隔的

这个例子显示了一个存储然后加载,所以IDK,如果这真的能解释它的话。即使内存排序硬件认为加载和存储可能位于同一个地址,我也不确定为什么这会阻止代码维持同样多的内存操作,或者为什么它对AVX2代码的影响比标量代码更大


值得尝试用额外的128B或256B或其他方式将数组相互抵消。

除了循环计数器之外,没有循环携带的依赖链。因此,来自不同循环迭代的操作可以同时进行。这意味着延迟不是瓶颈,只是吞吐量(执行单元和前端(每个时钟最多4个融合域UOP))

而且,你的数字简直是疯了
mov
加载不需要4个ALU执行单元!并且加载/存储延迟数字是错误的/没有意义的(参见最后一节)

总计:7个融合域UOP表示循环可以从循环缓冲区以每2c一次迭代的速度发出。(不符合1.75c)。因为我们混合使用了加载、存储和ALU UOP,所以执行端口不是瓶颈,只是融合了域4宽的问题宽度。每2c两次加载和每2c一次存储仅为加载和存储执行单元吞吐量的一半

请注意,2寄存器寻址模式。这对于纯负载来说不是问题,因为即使没有微熔合,它们也是1 uop。

for( i = 0 ; i < MAX1 ; i++){ for(j = 0 ; j < MAX2 ; j += 8){ a0_i= _mm256_add_epi32( _mm256_load_si256((__m256i *)&a[i][j]) , _mm256_load_si256((__m256i *)&b[i][j])); _mm256_store_si256((__m256i *)&c_result[i][j], a0_i); }} .L22: vmovdqa (%rcx,%rax), %ymm0 // latency : 3 , throughput : 0.5 number of execution unit : 4 ALU vpaddd (%r8,%rax), %ymm0, %ymm0 // latency : dont know , throughput : 0.5 number of execution unit : 3 VEC-ALU vmovdqa %ymm0, c_result(%rdx,%rax) // latency : 3 , throughput : 1 number of execution unit : 4 ALU addq $32, %rax cmpq $4096, %rax jne .L22
# Scalar  (serial is the wrong word.  Both versions are serial, not parallel)
.L16:
    movl    (%r9,%rax), %edx           // fused-domain uops: 1.  Unfused domain: a load port
    addl    (%r8,%rax), %edx           // fused-domain uops: 2   Unfused domain: a load port and any ALU port
    movl    %edx, c_result(%rcx,%rax)  // fused-domain uops: 2   Unfused domain: store-address and store-data ports.  port7 can't handle 2-reg addresses
    addq    $4, %rax                   // fused-domain uops: 1   unfused: any ALU
    cmpq    $4096, %rax                // fused-domain uops: 0 (fused with jcc)
    jne .L16                           // fused-domain uops: 1   unfused: port6 (predicted-taken branch)