霓虹灯和手臂组件优化

时间:2015-08-01 22:32:16

标签: assembly arm neon

我在霓虹灯和手臂组装中实现了计算机视觉卷积算法,其中每个像素被九个自身和相邻像素的和积所代替。主循环看起来像这样:

.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指令吗?

2 个答案:

答案 0 :(得分:5)

尝试交错加载代码和数学代码。你想要一些加载/使用延迟(瞄准几个周期),但交错的负载和数学通常更好。

一次加载Q寄存器以获取16个值可能有助于而不是D寄存器,如果你可以使用寄存器空间来适应它。根据上面的流加载/使用有帮助(你可以经常使用一个物理在一次迭代中注册两个值。)

使用预加载。

答案 1 :(得分:2)

如果可能的话,每行加载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之前预取。