使用SSE内在函数将4个点产品存储到C中的连续数组中的最有效方法

Sam*_*Sam 11 c sse simd intrinsics dot-product

我正在使用SSE内在函数为英特尔x86 Nehalem微架构优化一些代码.

我的程序的一部分计算4个点产品,并将每个结果添加到阵列的连续块中的先前值.进一步来说,

tmp0 = _mm_dp_ps(A_0m, B_0m, 0xF1);
tmp1 = _mm_dp_ps(A_1m, B_0m, 0xF2);
tmp2 = _mm_dp_ps(A_2m, B_0m, 0xF4);
tmp3 = _mm_dp_ps(A_3m, B_0m, 0xF8);

tmp0 = _mm_add_ps(tmp0, tmp1);
tmp0 = _mm_add_ps(tmp0, tmp2);
tmp0 = _mm_add_ps(tmp0, tmp3);
tmp0 = _mm_add_ps(tmp0, C_0n);

_mm_storeu_ps(C_2, tmp0);
Run Code Online (Sandbox Code Playgroud)

请注意,我通过使用4个临时xmm寄存器来保存每个点积的结果.在每个xmm寄存器中,结果被放入相对于其他临时xmm寄存器的唯一32位,这样最终结果如下所示:

tmp0 = R0-零 - 零

tmp1 =零-R1-零

tmp2 =零 - 零 - 零

tmp3 =零 - 零 - R3

我将每个tmp变量中包含的值组合成一个xmm变量,并将它们与以下说明相加:

tmp0 = _mm_add_ps(tmp0, tmp1);
tmp0 = _mm_add_ps(tmp0, tmp2);
tmp0 = _mm_add_ps(tmp0, tmp3);
Run Code Online (Sandbox Code Playgroud)

最后,我将包含点积的所有4个结果的寄存器添加到数组的连续部分,以便数组的索引以点积递增,就像这样(C_0n是数组中当前要更新的4个值) ; C_2是指向这4个值的地址):

tmp0 = _mm_add_ps(tmp0, C_0n);
_mm_storeu_ps(C_2, tmp0);
Run Code Online (Sandbox Code Playgroud)

我想知道是否有一种更少的,更有效的方法来获取点积的结果并将它们添加到阵列的连续块中.通过这种方式,我在寄存器之间进行了3次添加,其中只有1个非零值.似乎应该有一个更有效的方法来解决这个问题.

我感谢所有的帮助.谢谢.

cel*_*ion 6

对于这样的代码,我想存储的A和B的"转",使{A_0m.x,A_1m.x,A_2m.x,A_3m.x}存储在一个载体,等等,那么你可以做只用点积相乘,并增加了,而当你做,你把所有4个的点产品于一体的载体,没有任何洗牌.

这是用来在频繁光线跟踪,以测试4条射线一次对一个平面(例如遍历kd树时).如果你没有在输入数据的控制,但是,做转置的开销可能不值得.该代码也将在SSE4之前的机器上运行,尽管这可能不是问题.


关于现有代码的小效率说明:而不是这个

tmp0 = _mm_add_ps(tmp0, tmp1);
tmp0 = _mm_add_ps(tmp0, tmp2);
tmp0 = _mm_add_ps(tmp0, tmp3);
tmp0 = _mm_add_ps(tmp0, C_0n);
Run Code Online (Sandbox Code Playgroud)

这样做可能稍微好一些:

tmp0 = _mm_add_ps(tmp0, tmp1);  // 0 + 1 -> 0
tmp2 = _mm_add_ps(tmp2, tmp3);  // 2 + 3 -> 2
tmp0 = _mm_add_ps(tmp0, tmp2);  // 0 + 2 -> 0
tmp0 = _mm_add_ps(tmp0, C_0n);
Run Code Online (Sandbox Code Playgroud)

因为前两个mm_add_ps人现在是完全独立的.另外,我不知道将与洗牌的相对时间,但可能是稍快.


希望有所帮助.