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

elmaelmc都是unsigned long数组。 res1res1也是如此。

 unsigned long simdstore[2]; __m128i *p, simda, simdb, simdc; p = (__m128i *) simdstore; for (i = 0; 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循环中包括非simd和simd版本的XOR元素。 第二个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 > 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]; } } 

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

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

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

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

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

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

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

代码看起来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_64中使用x86或更多的所有8个寄存器

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

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

我也不是SIMD专家,但看起来你也可以从预取数据中获益,再加上推出的EboMike。 如果你将res1和res2合并为一个对齐的数组(结构取决于其他用途),也可能有帮助,那么你不需要额外的复制,你可以直接在它上面操作。