我写了一个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;
}
答案 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