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

C SIMD代码比标量代码运行得慢,c,optimization,sse,simd,sse2,C,Optimization,Sse,Simd,Sse2,elma和elmc都是无符号长数组。res1和res2也是如此 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;

elma
elmc
都是
无符号长
数组。
res1
res2
也是如此

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];                     
    }     
}  
无符号长simdstore[2];
__m128i*p、simda、simdb、simdc;
p=(uu m128i*)simdstore;
对于(i=0;i<\u polyen;i++)
{
u1=(elma[i]>>l)和15;
u2=(elmc[i]>>l)和15;
对于(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];
}     
}  
for循环中包括非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];   
    }  
}  
m128i*p1、*p2、*p3、*p4;
p1=(uuu m128i*)simdstore1;
p2=(uuu m128i*)simdstore2;
p3=(uuu m128i*)simdstore3;
p4=(uu m128i*)simdstore4;
对于(i=0;i<20;i++)
{
u1=(elma[i]>>l)和15;
u2=(elmc[i]>>l)和15;
对于(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];
}  
}  

但是,结果变化不大;它仍然需要两倍于标量代码的时间。

免责声明:我来自PowerPC背景,所以我在这里说的可能完全是废话。但是,由于您试图立即访问结果,所以您正在暂停向量管道

最好将所有内容都保存在向量管道中。一旦你从vector到int或float进行任何类型的转换,或者将结果存储到内存中,你就会陷入停滞

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

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

编辑:另外,如果展开,并且将res1和res2对齐16个字节,则可以直接将结果存储在内存中,而无需通过simdstore间接寻址,这可能是LHS和另一个暂停


编辑:忘记了显而易见的。如果您的Polyen通常较大,请不要忘记在每次迭代时进行数据缓存预取。

我也不是SIMD专家,但看起来您还可以从预取数据以及前面提到的减卷操作中获益。如果您将res1和res2合并到一个对齐的数组中(结构的类型取决于其他使用它的对象),那么您不需要额外的复制,您可以直接对其进行操作。

代码的外观res1和res2似乎是完全独立的向量。 然而,您将它们混合在同一个寄存器中以进行异或运算

我会使用不同的寄存器,比如下面的 向量必须全部对齐)

\uuuum128ix0,x1,x2,x3;
对于(i=0;i<\u polyen;i++)
{  
u1=(elma[i]>>l)和15;
u2=(elmc[i]>>l)和15;
对于(k=0;k<20;k+=2)
{     
//res1[i+k]^=_mulpre1[u1][k];
x0=毫米荷载(si128)(&U mulpre1[u1][k]);
x1=_mm_load_si128(&res1[i+k]);
x0=_-mm_-xor_-si128(x0,x1);
_mm_store_si128(&res1[i+k],x0);
//res2[i+k]^=_mulpre2[u2][k];
x2=最小荷载为128(&u mulpre2[u2][k]);
x3=_mm_load_si128(&res2[i+k]);
x2=_-mm\u-xor\u-si128(x2,x3);
_mm_-store_-si128(&res2[i+k],x2);
}     
}  

请注意,我只使用4个寄存器。在x86_64中,您可以手动展开以使用x86或更多版本中的所有8个寄存器。相对于正在执行的加载和存储的数量,您在这里所做的计算非常少,因此您几乎看不到SIMD带来的好处。在这种情况下,您可能会发现使用标量代码更有用,特别是如果您有一个可以在64位模式下使用的x86-64 CPU。这将减少加载和存储的数量,这是当前性能的主要因素


(注意:您可能应该而不是展开循环,尤其是在使用Core 2或更新版本时。)

对齐res1/res2,旋转寄存器,然后直接写入。摆脱simdstore.res1/res2
__m128i x0, x1, x2, x3;  
for (i = 0; i < _polylen; i++)  
{  

    u1 = (elma[i] >> l) & 15;  
    u2 = (elmc[i] >> l) & 15;  
    for (k = 0; k < 20; k+=2)  
    {     
        //res1[i + k] ^= _mulpre1[u1][k];
        x0= _mm_load_si128(&_mulpre1[u1][k]);
        x1= _mm_load_si128(&res1[i + k]);
        x0= _mm_xor_si128 (x0, x1);
        _mm_store_si128 (&res1[i + k], x0);
        //res2[i + k] ^= _mulpre2[u2][k];               
        x2= _mm_load_si128(&_mulpre2[u2][k]);
        x3= _mm_load_si128(&res2[i + k]);
        x2= _mm_xor_si128 (x2, x3);
        _mm_store_si128 (&res2[i + k], x2);
   }     
}