使用ARM SIMD指令优化掩码功能

时间:2014-05-13 14:03:35

标签: mask simd neon

我想知道你是否可以帮助我使用NEON内在函数来优化这个掩码功能。我已经尝试使用O3 gcc编译器标志来使用自动矢量化,但是该函数的性能比使用O2运行它要小,这会关闭自动矢量化。由于某种原因,使用O3生成的汇编代码比使用O2的汇编代码长1.5。

  void mask(unsigned int x, unsigned int y, uint32_t *s, uint32_t *m)
{                             
  unsigned int ixy;
  ixy = xsize * ysize;
  while (ixy--)                 
    *(s++) &= *(m++);
}

可能我必须使用以下命令:

vld1q_u32 //从s和m

加载4个整数

vandq_u32 //执行s和m的4个整数之间的逻辑和

vst1q_u32 //将它们存储回s

然而,我不知道如何以最佳方式做到这一点。例如,我应该在加载,存储和存储后将s,m增加4?我对NEON很新,所以我真的需要一些帮助。

我正在使用gcc 4.8.1,我使用以下cmd进行编译:

arm-linux-gnueabihf-gcc -mthumb -march = armv7-a -mtune = cortex-a9 -mcpu = cortex-a9 -mfloat-abi = hard -mfpu = neon -O3 -fprefetch-loop-数组name.c -o name

提前致谢

2 个答案:

答案 0 :(得分:2)

我可能会这样做。我已经包含了4x循环展开。预加载缓存总是一个好主意,可以将速度提高25%。由于没有太多的处理(它主要花费时间加载和存储),最好加载大量寄存器,然后处理它们,因为它给了数据实际加载的时间。它假设数据是16个元素的偶数倍。

void fmask(unsigned int x, unsigned int y, uint32_t *s, uint32_t *m)
{                             
  unsigned int ixy;
  uint32x4_t srcA,srcB,srcC,srcD;
  uint32x4_t maskA,maskB,maskC,maskD;

  ixy = xsize * ysize;
  ixy /= 16; // process 16 at a time
  while (ixy--)
  {
    __builtin_prefetch(&s[64]); // preload the cache
    __builtin_prefetch(&m[64]);
    srcA = vld1q_u32(&s[0]);
    maskA = vld1q_u32(&m[0]);
    srcB = vld1q_u32(&s[4]);
    maskB = vld1q_u32(&m[4]);
    srcC = vld1q_u32(&s[8]);
    maskC = vld1q_u32(&m[8]);
    srcD = vld1q_u32(&s[12]);
    maskD = vld1q_u32(&m[12]);
    srcA = vandq_u32(srcA, maskA); 
    srcB = vandq_u32(srcB, maskB); 
    srcC = vandq_u32(srcC, maskC); 
    srcD = vandq_u32(srcD, maskD);
    vst1q_u32(&s[0], srcA);
    vst1q_u32(&s[4], srcB);
    vst1q_u32(&s[8], srcC);
    vst1q_u32(&s[12], srcD);
    s += 16;
    m += 16;
  }
}

答案 1 :(得分:0)

我将从最简单的一个开始,并将其作为与未来例程进行比较的参考。

一个好的经验法则是尽快计算所需的东西,而不是在需要时。 这意味着指令可以执行X个循环,但结果并不总是立即就绪,因此调度很重要

例如,您案例的简单调度架构将是(伪代码)

nn=n/4  // Assuming n is a multiple of 4

LOADI_S(0)  // Load and immediately after increment pointer
LOADI_M(0)  // Load and immediately after increment pointer
for( k=1; k<nn;k++){
   AND_SM(k-1)    // Inner op
   LOADI_S(k)     // Load and increment after
   LOADI_M(k)     // Load and increment after
   STORE_S(k-1)  // Store and increment after
}
AND_SM(nn-1)
STORE_S(nn-1)     // Store. Not needed to increment

从内循环中省略这些指令,我们实现内部的操作不依赖于前一个操作的结果。 可以进一步扩展此模式,以便从等待前一个操作结果的时间中获利。

此外,由于内在函数仍然依赖于优化器,请参阅编译器在不同优化选项下执行的操作。我更喜欢使用内联汇编,这对于小程序来说并不困难,并且可以为您提供更多控制。