Gcc 在标量矩阵加法中使用VADD而不是ADDS有什么好处?

Gcc 在标量矩阵加法中使用VADD而不是ADDS有什么好处?,gcc,assembly,x86,sse,avx,Gcc,Assembly,X86,Sse,Avx,我已经实现了标量矩阵加法内核 #include <stdio.h> #include <time.h> //#include <x86intrin.h> //loops and iterations: #define N 128 #define M N #define NUM_LOOP 1000000 float __attribute__(( aligned(32))) A[N][M], __attribute__(( aligne

我已经实现了标量矩阵加法内核

#include <stdio.h>
#include <time.h>
//#include <x86intrin.h>

//loops and iterations:
#define N 128
#define M N
#define NUM_LOOP 1000000


float   __attribute__(( aligned(32))) A[N][M],
        __attribute__(( aligned(32))) B[N][M],
        __attribute__(( aligned(32))) C[N][M];

int main()
{
int w=0, i, j;
struct timespec tStart, tEnd;//used to record the processiing time
double tTotal , tBest=10000;//minimum of toltal time will asign to the best time
do{
    clock_gettime(CLOCK_MONOTONIC,&tStart);

    for( i=0;i<N;i++){
        for(j=0;j<M;j++){
            C[i][j]= A[i][j] + B[i][j];
        }
    }

    clock_gettime(CLOCK_MONOTONIC,&tEnd);
    tTotal = (tEnd.tv_sec - tStart.tv_sec);
    tTotal += (tEnd.tv_nsec - tStart.tv_nsec) / 1000000000.0;
    if(tTotal<tBest)
        tBest=tTotal;
    } while(w++ < NUM_LOOP);

printf(" The best time: %lf sec in %d repetition for %dX%d matrix\n",tBest,w, N, M);
return 0;
}
gcc-O2-mavx
:128X128矩阵的最佳时间:1000001次重复中的0.000009秒

movss   xmm1, DWORD PTR A[rcx+rax]
addss   xmm1, DWORD PTR B[rcx+rax]
movss   DWORD PTR C[rcx+rax], xmm1
vmovss  xmm1, DWORD PTR A[rcx+rax]
vaddss  xmm1, xmm1, DWORD PTR B[rcx+rax]
vmovss  DWORD PTR C[rcx+rax], xmm1
AVX版本
gcc-O2-mavx

__m256 vec256;
for(i=0;i<N;i++){   
    for(j=0;j<M;j+=8){
        vec256 = _mm256_add_ps( _mm256_load_ps(&A[i+1][j]) ,  _mm256_load_ps(&B[i+1][j]));
        _mm256_store_ps(&C[i+1][j], vec256);
            }
        }
在标量程序中,
-mavx
msse4.2
的加速比为2.7x。我知道
avx
有效地改进了ISA,这可能是因为这些改进。但是当我在intrinsics中为
AVX
SSE
实现程序时,加速比是3倍。问题是:当我对AVX标量进行矢量化时,它比SSE快2.7倍,速度是3倍(这个问题的矩阵大小是128x128)。在标量模式下使用AVX和SSE时有意义吗?速度提升2.7倍。但矢量化方法必须更好,因为我在AVX中处理八个元素,而在SSE中处理四个元素。当
perf stat
报告时,所有程序的缓存未命中率都低于4.5%

使用
gcc-O2
linux-mint
skylake


更新:简而言之,标量AVX比标量SSE快2.7倍,但矢量化时,AVX-256只比SSE-128快3倍。我想这可能是因为管道。在scalar中,我有3个向量ALU,在向量化模式下可能不可用。我可能会把苹果和桔子比较,而不是把苹果和苹果比较,这可能是我无法理解的原因

您观察到的问题已得到解释。在Skylake系统上,如果AVX寄存器的上半部分脏了,则AVX寄存器的上半部分对非vex编码的SSE操作存在虚假依赖关系。在您的情况下,您的glibc 2.23版本中似乎有一个bug。在我使用Ubuntu 16.10和glibc 2.24的Skylake系统上,我没有这个问题。你可以用

__asm__ __volatile__ ( "vzeroupper" : : : ); 
清洁AVX寄存器的上半部分。我不认为您可以使用诸如
\u mm256\u zeropper
之类的内在代码来解决这个问题,因为GCC会说它是SSE代码,而不会识别内在代码。选项
-mvzeropper
也不起作用,因为GCC one再次认为它是SSE代码,并且不会发出
vzeropper
指令

顺便说一句


更新:

。在
printf
clock\u gettime
之后观察到


如果您的目标是将128位操作与256位操作进行比较,可以考虑使用<代码> -MPLAY-AVX128- MAVX(这在AMD上特别有用)。但是,您将比较AVX256与AVX128,而不是AVX256与SSE。AVX128和SSE都使用128位操作,但它们的实现方式不同。如果你做基准测试,你应该提到你使用了哪一个。

回答标题问题(我不能完全解析正文的最后一部分):。当使用AVX瞄准系统时,@MargaretBloom,没有
gcc-O2
I添加到问题中。目标确定是可以的,但我正在比较纯AVX和SSE,而不是AVX-256和AVX-128。@MargaretBloom,矢量化是通过
-ftree loop vectorize启用的,它是通过
-O3
启用的,而不是
-O2
启用的。这甚至可以通过
-O1-ftree循环向量化
@MargaretBloom进行向量化,我同意我不明白这一点。老年退休金计划的声明令人困惑,更新似乎自相矛盾。在这种情况下,我不认为标量SSE或AVX代码会产生显著差异。到目前为止,我无法用GCC 6.2、Ubuntu 16.10和Skylake复制OPs结果。我想可能是运算。很抱歉,贝拉博,但我刚刚意识到一个解决方案是只使用AVX编译,而不担心非vex编码。你不能在你的系统上测试SSE-only代码,因为你没有一个只有SSE的系统。如果要比较128位和256位操作,可以尝试使用
-mprefere-avx128
。使用
\uuuuasm\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu是指它会在没有AVX的系统上崩溃。这就是为什么除了asm,GCC不允许您这样做的原因。如果您使用该指令,您还可以使用
-mavx
进行编译。根据ABI,使用AVX的每个函数在完成时都应该执行
vzeropper
。好像这个bug在别的地方。@fuz,你看过我指的第一个链接了吗?清除AVX寄存器的上部时,问题消失。我无法在我的系统上重现该问题,因此无法对其进行测试。OP表示,这个问题并没有通过
\uuuasm\uuuuuuuuuuuuuuuuuuuuuuuuuuvolatile\uuuuuuuuuuuuvzeropper::)
就在
main
之后,这正是我所期望的,但当它在
clock\u gettime
之后使用时,它就消失了。在我的回答中,我没有提到这一点,因为我唯一可以肯定的是,问题是上半部很脏。我们能就此达成一致吗?阅读你链接的帖子的最后几行,它说的基本上和我说的一样:一定有人使用了AVX指令,但之后没有执行
vzeropper
。@fuz,在该链接中,bug出现在
\u dl\u runtime\u resolve\u AVX(),/lib64/ld-linux-x86-64中。所以。2
__asm__ __volatile__ ( "vzeroupper" : : : );