我已经实现了标量矩阵加法内核。
#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 msse4.2
:最佳时间:对于128X128矩阵,在406490次重复中为0.000024秒
movss xmm1, DWORD PTR A[rcx+rax]
addss xmm1, DWORD PTR B[rcx+rax]
movss DWORD PTR C[rcx+rax], xmm1
gcc -O2 -mavx
:最佳时间:对于128X128矩阵,在1000001次重复中为0.000009秒
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);
}
}
SSE版本gcc -O2 -sse4.2
::
__m128 vec128;
for(i=0;i<N;i++){
for(j=0;j<M;j+=4){
vec128= _mm_add_ps( _mm_load_ps(&A[i][j]) , _mm_load_ps(&B[i][j]));
_mm_store_ps(&C[i][j], vec128);
}
}
在标量程序中,-mavx
超过msse4.2
的加速比为2.7x。我知道avx
有效地改进了ISA,可能是因为这些改进。但是当我在AVX
和SSE
的内在函数中实现该程序时,加速是3倍。问题是:当我向其矢量化时,AVX标量比SSE快2.7倍,速度提高3倍(此问题的矩阵大小为128x128)。它是否有意义在标量模式下使用AVX和SSE时,速度提高2.7倍。但是矢量化方法必须更好,因为我在AVX中处理8个元素而不是SSE中的4个元素。报告perf stat
时,所有程序的缓存未命中率都不到4.5%。
使用gcc -O2
,linux mint
,skylake
更新:简而言之,Scalar-AVX比Scalar-SSE快2.7倍,但AVX-256在向量化时仅比SSE-128快3倍。我认为这可能是因为流水线操作。在标量中,我有3 vec-ALU
在矢量化模式下可能无法使用。我可能会比较苹果和橘子而不是苹果到苹果,这可能是我无法理解原因的一点。
答案 0 :(得分:3)
您正在观察的问题已解释为here。在Skylake系统上,如果AVX寄存器的上半部分是脏的,则AVX寄存器上半部分的非vex编码SSE操作存在错误依赖性。在您的情况下,您的glibc 2.23版本中似乎存在错误。在我的Skylake系统上使用Ubuntu 16.10和glibc 2.24我没有问题。你可以使用
__asm__ __volatile__ ( "vzeroupper" : : : );
清洁AVX寄存器的上半部分。我不认为你可以使用_mm256_zeroupper
这样的内在函数来解决这个问题,因为GCC会说它是SSE代码而不是识别内在函数。选项-mvzeroupper
也不起作用,因为GCC one再次认为它是SSE代码并且不会发出vzeroupper
指令。
BTW,it's Microsoft's fault that the hardware has this problem。
更新
Other people are apparently encountering this problem on Skylake。在printf
,memset
和clock_gettime
之后观察到了它。
如果您的目标是将128位操作与256位操作进行比较,可以考虑使用-mprefer-avx128 -mavx
(这在AMD上特别有用)。但是你会比较AVX256和AVX128而不是AVX256和SSE。 AVX128和SSE都使用128位操作,但它们的实现是不同的。如果你的基准测试,你应该提到你使用的那个。