SIMD代码比标量代码运行得慢

anu*_*nup 5 c optimization sse simd sse2

elma并且elmc都是unsigned long数组.所以是res1res2.

unsigned long simdstore[2];  
__m128i *p, simda, simdb, simdc;  
p = (__m128i *) simdstore;  

for (i = 0; i < _polylen; i++)  
{
    u1 = (elma[i] >> l) & 15;  
    u2 = (elmc[i] >> l) & 15;  
    for (k = 0; k < 20; k++)  
    {
        //res1[i + k] ^= _mulpre1[u1][k];  
        //res2[i + k] ^= _mulpre2[u2][k];               

        simda = _mm_set_epi64x (_mulpre2[u2][k], _mulpre1[u1][k]);  
        simdb = _mm_set_epi64x (res2[i + k], res1[i + k]);  
        simdc = _mm_xor_si128 (simda, simdb);  
        _mm_store_si128 (p, simdc);  
        res1[i + k] = simdstore[0];  
        res2[i + k] = simdstore[1];                     
    }     
}  
Run Code Online (Sandbox Code Playgroud)

在for循环中包括元素的XOR的非simd和simd版本.第二个for循环中的前两行执行显式XOR,而其余的执行相同操作的simd版本.

从外部调用此循环数百次,因此优化此循环将有助于缩短总计算时间.

问题是simd代码的运行速度比标量代码慢很多倍.

编辑:完成部分展开

__m128i *p1, *p2, *p3, *p4;  
p1 = (__m128i *) simdstore1;  
p2 = (__m128i *) simdstore2;  
p3 = (__m128i *) simdstore3;  
p4 = (__m128i *) simdstore4;  

for (i = 0; i < 20; i++)  
{
    u1 = (elma[i] >> l) & 15;  
    u2 = (elmc[i] >> l) & 15;  
    for (k = 0; k < 20; k = k + 4)  
    {
        simda1  = _mm_set_epi64x (_mulpre2[u2][k], _mulpre1[u1][k]);  
        simda2  = _mm_set_epi64x (_mulpre2[u2][k + 1], _mulpre1[u1][k + 1]);  
        simda3  = _mm_set_epi64x (_mulpre2[u2][k + 2], _mulpre1[u1][k + 2]);  
        simda4  = _mm_set_epi64x (_mulpre2[u2][k + 3], _mulpre1[u1][k + 3]);  

        simdb1  = _mm_set_epi64x (res2[i + k], res1[i + k]);  
        simdb2  = _mm_set_epi64x (res2[i + k + 1], res1[i + k + 1]);  
        simdb3  = _mm_set_epi64x (res2[i + k + 2], res1[i + k + 2]);  
        simdb4  = _mm_set_epi64x (res2[i + k + 3], res1[i + k + 3]);  

        simdc1  = _mm_xor_si128 (simda1, simdb1);  
        simdc2  = _mm_xor_si128 (simda2, simdb2);  
        simdc3  = _mm_xor_si128 (simda3, simdb3);  
        simdc4  = _mm_xor_si128 (simda4, simdb4);  

        _mm_store_si128 (p1, simdc1);  
        _mm_store_si128 (p2, simdc2);  
        _mm_store_si128 (p3, simdc3);  
        _mm_store_si128 (p4, simdc4);  

        res1[i + k]= simdstore1[0];  
        res2[i + k]= simdstore1[1]; 
        res1[i + k + 1]= simdstore2[0];  
        res2[i + k + 1]= simdstore2[1];   
        res1[i + k + 2]= simdstore3[0];  
        res2[i + k + 2]= simdstore3[1]; 
        res1[i + k + 3]= simdstore4[0];  
        res2[i + k + 3]= simdstore4[1];   
    }  
}  
Run Code Online (Sandbox Code Playgroud)

但是,结果并没有太大变化; 它仍然需要标量代码的两倍.

Ebo*_*ike 6

免责声明:我来自PowerPC背景,所以我在这里说的可能是完全的h .. 但是,由于您尝试立即访问结果,因此停止了矢量管道.

最好将所有内容保存在矢量管道中.只要你从vector到int或float进行任何类型的转换,或者将结果存储到内存中,你就会停滞不前.

处理SSE或VMX时的最佳操作模式是:加载,处理,存储.将数据加载到向量寄存器中,执行所有向量处理,然后将其存储到内存中.

我建议:保留几个__m128i寄存器,多次展开循环,然后存储它.

编辑:另外,如果您展开,如果按16个字节对齐RES1和RES2,你可以直接在内存中存储你的结果,而通过这个simdstore间接,这可能是一个LHS和其他摊位去.

编辑:忘了显而易见.如果您的polylen通常很大,请不要忘记在每次迭代时都执行数据缓存预取.

  • 在PowerPC上循环展开是很好的,但在现代x86 CPU上没有那么好,在那里可以使用更少的寄存器,CPU本身将为小循环进行一些展开. (4认同)
  • 很高兴知道,谢谢!在这种特殊情况下,我认为至少展开一次以便在向量寄存器中构造输出是有意义的(因为你需要连续两个结果并将它们混合在一起) - 我猜这里最大的问题之一是simdstore - > res1/res2间接. (2认同)

Pau*_*l R 5

相对于正在执行的加载和存储数量,您在这里做的计算很少,因此您几乎看不到 SIMD 的好处。在这种情况下,您可能会发现使用标量代码更有用,特别是如果您有一个可以在 64 位模式下使用的 x86-64 CPU。这将减少加载和存储的数量,这些是当前性能的主要因素。

(注:你应该不是。展开循环,特别是如果你使用酷睿2或更高版本)