我正在使用 SSE 内在函数为 Intel x86 Nehalem 微架构优化一些代码。
我的程序的一部分计算 4 个点积,并将每个结果添加到数组的连续 block 中的先前值。更具体地说,
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);
请注意,我将使用 4 个临时 xmm 寄存器来保存每个点积的结果。在每个 xmm 寄存器中,结果被放入相对于其他临时 xmm 寄存器的唯一 32 位,因此最终结果如下所示:
tmp0= R0-零-零-零
tmp1= 零-R1-零-零
tmp2= 零-零-R2-零
tmp3= 零-零-零-R3
我将每个 tmp 变量中包含的值合并到一个 xmm 变量中,方法是按照以下说明对它们求和:
tmp0 = _mm_add_ps(tmp0, tmp1);
tmp0 = _mm_add_ps(tmp0, tmp2);
tmp0 = _mm_add_ps(tmp0, tmp3);
最后,我将包含所有 4 个点积结果的寄存器添加到数组的连续部分,以便数组的索引按点积递增,就像这样(C_0n 是数组中当前的 4 个值待更新;C_2是指向这4个值的地址):
tmp0 = _mm_add_ps(tmp0, C_0n);
_mm_storeu_ps(C_2, tmp0);
我想知道是否有更简单、更有效的方法来获取点积的结果并将它们添加到数组的连续 block 中。这样,我在只有 1 个非零值的寄存器之间进行了 3 次加法运算。似乎应该有更有效的方法来解决这个问题。
感谢所有帮助。谢谢。
最佳答案
对于这样的代码,我喜欢存储 A 和 B 的“转置”,以便将 {A_0m.x, A_1m.x, A_2m.x, A_3m.x} 存储在一个 vector 中,等等。然后您可以仅使用乘法和加法来计算点积,完成后,您可以在一个 vector 中获得所有 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);
这样做可能稍微好一点:
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);
因为前两个 mm_add_ps
现在是完全独立的了。另外,我不知道添加与改组的相对时间,但这可能会稍微快一些。
希望对您有所帮助。
关于c - 使用 SSE 内在函数将 4 个点积存储到 C 中的连续数组中的最有效方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4171241/