使用 SIMD 指令避免无效的内存加载

Posted

技术标签:

【中文标题】使用 SIMD 指令避免无效的内存加载【英文标题】:Avoiding invalid memory load with SIMD instructions 【发布时间】:2012-10-23 11:27:11 【问题描述】:

我正在使用 SIMD 加载指令从内存中加载元素,比如说使用 Altivec,假设地址对齐:

float X[SIZE];
vector float V0;
unsigned FLOAT_VEC_SIZE = sizeof(vector float);
for (int load_index =0; load_index < SIZE; load_index+=FLOAT_VEC_SIZE)

    V0 = vec_ld(load_index, X);
    /* some computation involving V0*/

现在如果 SIZE 不是 FLOAT_VEC_SIZE 的倍数,V0 可能在最后一次循环迭代中包含一些无效的内存元素。避免这种情况的一种方法是通过一次迭代减少循环,另一种方法是屏蔽潜在的无效元素,这里还有其他有用的技巧吗?考虑到上述内容是一组嵌套循环中的最内层。因此,任何额外的非 SIMD 指令都会带来性能损失!

【问题讨论】:

【参考方案1】:

理想情况下,您应该将数组填充到 vec_step(vector float) 的倍数(即 4 个元素的倍数),然后从 SIMD 处理中屏蔽掉任何其他不需要的值,或者使用标量代码来处理最后几个元素,例如

const INT VF_ELEMS = vec_step(vector float);
const int VEC_SIZE = (SIZE + VF_ELEMS - 1) / VF_ELEMS; // number of vectors in X, rounded up
vector float VX[VEC_SIZE];   // padded array with 16 byte alignment
float *X = = (float *)VX;    // float * pointer to base of array

for (int i = 0; i <= SIZE - VF_ELEMS; i += VF_ELEMS)
                            // for each full SIMD vector
    V0 = vec_ld(0, &X[i]);
    /* some computation involving V0 */

if (i < SIZE)                // if we have a partial vector at the end

#if 1                        // either use SIMD and mask out the unwanted values
    V0 = vec_ld(0, &X[i]);
    /* some SIMD computation involving partial V0 */
#else                        // or use a scalar loop for the remaining 1..3 elements
    /* small scalar loop to handle remaining points */
#endif

【讨论】:

感谢您的留言,但我认为对齐不是问题。假设 X 中有 7 个元素,因此 vec_ld(0,X) 将带来前四个“浮点数”,而 vec_ld(4,X) 将返回 3 个有效元素,而不能保证第四个元素,不是吗? 抱歉 - 我错过了关于 SIZE 不是 FLOAT_VEC_SIZE 倍数的部分 - 我会尽快更新我的答案。【参考方案2】:

有时零填充不是一个选项,就像 const 数组一样。另一方面,添加标量代码会导致向量和标量结果相互混合,例如在写回计算结果时;屏蔽不需要的值似乎是一个更好的解决方案。请注意,这假定地址为 16 字节对齐。 玩具示例,清除 SIMD 向量的最后三个元素

vector bool int V_MASK = (vector bool int) 0,0,0,0;
unsigned int all_ones = 0xFFFFFFFFFFFFFFFF;
unsigned int * ptr_mask = (unsigned int *) &V_MASK;
ptr_mask[0]= all_ones;
vector float XV = vec_ld(0,some_float_ptr);
XV = vec_and(XV,V_MASK);

【讨论】:

以上是关于使用 SIMD 指令避免无效的内存加载的主要内容,如果未能解决你的问题,请参考以下文章

内存是矩阵加法(SIMD 指令)的瓶颈吗?

是否有 SIMD 指令来实现批量数组内存索引映射?

将数据从全局加载到共享内存时如何避免银行冲突

使用 SIMD 指令去交错音频通道

如何避免将重复对象加载到主内存中?

C++:使用指针存储/访问大对象以避免内存不足