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循环中包含非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 < 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];
}
}
但是,结果并没有太大变化;它仍然需要标量码的两倍。
答案 0 :(得分:6)
免责声明:我来自PowerPC背景,所以我在这里说的可能是完全的h ..但是,当你试图立即访问你的结果时,你就停止了你的矢量管道。
最好将所有内容保存在矢量管道中。只要你从vector到int或float进行任何类型的转换,或者将结果存储到内存中,你就会停滞不前。
处理SSE或VMX时的最佳操作模式是:加载,处理,存储。将数据加载到向量寄存器中,进行所有向量处理,然后将其存储到内存中。
我建议:保留几个__m128i寄存器,多次展开循环,然后存储它。
编辑:此外,如果您展开,并且如果您将res1和res2对齐16个字节,您可以将结果直接存储在内存中,而无需通过此simdstore间接,这可能是LHS和另一个停顿。
编辑:忘了显而易见的。如果您的polylen通常很大,请不要忘记在每次迭代时都进行数据缓存预取。答案 1 :(得分:4)
代码看起来res1和res2的方式似乎是完全独立的向量。 然而,您将它们混合在同一个寄存器中以对它们进行混合。
我会使用不同的寄存器,如下所示(The 向量必须全部对齐)。
__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个寄存器答案 2 :(得分:4)
相对于正在执行的加载和存储的数量,您在这里进行的计算非常少,因此您看不到SIMD带来的好处。在这种情况下,您可能会发现使用标量代码更有用,特别是如果您有一个可以在64位模式下使用的x86-64 CPU。这将减少负载和存储的数量,这是目前性能的主要因素。
(注意:你可能不展开循环,特别是如果你使用的是Core 2或更新版本。)
答案 3 :(得分:2)
我也不是SIMD专家,但看起来你也可以从预取数据中获益,再加上推出的EboMike。如果你将res1和res2合并为一个对齐的数组(结构取决于其他用途),也可能有帮助,那么你不需要额外的复制,你可以直接在它上面操作。