5

elma并且elmc都是unsigned long数组。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];                     
    }     
}  

在 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];   
    }  
}  

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

4

4 回答 4

7

免责声明:我来自 PowerPC 背景,所以我在这里所说的可能完全是胡说八道。但是由于您尝试立即访问结果,因此您正在拖延矢量管道。

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

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

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

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

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

于 2010-12-09T04:58:54.860 回答
5

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

(注意:您可能不应该展开循环,特别是如果您使用的是 Core 2 或更新版本。)

于 2010-12-09T10:08:57.137 回答
4

您的代码看起来 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);
   }     
}  

请注意,我只使用了 4 个寄存器。您可以手动展开以使用 x86 中的所有 8 个寄存器或 x86_64 中的更多寄存器

于 2010-12-09T06:00:23.470 回答
2

我也不是 SIMD 专家,但看起来您也可以从预取数据中受益,再加上提到的 derolling EboMike。如果您将 res1 和 res2 合并到一个对齐的数组中(结构取决于其他使用它的结构),也可能会有所帮助,那么您不需要额外的复制,您可以直接对其进行操作。

于 2010-12-09T05:13:40.920 回答