霓虹灯和手臂装配优化

Neon and arm assembly optimization

我在 Neon 和 arm assembly 中实施计算机视觉 卷积 算法,其中每个像素由九个自身像素和相邻像素的和积替换。主循环如下所示:

.loop:
    vld1.u8 {d0}, [line_prev]
    add line_prev, line_prev, #1
    vld1.u8 {d1}, [line_prev]
    add line_prev, line_prev, #1
    vld1.u8 {d2}, [line_prev]
    add line_prev, line_prev, #6

    vld1.u8 {d3}, [line]
    add line, line, #1
    vld1.u8 {d4}, [line]
    add line, line, #1
    vld1.u8 {d5}, [line]
    add line, line, #6

    vld1.u8 {d6}, [line_next]
    add line_next, line_next, #1
    vld1.u8 {d7}, [line_next]
    add line_next, line_next, #1
    vld1.u8 {d8}, [line_next]
    add line_next, line_next, #6

//Everything is loaded now. Let's multiply and sum

    vmull.u8 q10, d0, d10       //d10 to d18 holds the kernel matrix values
    vmlal.u8 q10, d1, d11
    vmlal.u8 q10, d2, d12
    vmlal.u8 q10, d3, d13
    vmlal.u8 q10, d4, d14
    vmlal.u8 q10, d5, d15
    vmlal.u8 q10, d6, d16
    vmlal.u8 q10, d7, d17
    vmlal.u8 q10, d8, d18
    vshrn.u16 d4, q10, d19      //Shift the sum by the value in d19

    vst1.u8 {d4}, [out]!        //Store result

    subs temp, temp, #8     //We have processed 8 pixels
    bgt .loop

如何优化(在速度方面)这个循环?有什么更聪明的方法来加载像素。此外,q11... 可用:我应该使用它们并行执行 vmull 和 mlal 指令吗?

尝试交错加载代码和数学代码。你想要一些 load/use 延迟(以几个周期为目标),但交错加载和数学通常效果更好。

加载 Q 寄存器以一次获取 16 个值可能会有所帮助,而不是 D 寄存器,如果您可以使用寄存器 space 来适应它。按照上述流式传输 load/use有帮助(您通常可以在一次迭代中将一个物理寄存器用于两个值)。

使用预加载。

如果可能的话,每行加载相同的数据 3 次几乎肯定是值得避免的。如果数据大小不合适,则通常难以避免未对齐的加载,但 will cost an extra cycle 值得牢记。

我尝试的一种方法是在一行中保留两个寄存器的数据,并使用 vext 从这对中提取偏移数据,从而节省两次负载和一些每次迭代的周期以一个额外的寄存器为代价,例如:

    vld1.u8 {d0}, [line]!  ; 'initial' chunk
    ...
.loop:
    vld1.u8 {d3}, [line]!  ; 'next' chunk
    ...
    vext.8  d1, d0, d3, #1
    vext.8  d2, d0, d3, #2

    ; do stuff with d0=line, d1=line+1, d2=line+2
    ...
    vmov    d0, d3  ; 'next' chunk becomes 'current' chunk for the next iteration
    ...
    bgt .loop

正如其他人所提到的,如果您乐于针对特定的微体系结构,那么仔细调整的手动预加载对于较旧的内核可能非常有益。在我的脑海中,我认为 Cortex-A9 的最佳位置往往是前面的 2 个缓存行。请注意,如果图像与 L1 缓存相比足够小,前面的行保持热,则可能只需要在 line_next 之前预取。