以下循环执行数百次
elma and elmc are both unsigned long (64-bit) arrays, so is res1 and 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++)
{
1. //res1[i + k] ^= _mulpre1[u1][k];
2. //res2[i + k] ^= _mulpre2[u2][k];
3. _mm_prefetch ((const void *) &_mulpre2[u2][k], _MM_HINT_T0);
4. _mm_prefetch ((const void *) &_mulpre1[u1][k], _MM_HINT_T0);
5. simda = _mm_set_epi64x (_mulpre2[u2][k], _mulpre1[u1][k]);
6. _mm_prefetch ((const void *) &res2[i + k], _MM_HINT_T0);
7. _mm_prefetch ((const void *) &res1[i + k], _MM_HINT_T0);
8. simdb = _mm_set_epi64x (res2[i + k], res1[i + k]);
9. simdc = _mm_xor_si128 (simda, simdb);
10. _mm_store_si128 (p, simdc);
11. res1[i + k] = simdstore[0];
12. res2[i + k] = simdstore[1];
}
}
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++)
{
1. //res1[i + k] ^= _mulpre1[u1][k];
2. //res2[i + k] ^= _mulpre2[u2][k];
3. _mm_prefetch ((const void *) &_mulpre2[u2][k], _MM_HINT_T0);
4. _mm_prefetch ((const void *) &_mulpre1[u1][k], _MM_HINT_T0);
5. simda = _mm_set_epi64x (_mulpre2[u2][k], _mulpre1[u1][k]);
6. _mm_prefetch ((const void *) &res2[i + k], _MM_HINT_T0);
7. _mm_prefetch ((const void *) &res1[i + k], _MM_HINT_T0);
8. simdb = _mm_set_epi64x (res2[i + k], res1[i + k]);
9. simdc = _mm_xor_si128 (simda, simdb);
10. _mm_store_si128 (p, simdc);
11. res1[i + k] = simdstore[0];
12. res2[i + k] = simdstore[1];
}
}
在for循环中,标量版本的代码(注释)运行速度比simd代码快两倍。下面提到了上述行的cachegrind输出(指令读取)。
第1行:668,460,000 2 2
第2行:668,460,000 1 1
3号线:89,985,000 1 1
第4行:89,985,000 1 1
5号线:617,040,000 2 2
第6行:44,992,500 0 0
7号线:44,992,500 0 0
8号线:539,910,000 1 1
第9行:128,550,000 0 0
第10行:。 。 。
11号线:205,680,000 0 0
第12行:205,680,000 0 0
从上图中可以看出,评论(标量代码)所需的指令数量明显少于simd代码。
如何更快地制作此代码?
答案 0 :(得分:3)
取出_mm_prefetch
内在函数 - 他们在这种背景下没有取得任何成果,甚至可能会损害性能。如果(a)您有足够的带宽并且(b)您可以在实际需要数据之前几百个时钟周期发出预取提示,则预取仅有好处。我认为(a)和(b)都不属于你的情况。
答案 1 :(得分:1)
你的性能问题是:
_mm_set_epi64x(_mulpre2 [u2] [k],_ mulpre1 [u1] [k]);
mm_set(a,b,c,d)类内在函数非常慢。 只有单个参数集内在函数(也就是广播)很快。
我看了他们在汇编代码中做了什么。
它们基本上在堆栈上创建一个数组,使用正常的内存移动(mov DWORD)将两个整数从它们当前所在的多维数组移动到堆栈数组。然后从堆栈数组中使用XMM内存移动(mov XMWORD)。
标量版本直接从内存转到寄存器。更快!
您看到开销来自于XMM寄存器一次只能与128位进行通信这一事实,因此您的程序在加载之前首先在另一个内存区域中对128位进行排序。
如果有办法将64位值直接移入或移出普通寄存器到XMM寄存器,我仍然在寻找它。
为了提高使用SSE / XMM寄存器的速度,您的数据可能需要在内存中按顺序排列。如果每次无序加载可以执行多个XMM操作,则将无序数据加载到XMM寄存器中是值得的。在这里你做一个XOR操作。