被忽略的C编译器递归内存优化

    先看看下面一段代码,这段代码作为本主题的解说对象,但没有任何实际作用。


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编译器,你可能会庆幸与自己无关。但是我可以大胆的猜测,没准你使用的编译器跟我的一样无能!

    希望在用户的推动下,编译器厂商能尽快推出更加“聪明”的产品!

 

 


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值