使用NEON装配进行优化

aka*_*aya 5 optimization opencv arm simd neon

我正在尝试使用NEON优化OpenCV代码的某些部分.这是我工作的原始代码块.(注意:如果它有任何重要性,你可以在"opencvfolder/modules/video/src/lkpyramid.cpp"找到完整的源代码.它是一个对象跟踪算法的实现.)

for( ; x < colsn; x++ )
{
    deriv_type t0 = (deriv_type)(trow0[x+cn] - trow0[x-cn]);
    deriv_type t1 = (deriv_type)((trow1[x+cn] + trow1[x-cn])*3 + trow1[x]*10);
    drow[x*2] = t0; drow[x*2+1] = t1;

}
Run Code Online (Sandbox Code Playgroud)

在此代码中,deriv_type的大小为2个字节.这是我写的NEON汇编.使用原始代码,我测量10-11 fps.有了NEON,情况更糟,我只能得到5-6 fps.我对NEON并不是很了解,可能这段代码中有很多错误.我哪里做错了?谢谢

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


    );
}
Run Code Online (Sandbox Code Playgroud)

编辑

这是内在的verison.它与之前几乎相同.它仍然很慢.

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);
}
Run Code Online (Sandbox Code Playgroud)

Pau*_*l R 1

那里有一些循环不变的东西需要移到 for 循环之外 - 这可能会有所帮助。

您还可以考虑使用全宽 SIMD 运算,这样每次循环迭代可以处理 8 个 ppt,而不是 4 个。

最重要的是,您可能应该使用内在函数而不是原始汇编,以便编译器可以处理窥孔优化、寄存器分配、指令调度、循环展开等。

例如

// constants - init outside loop

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)
{
    int16x8_t t0a = vld1q_s16(&trow0[x + cn]);
    int16x8_t t0b = vld1q_s16(&trow0[x - cn]);
    int16x8_t t0 = vsubq_s16(t0a, t0b); // t0 = (trow0[x + cn] - trow0[x - cn])

    // ...
}
Run Code Online (Sandbox Code Playgroud)