Performance with SSE is the same
我对以下循环进行了矢量化处理,该循环出现在正在开发的应用程序中:
1 2 3 4 5 6 7 8 | void vecScl(Node** A, Node* B, long val){ int fact = round( dot / const); for(i=0; i<SIZE ;i++) (*A)->vector[i] -= fact * B->vector[i]; } |
这是SSE代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | void vecSclSSE(Node** A, Node* B, long val){ int fact = round( dot / const); __m128i vecPi, vecQi, vecCi, vecQCi, vecResi; int sseBound = SIZE/4; for(i=0,j=0; j<sseBound ; i+=4,j++){ vecPi = _mm_loadu_si128((__m128i *)&((*A)->vector)[i] ); vecQi = _mm_set_epi32(fact,fact,fact,fact); vecCi = _mm_loadu_si128((__m128i *)&((B)->vector)[i] ); vecQCi = _mm_mullo_epi32(vecQi,vecCi); vecResi = _mm_sub_epi32(vecPi,vecQCi); _mm_storeu_si128((__m128i *) (((*A)->vector) + i), vecResi ); } //Compute remaining positions if SIZE % 4 != 0 for(; i<SIZE ;i++) (*A)->vector[i] -= q * B->vector[i]; } |
尽管这在正确性方面可行,但无论有没有SSE,其性能都完全相同。 我正在用以下代码编译代码:
1 | g++ *.cpp *.h -msse4.1 -march=corei7-avx -mtune=corei7-avx -mno-avx -mno-aes -Warray-bounds -O2 |
这是因为我没有分配(并相应地使用SSE函数)对齐的内存吗? 代码更改非常复杂,因此我现在避免了。
顺便说一句,就进一步的改进而言,考虑到我受Sandy Bridge体系结构的束缚,我能做的最好的事情是什么?
编辑:编译器尚未向量化代码呢。 首先,我将向量的数据类型更改为
非常感谢
如果您的数据很大,那么您可能只是受内存限制,因为每个加载/存储只执行很少的ALU操作。
但是,您可以尝试一些小改进:
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 | inline void vecSclSSE(Node** A, Node* B, long val){ // make function inline, for cases where `val` is small const int fact = (dot + const / 2 - 1) / const; // use integer arithmetic here if possible const __m128i vecQi = _mm_set1_epi32(fact); // hoist constant initialisation out of loop int32_t * const pA = (*A)->vector; // hoist invariant de-references out of loop int32_t * const pB = B->vector; __m128i vecPi, vecCi, vecQCi, vecResi; for(int i = 0; i < SIZE - 3; i += 4) { // use one loop variable vecPi = _mm_loadu_si128((__m128i *)&(pA[i])); vecCi = _mm_loadu_si128((__m128i *)&(pB[i])); vecQCi = _mm_mullo_epi32(vecQi,vecCi); vecResi = _mm_sub_epi32(vecPi,vecQCi); _mm_storeu_si128((__m128i *)&(pA[i]), vecResi); } //Compute remaining positions if SIZE % 4 != 0 for(; i<SIZE ;i++) pA[i] -= q * pB[i]; } |
正如Paul所说,您每次数据访问的计算量很少,并且您的代码可能受IO限制。由于未对齐的存储/加载要比未对齐的存储/加载慢,因此您确实应该对齐数据。
您应该将SSE(也是一条缓存行)与16个字节对齐,将AVX与(我认为)对齐32个字节。如果您自己分配数据,则使用
然后您可以切换到对齐的加载/存储说明。
另外,我不确定
但是在进行所有这些操作之前,请确保这值得您花费时间和维护负担。您可以使用
我想说的是,对于相同的SIZE,我能够向量化一个内核,该内核恰好出现在第一篇文章中。这次,我有了很大的提速(我不会说这个因素,因为除非我量化整个应用程序在内核上花费的时间,否则它是无关紧要的)。内核计算两个向量的点积,即:
1 2 | for(i=0;i<SIZE;i++) dot += A->vector[i] * B->vector[i]; |
由此我可以得出结论,SIZE很小不是问题。反过来,这表明我在第一个内核中可能做错了。有人可以为第一个内核建议不同的SSE操作集吗?我认为值得尝试。下一步是分配对齐的内存,但是如前所述,这在Sandy Bridge和其他新架构中并不关键。
这再次证明编译器没有对代码进行向量化。
谢谢