Optimization 利用霓虹灯组件进行优化
我正在尝试使用NEON优化OpenCV代码的某些部分。这是我处理的原始代码块。(注意:如果它很重要,您可以在“opencvfolder/modules/video/src/lkpyramid.cpp”找到完整的源代码。它是一个对象跟踪算法的实现。) (;xOptimization 利用霓虹灯组件进行优化,optimization,opencv,arm,simd,neon,Optimization,Opencv,Arm,Simd,Neon,我正在尝试使用NEON优化OpenCV代码的某些部分。这是我处理的原始代码块。(注意:如果它很重要,您可以在“opencvfolder/modules/video/src/lkpyramid.cpp”找到完整的源代码。它是一个对象跟踪算法的实现。) (;x
{
derivu类型t0=(derivu类型)(trow0[x+cn]-trow0[x-cn]);
deriv_型t1=(deriv_型)((trow1[x+cn]+trow1[x-cn])*3+trow1[x]*10);
卓尔[x*2]=t0;卓尔[x*2+1]=t1;
}
在这段代码中,deriv_类型的大小是2字节。
这是我写的霓虹灯装配图。使用原始代码,我测量10-11 fps。使用霓虹灯更糟糕,我只能获得5-6 fps。我对霓虹灯了解不多,可能代码中有很多错误。我哪里做错了?谢谢
for( ; x < colsn; x+=4 )
{
__asm__ __volatile__(
"vld1.16 d2, [%2] \n\t" // d2 = trow0[x+cn]
"vld1.16 d3, [%3] \n\t" // d3 = trow0[x-cn]
"vsub.i16 d9, d2, d3 \n\t" // d9 = d2 - d3
"vld1.16 d4, [%4] \n\t" // d4 = trow1[x+cn]
"vld1.16 d5, [%5] \n\t" // d5 = trow1[x-cn]
"vld1.16 d6, [%6] \n\t" // d6 = trow1[x]
"vmov.i16 d7, #3 \n\t" // d7 = 3
"vmov.i16 d8, #10 \n\t" // d8 = 10
"vadd.i16 d4, d4, d5 \n\t" // d4 = d4 + d5
"vmul.i16 d10, d4, d7 \n\t" // d10 = d4 * d7
"vmla.i16 d10, d6, d8 \n\t" // d10 = d10 + d6 * d8
"vst2.16 {d9,d10}, [%0] \n\t" // drow[x*2] = d9; drow[x*2+1] = d10;
//"vst1.16 d4, [%1] \n\t"
: //output
:"r"(drow+x*2), "r"(drow+x*2+1), "r"(trow0+x+cn), "r"(trow0+x-cn), "r"(trow1+x+cn), "r"(trow1+x-cn), "r"(trow1) //input
:"d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "d10" //registers
);
}
(;x
{
__asm\uuuuuuuuuuuuuuuuuu挥发性__(
“vld1.16 d2,[%2]\n\t”//d2=trow0[x+cn]
“vld1.16 d3,[%3]\n\t”//d3=trow0[x-cn]
“vsub.i16 d9,d2,d3\n\t”//d9=d2-d3
“vld1.16 d4,[%4]\n\t”//d4=trow1[x+cn]
“vld1.16 d5,[%5]\n\t”//d5=trow1[x-cn]
“vld1.16 d6,[%6]\n\t”//d6=trow1[x]
“vmov.i16 d7,#3\n\t”//d7=3
“vmov.i16 d8,#10\n\t”//d8=10
“vadd.i16 d4,d4,d5\n\t”//d4=d4+d5
“vmul.i16 d10,d4,d7\n\t”//d10=d4*d7
“vmla.i16 d10、d6、d8\n\t”//d10=d10+d6*d8
“vst2.16{d9,d10},[%0]\n\t”//drow[x*2]=d9;drow[x*2+1]=d10;
//vst1.16 d4,[%1]\n\t
://输出
:“r”(卓尔+x*2),“r”(卓尔+x*2+1),“r”(卓尔+x+cn),“r”(卓尔+x-cn),“r”(卓尔+x-cn),“r”(卓尔+x-cn),“r”(卓尔+x-cn),“r”(卓尔+x-cn)//输入
:“d2”、“d3”、“d4”、“d5”、“d6”、“d7”、“d8”、“d9”、“d10”//寄存器
);
}
编辑
这是verison与intrinsics。和以前几乎一样。它仍然工作缓慢
const int16x8_t vk3 = { 3, 3, 3, 3, 3, 3, 3, 3 };
const int16x8_t vk10 = { 10, 10, 10, 10, 10, 10, 10, 10 };
for( ; x < colsn; x+=8 )
{
int16x8x2_t loaded;
int16x8_t t0a = vld1q_s16(&trow0[x + cn]);
int16x8_t t0b = vld1q_s16(&trow0[x - cn]);
loaded.val[0] = vsubq_s16(t0a, t0b); // t0 = (trow0[x + cn] - trow0[x - cn])
loaded.val[1] = vld1q_s16(&trow1[x + cn]);
int16x8_t t1b = vld1q_s16(&trow1[x - cn]);
int16x8_t t1c = vld1q_s16(&trow1[x]);
loaded.val[1] = vaddq_s16(loaded.val[1], t1b);
loaded.val[1] = vmulq_s16(loaded.val[1], vk3);
loaded.val[1] = vmlaq_s16(loaded.val[1], t1c, vk10);
}
const int16x8_t vk3={3,3,3,3,3};
常量int16x8_t vk10={10,10,10,10,10,10};
对于(;x
有一些循环不变的东西需要移到for循环之外-这可能有点帮助
您也可以考虑使用全宽度SIMD操作,这样您就可以处理8个循环每一次迭代,而不是4次。 但最重要的是,您可能应该使用内部函数而不是原始asm,这样编译器就可以处理窥视孔优化、寄存器分配、指令调度、循环展开等
例如//常量-init外部循环
常量int16x8_t vk3={3,3,3,3,3,3};
常量int16x8_t vk10={10,10,10,10,10,10};
对于(;x
由于数据危害,您正在创建大量管道暂停。例如,以下三个说明:
"vadd.i16 d4, d4, d5 \n\t" // d4 = d4 + d5
"vmul.i16 d10, d4, d7 \n\t" // d10 = d4 * d7
"vmla.i16 d10, d6, d8 \n\t" // d10 = d10 + d6 * d8
它们每个只发出一条指令,但它们之间有几个循环暂停,因为结果尚未就绪()
尝试将循环展开几次,并将它们的指令交叉排列。如果使用intrinsic,编译器可能会为您执行此操作。在指令调度等方面击败编译器并非不可能,但这相当困难,而且通常不值得(这可能属于不过早优化)
编辑
您的内在代码是合理的,我怀疑编译器做得不是很好。看看它正在生成的汇编代码(objdump-d
),您可能会看到它也在创建许多管道危险。更高版本的编译器可能会有所帮助,但如果没有,您可能必须自己修改循环以隐藏结果的延迟(您将需要指令计时)。保留当前代码,因为它是正确的,并且应该由一个聪明的编译器进行优化
您可能会得到以下结果:
// do step 1 of first iteration
// ...
for (int i = 0; i < n - 1; i++) {
// do step 1 of (i+1)th
// do step 2 of (i)th
// with their instructions interleaved
// ...
}
// do step 2 of (n-1)th
// ...
//执行第一次迭代的步骤1
// ...
对于(int i=0;i
您还可以将循环拆分为两个以上的步骤,或将循环展开几次(例如,将
i++
更改为i+=2
,将循环体翻倍,在下半部分将i
更改为i+1
)。我希望这个答案有帮助,让我知道如果有什么不清楚 谢谢,我会尝试一下,但我已经读到gcc对内部函数的处理非常糟糕,这就是我选择汇编的原因。你认为gcc会有很大帮助吗?我建议你先让它使用内部函数——这肯定会比原始的标量代码快,而且可能已经足够好了。但是如果不是这样的话,你可以用asm来进一步调整循环的某些部分,这样你就可以两全其美了。我让它与内在的工作,但我仍然得到一个低fps。可能还有别的事情我做错了。无论如何,谢谢。您是否按照建议切换到全宽度SIMD?我想我会在最后去掉中间加载的变量,只在这里进行两次存储-很难说编译器将如何处理它。还有一件事:你正在使用gcc-O3,不是吗?它会
"vadd.i16 d4, d4, d5 \n\t" // d4 = d4 + d5
"vmul.i16 d10, d4, d7 \n\t" // d10 = d4 * d7
"vmla.i16 d10, d6, d8 \n\t" // d10 = d10 + d6 * d8
// do step 1 of first iteration
// ...
for (int i = 0; i < n - 1; i++) {
// do step 1 of (i+1)th
// do step 2 of (i)th
// with their instructions interleaved
// ...
}
// do step 2 of (n-1)th
// ...