Arm-neon优化版SAD 16 * 4没有给出预期的增益

时间:2014-09-30 06:14:28

标签: arm simd neon

我写了一个16 * 4 SAD功能及其arm-neon优化版本。 arm-neon版本采用内联汇编编写。 我的问题是我只获得了2倍的优化(启用了O3),理想情况下我应该至少获得6倍的优化。 任何人都可以解释正在发生的事情的内部结构吗?

static unsigned int f_sad_16x4 ( const unsigned char* a, const unsigned char* b, const unsigned int uiStrideOrg, const unsigned int uiStrideCur )
{
  unsigned int sad = 0;
  for (int i = 0; i < 4; i++) 
  {
      for (int j = 0; j < 16; j++)
      {
          sad += abs(static_cast<int>(a[i*uiStrideOrg+j]) - static_cast<int>(b[i*uiStrideCur+j]));
      }
  }
  return sad;
}

static unsigned int f_sad_16x4_neon(const unsigned char* a, const unsigned char* b, const unsigned int uiStrideOrg, const unsigned int uiStrideCur )
{
    unsigned short auiSum[8];
    unsigned short* puiSum = auiSum;

    __asm__ volatile(                             \

    /* Load 4 rows of piOrg and piCur each */
    "vld1.8 {q0},[%[piOrg]],%[iStrideOrg]   \n\t"\
    "vld1.8 {q4},[%[piCur]],%[iStrideCur]   \n\t"\
    "vld1.8 {q1},[%[piOrg]],%[iStrideOrg]   \n\t"\
    "vabd.u8 q8,  q0, q4                    \n\t"\
    "vld1.8 {q5},[%[piCur]],%[iStrideCur]   \n\t"\
    "vld1.8 {q2},[%[piOrg]],%[iStrideOrg]   \n\t"\
    "vabd.u8 q9,  q1, q5                    \n\t"\
    "vld1.8 {q6},[%[piCur]],%[iStrideCur]   \n\t"\
    "vld1.8 {q3},[%[piOrg]],%[iStrideOrg]   \n\t"\
    "vabd.u8 q10, q2, q6                    \n\t"\
    "vld1.8 {q7},[%[piCur]],%[iStrideCur]   \n\t"\
    "vpaddl.u8 q12, q8                      \n\t"\
    "vabd.u8 q11, q3, q7                    \n\t"\
    "vpaddl.u8 q13, q9                      \n\t"\
    "vpaddl.u8 q14, q10                     \n\t"\
    "vadd.u16 q8, q12, q13                  \n\t"\
    "vpaddl.u8 q15, q11                     \n\t"\
    "vadd.u16 q9, q14, q15                  \n\t"\
    "vadd.u16 q0, q8, q9                    \n\t"\
    "vst1.16 {q0}, [%[puiSum]]              \n\t"\
    :[piOrg]        "+r"    (a),
     [piCur]        "+r"    (b),
     [puiSum]       "+r"    (puiSum)
    :[iStrideCur]   "r"     (uiStrideCur),
     [iStrideOrg]   "r"     (uiStrideOrg)
    :"q0","q1","q2","q3","q4","q5","q6","q7","q8","q9","q10","q11","q12","q13","q14","q15"
    );

    unsigned int uiSum += auiSum[0] + auiSum[1] + auiSum[2] + auiSum[3] + auiSum[4] + auiSum[5] + auiSum[6] + auiSum[7];


    return uiSum;
}

1 个答案:

答案 0 :(得分:1)

此代码执行效果不佳,因为除了内联汇编程序块中的20条NEON指令外,编译器还必须发出23条整数指令。

要解决的最简单的部分是这一行:

unsigned int uiSum += auiSum[0] + auiSum[1] + auiSum[2] + auiSum[3] + auiSum[4] + auiSum[5] + auiSum[6] + auiSum[7];

可以在NEON单元上执行该最终缩小步骤。例如

VADDL.S16   q0, d0, d1     // 32 bit lanes in q0
VPADDL.S32  q0, q0         // 64 bit lanes in q0
VADD.I64    d0, d0, d1     // one 64 bit result in d0

然后,您可以通过一次移动来检索结果:

VMOV %n, %Hn, d0           // retrieve 64 bit result

在上面,你需要设置n以对应内联asm输出块中结果变量的相应操作数。

另一个问题是寄存器分配不是最理想的。寄存器d8到d15(q4到q7)必须由使用它们的任何函数保留,因此编译器会发出代码来执行该操作。您可以重写函数以重用寄存器并避免使用这些寄存器。

此功能将受益于NEON内在函数的使用。这样可以避免担心寄存器分配,也可以使代码可以移植到Aarch64