霓虹灯和手臂组装优化

Posted

技术标签:

【中文标题】霓虹灯和手臂组装优化【英文标题】:Neon and arm assembly optimization 【发布时间】:2015-08-01 22:32:16 【问题描述】:

我在 Neon 和 arm 组件中实现了一个计算机视觉 卷积 算法,其中每个像素都替换为九个自身和相邻像素的和积。主循环如下所示:

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

【问题讨论】:

如果您以特定 CPU 为目标,需要调查的一件事是预取。手动插入预取指令可以加快速度,但仅限于给定的缓存大小和代码配置。如果您的程序可移植到不同的 Cortex 模型,请忘记手动预取,除非您想为每个模型维护有条件编译的代码。 我的目标是一个特定的 ARM,又名。 OMAP4 双皮质-A9。 【参考方案1】:

尝试交错加载代码和数学代码。您想要一些加载/使用延迟(针对几个周期),但交错加载和数学通常效果更好。

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

使用预加载。

【讨论】:

【参考方案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 之前预取。

【讨论】:

以上是关于霓虹灯和手臂组装优化的主要内容,如果未能解决你的问题,请参考以下文章

手臂霓虹灯转置 4x4 uint32

组装armv7霓虹灯电源功能

ARM中乘法和存储的霓虹灯优化

隔行扫描 YUYV 到灰度的霓虹灯优化

霓虹灯代码没有优化

使用 ARM 霓虹灯