如何优化Cortex-M3的滤波器环路?

时间:2019-04-17 18:58:42

标签: performance loops assembly arm thumb

我只需要更改代码即可执行相同的基本功能,但还要对其进行优化,基本上,我认为过滤器循环是可以更改的主要代码,因为我觉得里面的指令太多了,但是不知道从哪里开始。我正在使用Cortex M3和Thumb2。

我试图篡改过滤器循环,以便可以将寄存器中存储的先前数字相加并将其除以8,但我不知道该如何真正执行。

; Perform in-place filtering of data supplied in memory
; the filter to be applied is a non-recursive filter of the form
; y[0] = x[-2]/8 + x[-1]/8 + x[0]/4 + x[1]/8 + x[2]/8

  ; set up the exception addresses
  THUMB
  AREA RESET, CODE, READONLY
  EXPORT __Vectors
  EXPORT Reset_Handler
__Vectors 
  DCD 0x00180000     ; top of the stack 
  DCD Reset_Handler  ; reset vector - where the program starts

num_words EQU (end_source-source)/4  ; number of input values
filter_length EQU 5  ; number of filter taps (values)

  AREA 2a_Code, CODE, READONLY
Reset_Handler
  ENTRY
  ; set up the filter parameters
  LDR r0,=source        ; point to the start of the area of memory holding inputs
  MOV r1,#num_words     ; get the number of input values
  MOV r2,#filter_length ; get the number of filter taps
  LDR r3,=dest          ; point to the start of the area of memory holding outputs

  ; find out how many times the filter needs to be applied
  SUBS r4,r1,r2   ; find the number of applications of the filter needed, less 1
  BMI exit        ; give up if there is insufficient data for any filtering

  ; apply the filter  
filter_loop
  LDMIA r0,{r5-r9}     ; get the next 5 data values to be filtered
  ADD r5,r5,r9         ; sum x[-2] with x[2]
  ADD r6,r6,r8         ; sum x[-1] with x[1]
  ADD r9,r5,r6         ; sum x[-2]+x[2] with x[-1]+x[1]
  ADD r7,r7,r9,LSR #1  ; sum x[0] with (x[-2]+x[2]+x[-1]+x[1])/2
  MOV r7,r7,LSR #2     ; form (x[0] + (x[-2]+x[-1]+x[1]+x[2])/2)/4
  STR r7,[r3],#4       ; save calculated filtered value, move to next output data item
  ADD r0,r0,#4         ; move to start of next 5 input data values
  SUBS r4,r4,#1        ; move on to next set of 5 inputs 
  BPL filter_loop      ; continue until last set of 5 inputs reached

  ; execute an endless loop once done 
exit    
  B exit

  AREA 2a_ROData, DATA, READONLY
source  ; some saw tooth data to filter - should blunt the sharp edges
  DCD 0,10,20,30,40,50,60,70,80,90,100,0,10,20,30,40,50,60,70,80,90,100
  DCD 0,10,20,30,40,50,60,70,80,90,100,0,10,20,30,40,50,60,70,80,90,100
  DCD 0,10,20,30,40,50,60,70,80,90,100,0,10,20,30,40,50,60,70,80,90,100
  DCD 0,10,20,30,40,50,60,70,80,90,100,0,10,20,30,40,50,60,70,80,90,100
end_source

  AREA 2a_RWData, DATA, READWRITE
dest  ; copy to this area of memory
  SPACE end_source-source
end_dest
  END
  END

我希望有一种更有效的代码运行方式,只要它能做同样的事情,它就可以减少代码的整体大小或加快循环的执行时间。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:2)

对于代码大小,请尝试仅使用可用于16位短编码的寄存器r0..r7。

此外,带有标志设置的指令版本通常在非标志设置版本需要32位时具有16位编码。例如

  • adds r0, #4是16位和32位add r0, #4
  • movs r7,r7,LSR #2是16位vs. 32位MOV r7,r7,LSR #2
  • movs r2,#filter_length是16位和32位MOV r2,#filter_length。 (#88这样的非微小立即数仍需要32位Thumb2 mov
  • stmia r3!, {r5}(具有回写功能)是16位,而32位str r7, [r3], #4具有后递增功能。

请参阅我对先前问题How do I reduce execution time and number of cycles for a factorial loop? And/or code-size?的回答的“拇指代码大小”部分。查看代码的反汇编并查找32位指令,并检查为什么它们是32位的,并寻找一种使它们成为16位的方法。这只是您始终可以做的超基本Thumb优化。


r1r2甚至不在循环内使用,并且r4 = r1-r2是一个汇编时常量,您将在运行时使用3条指令进行计算...因此,与movs r4, #num_words - filter_length相比显然很疯狂。

如果假定这些是在汇编时对于您的真实代码未知的输入(也许在不同的输入上有时使用相同的功能?),则在计算循环计数器后重用“无效”的寄存器。在r0和r3中接受指针有点笨拙,因此如果您将r2用作循环计数器,或者将r4-r7和{如果您使用r1,则{1}}免费。

我选择使用r1-r2作为循环计数器。这是我的版本(r5-r7)的反汇编内容

r4

Cortex-M3没有NEON,但是输出之间存在数据重用。通过展开,我们绝对可以重用加载结果以及某些“内部” r1结果。也许可以通过滑动窗口来减去不再占总数的单词并添加新单词。

但是中间元素为“特殊”,我们在两侧都有两个2元素窗口,除非顶部有足够的备用位来将arm-none-eabi-gcc -g -c -mthumb -mcpu=cortex-m3 arm-filter.S && arm-none-eabi-objdump -drwC arm-filter.o加两次,然后右移3 然后,您甚至不需要展开,只需加载1个元素/调整滑动窗口并重新计算中间/存储1个元素。

(此答案的第一个版本是基于对代码的误解。我稍后可能会通过速度优化进行更新,但现在进行编辑以删除错误的内容。)