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

时间:2012-10-23 11:27:11

标签: simd altivec

我正在使用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指令都会带来性能损失!

2 个答案:

答案 0 :(得分:2)

理想情况下,您应该将数组填充为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
}

答案 1 :(得分:0)

有时,零填充不是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);