我正在尝试编写一个asm函数,它将16个样本生成一个缓冲区,该缓冲区从编解码器输出48k。 每个样本将16个正弦波加在一起,每个正弦或部分具有ADSR包络,并具有额外的延迟级(等待)以控制幅度。换句话说,它的添加剂合成。 无论如何,我很生气,因为CooCox中的编译器实际上可以比ASM中的所有汗水更快地完成这项任务。我该怎么做才能优化它并使其更快?涉及许多控制参数,这使得来自全局数组变量的大量负载减慢了速度。这适用于STM32F4 BTW。
@ ARM function definition
@ void get_sine(void)
.align 2 @ Align to word boundary
.global get_sine @ This makes it a real symbol
.syntax unified @ Remember this!
.type get_sine STT_FUNC @ Declare to be a function.
.equ bufsize, 1024
.equ partials, 16
.equ MAX_EG, 524288
.data
count16: .word 0x0
get_sine: @ Start of function definition
push {r4-r12}
ldr r2,=sineLUT @ sine_tab base addy
ldr r9,=atk
ldr r10,=dcy
ldr r11,=sus
ldr r12,=rel
ldr r6,=env_1
ldr r1,=count16
mov r0,#0
str r0,[r1,#0]
outloop:
ldr r7,=ph_inc @ pitch val into r7
ldr r7,[r7,#0] @ get current phase
ldr r3,=phase @ phase address to r3
ldr r1,[r3,#0] @ get current phase
add r1,r1,r7 @ add current phase and ph_inc
str r1,[r3,#0] @ store phase
mov r7,#0 @ set to 1 for r7 to be inner loop counter
mov r5,#0 @ clear sum reg
ldr r8,=flag
ldr r3,=EG_stage
innerloop:
ldr r0,[r3,r7,lsl #2] @get EG_stage r0
cmp r0,#0 @ if zero goto wait
beq waitj
cmp r0,#1 @ if 1 jump attack
beq attackj
cmp r0,#2 @ if 2 jump decay
beq decayj
b releasej @ if 3 jump release
waitj:
ldr r1,=wait_temp @get wait_temp array addr
ldr r0,[r1,r7,lsl #2] @load value to r0
sub r0,r0,#1 @subtract
mov r4,#1 @ load one for next
cmp r0,#0 @compare if gt or equal to zero
ite ge
strge r0,[r1,r7,lsl #2] @ store wait state if >= 0
strlt r4,[r3,r7,lsl #2] @ store EG_stage value if less than
b break
attackj:
ldr r0,[r6,r7,lsl #2] @ get env1 value into r0
ldr r1,[r9,r7,lsl #2] @ get attack value
add r0,r0,r1 @ env_1[par] += atk[par];
mov r4,#2
mov r1,MAX_EG
cmp r0,r1
itte GE @ if (env_1[par] >= MAX)
strge r4,[r3,r7,lsl #2] @ EG_stage[par] = 2, env_1[par] = MAX;
strge r1,[r6,r7,lsl #2]
strlt r0,[r6,r7,lsl #2]
b break
decayj:
ldr r0,[r6,r7,lsl #2] @ get env1 value into r0
ldr r1,[r10,r7,lsl #2] @ decay value
sub r0,r0,r1 @ env_1[par] -= dcy[par];
str r0,[r6,r7,lsl #2] @ update env_1 now in case
ldr r4,[r11,r7,lsl #2] @ get sus value
add r1,r4,r1 @ add decay and sus value for compare
cmp r0,r1 @ if < sus[par]+dcy[par] || env_1[par]<0)
ittt lt
movlt r0,r4 @env_1[par] = (sus[par]);
strlt r4,[r6,r7,lsl #2] @ store to env_1
blt break
cmp r0,#0
itt lt @|| env_1[par]<0)
movlt r0,r4 @env_1[par] = (sus[par]);
strlt r4,[r6,r7,lsl #2] @ store to env_1
b break
releasej:
ldr r0,[r6,r7,lsl #2] @ get env1 value into r0
ldr r1,[r12,r7,lsl #2] @ release value
sub r0,r0,r1 @ env_1[par] -= rel[par];
str r0,[r6,r7,lsl #2] @ update env_1 now in case
mov r1,#0
cmp r0,#0 @
it lt @ if (env_1[par]<0)
strlt r1,[r6,r7,lsl #2] @ env_1[par] = 0;
break:
mov r4,#0
add r4,r7,#1
ldr r1,=phase @ phase address to r3
ldr r1,[r1,#0]
umull r0,r4,r1,r4 @ multiply phase for each partial
lsr r0,r0,#18 @ shift it right by 18 into r0 for sine_tab lookup
ldr r0,[r2,r0,lsl #2] @ lookup sine val with r0 into r1 and sign extend
ldr r4,[r6,r7,lsl #2] @ get envelope value into r4
lsr r4,r4,#4 @ shift it to 16bit range
smulbb r0,r0,r4 @ signed multiply of sine table * envelope for scaling
asr r0,r0,#15 @ asr shift back to 16bit
ldr r4,[r8,r7,lsl #2] @ get flag if withing bandwidth
cmp r4,#0
it ne @ if 1 add it to sum
addne r5,r5,r0
add r7,r7,#1
cmp r7,#16 @ compare loop index with 16 (i=0;i<16;i++)
bne innerloop
asr r0,r5,#5
pkhbt r0,r0,r0,lsl #16 @ pack R+L channel in r0
ldr r5,=writePos @ get writepos addr
ldr r1,[r5,#0] @ get writePos
lsl r3,r1,#2 @ align address 4
ldr r4,=WaveBuffer @ storage array addy
str r0,[r4,r3] @ store sine to WaveBuffer
add r1,r1,#1 @ increment array pointer writepos
mov r3,bufsize @ load BUFFERSIZE compare
cmp r1,r3 @ skip if less than BUFFERSIZE
it hs
movhs r1,#0 @ clr writepos if >=BUFFERSIZE
str r1,[r5,#0] @ store writepos value
ldr r0,=dataSize @ get datasize counter addr
ldr r1,[r0,#0] @ get val
add r1,r1,#1 @ increment datasize counter
str r1,[r0,#0] @ store counter
ldr r1,=count16
ldr r0,[r1,#0]
add r0,r0,#1 @ increment loop counter
str r0,[r1,#0]
cmp r0,#16 @ compare with 16 (i=0;i<16;i++)
bne outloop
pop {r4-r12}
bx lr
.section .rodata
sineLUT:
@ Array goes in here. Type can be .byte, .hword or .word
@ NOTE! No comma at the end of a line! This is important
.word 0x0000,0x000c,0x0018,0x0024,0x0030,0x003c,0x0048,0x0054
.word 0x0064,0x0070,0x007c,0x0088,0x0094,0x00a0,0x00ac,0x00bc
.word 0x00c8,0x00d4,0x00e0,0x00ec,0x00f8,0x0104,0x0114,0x0120
.word 0x012c,0x0138,0x0144,0x0150,0x015c,0x016c,0x0178,0x0184
.word 0x0190,0x019c,0x01a8,0x01b4,0x01c4,0x01d0,0x01dc,0x01e8
.word 0x01f4,0x0200,0x020c,0x021c,0x0228,0x0234,0x0240,0x024c
答案 0 :(得分:3)
了解编译器如何执行此操作的最佳方法是在该编译器中编译代码,然后查看它输出的程序集。它可能会令人困惑,因为它已经过如此优化,但你可能会拿起一些技巧。
一个非常难以理解但可能有所帮助的技巧是实现某种“跳转表” “功能。在内部循环中,不是做3个比较语句,而是将(r0 * X)添加到当前指令指针。在适当的目的地,无条件跳转指令进入你需要去的地方(attack,decayj,releasej)。 X的值将基于保存跳转指令所需的字节数。这只是我的想法,你需要自己测试它的功效。