使用AVX intrinsics而不是SSE不会提高速度 – 为什么?
我一直在使用英特尔的SSE内部函数,并获得了很好的性能提升。 因此,我期望AVX内部函数能够进一步加速我的程序。 不幸的是,直到现在,情况并非如此。 也许我犯了一个愚蠢的错误,所以如果有人能帮助我,我将不胜感激。
我使用Ubuntu 11.10与g ++ 4.6.1。 我编译我的程序(见下文)
g++ simpleExample.cpp -O3 -march=native -o simpleExample
testing系统有一个Intel i7-2600 CPU。
这里是代表我的问题的代码。 在我的系统上,我得到了输出
98.715 ms, b[42] = 0.900038 // Naive 24.457 ms, b[42] = 0.900038 // SSE 24.646 ms, b[42] = 0.900038 // AVX
请注意,计算sqrt(sqrt(sqrt(x)))只是为了确保内存带宽不限制执行速度; 这只是一个例子。
simpleExample.cpp:
#include <immintrin.h> #include <iostream> #include <math.h> #include <sys/time.h> using namespace std; // ----------------------------------------------------------------------------- // This function returns the current time, expressed as seconds since the Epoch // ----------------------------------------------------------------------------- double getCurrentTime(){ struct timeval curr; struct timezone tz; gettimeofday(&curr, &tz); double tmp = static_cast<double>(curr.tv_sec) * static_cast<double>(1000000) + static_cast<double>(curr.tv_usec); return tmp*1e-6; } // ----------------------------------------------------------------------------- // Main routine // ----------------------------------------------------------------------------- int main() { srand48(0); // seed PRNG double e,s; // timestamp variables float *a, *b; // data pointers float *pA,*pB; // work pointer __m128 rA,rB; // variables for SSE __m256 rA_AVX, rB_AVX; // variables for AVX // define vector size const int vector_size = 10000000; // allocate memory a = (float*) _mm_malloc (vector_size*sizeof(float),32); b = (float*) _mm_malloc (vector_size*sizeof(float),32); // initialize vectors // for(int i=0;i<vector_size;i++) { a[i]=fabs(drand48()); b[i]=0.0f; } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Naive implementation // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ s = getCurrentTime(); for (int i=0; i<vector_size; i++){ b[i] = sqrtf(sqrtf(sqrtf(a[i]))); } e = getCurrentTime(); cout << (es)*1000 << " ms" << ", b[42] = " << b[42] << endl; // ----------------------------------------------------------------------------- for(int i=0;i<vector_size;i++) { b[i]=0.0f; } // ----------------------------------------------------------------------------- // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // SSE2 implementation // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pA = a; pB = b; s = getCurrentTime(); for (int i=0; i<vector_size; i+=4){ rA = _mm_load_ps(pA); rB = _mm_sqrt_ps(_mm_sqrt_ps(_mm_sqrt_ps(rA))); _mm_store_ps(pB,rB); pA += 4; pB += 4; } e = getCurrentTime(); cout << (es)*1000 << " ms" << ", b[42] = " << b[42] << endl; // ----------------------------------------------------------------------------- for(int i=0;i<vector_size;i++) { b[i]=0.0f; } // ----------------------------------------------------------------------------- // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // AVX implementation // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pA = a; pB = b; s = getCurrentTime(); for (int i=0; i<vector_size; i+=8){ rA_AVX = _mm256_load_ps(pA); rB_AVX = _mm256_sqrt_ps(_mm256_sqrt_ps(_mm256_sqrt_ps(rA_AVX))); _mm256_store_ps(pB,rB_AVX); pA += 8; pB += 8; } e = getCurrentTime(); cout << (es)*1000 << " ms" << ", b[42] = " << b[42] << endl; _mm_free(a); _mm_free(b); return 0; }
任何帮助表示赞赏!
这是因为在Sandy Bridge处理器上, VSQRTPS
(AVX指令)的占用次数是SQRTPS
(SSE指令)的两倍。 请参阅Agner Fog的优化指南: 指导表 ,第88页。
像平方根和除法这样的指令不会从AVX中受益。 另一方面,增加,乘法等。
如果您对增加平方根性能感兴趣,则可以使用VRSQRTPS和Newton-Raphson公式来代替VSQRTPS:
x0 = vrsqrtps(a) x1 = 0.5 * x0 * (3 - (a * x0) * x0)
VRSQRTPS本身并不能从AVX中获益,但是其他的计算方法也是如此。
使用它,如果23位的精度足够你。
只是为了完整。 牛顿 – 拉夫逊(NR)的实施像分部或平方根的操作只会有利于你的代码中有限的这些操作。 这是因为如果您使用这些替代方法,您将在其他端口(例如乘法和附加端口)上产生更多的压力。 这就是为什么x86架构有特殊硬件单元来处理这些操作,而不是替代软件解决scheme(如NR)的原因。 我引用了Intel 64和IA-32体系结构优化参考手册 p.556:
“在某些情况下,当分裂或平方根操作是隐藏这些操作的一些延迟的更大algorithm的一部分时,牛顿 – 拉夫逊(Newton-Raphson)的近似可以减缓执行速度。
所以在大algorithm中使用NR时要小心。 实际上,我已经拿出了我的硕士论文,一旦发表,我会在这里留下一个链接,供将来参考。
另外对于人们来说,总是对某些指令的吞吐量和延迟有所疑问,请看看IACA 。 这是英特尔提供的一个非常有用的工具,可以静态分析代码的内核执行性能。
这里编辑的是论文的链接,对那些有兴趣的论文者来说
根据处理器硬件的不同,AVX指令可能会在SSE指令的硬件中模拟。 您需要查看您的处理器的部件号以获得确切的规格,但是这是低端和高端intel处理器之间的主要区别之一,专用执行单元的数量与硬件仿真的数量之间的差异。