先看看下面一段代码,这段代码作为本主题的解说对象,但没有任何实际作用。
void recursion_test(int n)
{
if(n > 0){
recursion_test(n - 1);
}else{
char buf[256];
sprintf(buf, "%d\n", n);
}
}
经过ARM编译器编译之后的汇编代码是:
SECTION `.text`:CODE:NOROOT(1)
THUMB
recursion_test:
PUSH {LR}
SUB SP,SP,#+260
CMP R0,#+1
BLT.N ??recursion_test_0
SUBS R0,R0,#+1
BL recursion_test
ADD SP,SP,#+260
POP {PC}
??recursion_test_0:
MOV R2,R0
ADR.N R1,??DataTable12 ;; "%d\n"
ADD R0,SP,#+0
BL sprintf
ADD SP,SP,#+260
POP {PC} ;; return
如果你传递n参数为10调用recursion_test函数,该函数递归深度为10。从汇编代码可以看到本次递归将占用264*10=2640字节的堆栈内存!
但是在C语言上,我们希望它仅占用4*10+260=300字节堆栈内存。因为只有在最后一级递归中的else分支,才需要使用buf[]缓冲区啊!这就是目前所有ARM编译器的最应该优化的方面!
在编译器厂商对其产品优化之前,我们勉强可以采取一个方法来减少递归堆栈内存的占用,就是启用编译器C99标准的VLA特性!然后将代码改为如下:
void recursion_test(int n)
{
if(n > 0){
recursion_test(n - 1);
}else{
char buf[n + 256];
sprintf(buf, "%d\n", n);
}
}
ARM编译器编译后的汇编代码是:
SECTION `.text`:CODE:NOROOT(1)
THUMB
recursion_test:
PUSH {R3-R5,LR}
MOV R4,R0
CMP R4,#+1
IT GE
MOVGE R4,#+0
ADD R0,R4,#+256
BL __iar_vla_alloc2
MOV R5,R0
MOV R2,R4
ADR.N R1,??DataTable12 ;; "%d\n"
BL sprintf
MOV R0,R5
POP {R1,R4,R5,LR}
B.W __iar_vla_dealloc2
这时,从汇编代码可以知道,递归深度为10的recursion_test函数占用堆栈内存为:16*10+256=416字节。节省了巨大的堆栈内存耗费,而且递归深度越深,节省的越多!规模效应么。
当你看到我使用的是IAR的ARM编译器,你可能会庆幸与自己无关。但是我可以大胆的猜测,没准你使用的编译器跟我的一样无能!
希望在用户的推动下,编译器厂商能尽快推出更加“聪明”的产品!