C语言代码优化示例(二)

一. 起源

在整理系统平台的代码时候,看到了一些原来同事编写的代码,有感于这些代码的编写方法和效率问题,挑出一段有代表意义的代码和大家分享如何改进代码的编码效率和提高代码的执行速率,同时降低代码对内存的消耗.

二. 原始代码(下面简称代码A)

void InintExchangeRateSet(void)

{

double ExchangeRateTab[] =   

       {

           1.0,       // 美元

              6.924,    //人民币

              30.36,    //新台币

              106.1,  //日元

              7.807,    //港币

              0.5108, //英镑

              1.039,  //瑞法郎

              1.151,    //澳元

              1.3657,  //新元

              0.6415,  //欧元

              1.018    //其它

       };

       SetSystemResouce((INT8U *)ExchangeRateTab, SYS_CVS_ECR, 0, YS_CVS_ECR_LEN);

}  

三. 优化代码(下面简称代码B)

static const double ExchangeRateTab[] =   

       {

           1.0,       // 美元

              6.924,    //人民币

              30.36,    //新台币

              106.1,  //日元

              7.807,    //港币

              0.5108, //英镑

              1.039,  //瑞法郎

              1.151,    //澳元

              1.3657,  //新元

              0.6415,  //欧元

              1.018    //其它

       };

void InintExchangeRateSet(void)

{

       SetSystemResouce((INT8U *)ExchangeRateTab, SYS_CVS_ECR, 0, YS_CVS_ECR_LEN);

}  

四. 代码解析

编译器使用的是mips-linux-gcc 3.3.1版本,使用O2优化.

上面的代码非常简单,而且A和B实现的是相同的功能 (满足前提条件是SetSystemResouce的第一个参数是一个常量指针,实际情况也是如此).而且对代码并没有做什么改动,只是修改了一个数组的定义方法,来达到优化的目的.

double ExchangeRateTab修改为static const double ExchangeRateTab

可能大家对这种优化感到迷茫和不解: 这不是一样的吗?有什么变化?

产生这种想法的根本原因是对编译器的行为不了解导致的,我们对比分析一下编译产生的两段代码就能立即明白这其中的道理:

代码A编译后的汇编代码:

8001e348 <InintExchangeRateSet>:

8001e348:     27bdff90      addiu      sp,sp,-112            //耗费更多的堆栈空间

8001e34c:     3c028027    lui    v0,0x8027

8001e350:     24423618    addiu      v0,v0,13848         //数组的访问地址:0x80273618

8001e354:     afbf0068      sw   ra,104(sp)

8001e358:     27a30010    addiu      v1,sp,16   //这部分代码是一个循环体,作用

8001e35c:     24440050    addiu      a0,v0,80   //就是拷贝ROM区域的数组值到

8001e360:     8c450000    lw    a1,0(v0)       //到堆栈空间.

8001e364:     8c460004    lw    a2,4(v0)  //编译器在编译代码A的时候,因为数组是

8001e368:     8c470008    lw    a3,8(v0) //定义在堆栈上的(使用局部变量定义), 编

8001e36c:     8c48000c     lw    t0,12(v0)  //译器认为调用者是有可能修改这个数组

8001e370:     ac650000     sw   a1,0(v1)     //的,而且数组包含有初值, 所以在后面的

8001e374:     ac660004     sw   a2,4(v1)  // ROM区域有一个常量数据保存区.由于

8001e378:     ac670008     sw   a3,8(v1)  //这个原因,它必须老老实实的使用

8001e37c: ac68000c     sw       t0,12(v1) //代码把ROM区域的初值拷贝到堆栈,以便

8001e380:     24420010    addiu      v0,v0,16 //应对后续代码可能对数组的修改

8001e384:     1444fff6       bne  v0,a0,8001e360 <InintExchangeRateSet+0x18>

8001e388:     24630010    addiu      v1,v1,16

8001e38c:     27a40010    addiu      a0,sp,16        //参数1

8001e390:     24050cee     li      a1,3310    //参数2

8001e394:     00003021    move      a2,zero          //参数3

8001e398:     8c490000    lw    t1,0(v0)

8001e39c:     8c4a0004     lw    t2,4(v0)

8001e3a0:     ac690000     sw   t1,0(v1)

8001e3a4:     ac6a0004     sw   t2,4(v1)

8001e3a8:     0c00a26a     jal    800289a8 <SetSystemResouce>

8001e3ac:     24070058    li      a3,88     //参数4

8001e3b0:     8fbf0068      lw    ra,104(sp)

8001e3b4:     03e00008    jr     ra

8001e3b8:     27bd0070    addiu      sp,sp,112

代码A访问的数组资源:       //常量数组的ROM保存地址(不包括黑体部分)

80273610:    00020063 00000000 00000000 3ff00000     c..............?

80273620:    0e560419 401bb22d f5c28f5c 403e5c28     ..V.-..@/...(/>@

80273630:    66666666 405a8666 353f7cee 401f3a5e     fffff.Z@.|?5^:.@

80273640:    3dd97f63 3fe05879 76c8b439 3ff09fbe     c..=yX.?9..v...?

80273650:    f9db22d1 3ff26a7e 3e425aee 3ff5d9e8     ."..~j.?.ZB>...?

80273660:    020c49ba 3fe4872b 5e353f7d 3ff049ba     .I..+..?}?5^.I.?

80273670:    00000001 00000002 00000002 00000004     ................

80273680:    00000001 00000002 00000003 00000004     ................

80273690:    00000006 00000008 0000000c 00000010     ................

802736a0:     00000018 00000020 00000000 00000000     .... ...........

代码B编译后的汇编代码:

8001e348 <InintExchangeRateSet>:

8001e348:     27bdffe8      addiu      sp,sp,-24

8001e34c:     3c048027    lui    a0,0x8027

8001e350:     248435c8    addiu      a0,a0,13768  //数组的访问地址: 0x802735c8,

//直接传递给函数的第一个参数a0

8001e354:     24050cee     li      a1,3310        //参数2

8001e358:     00003021    move      a2,zero   //参数3

8001e35c:     afbf0010      sw   ra,16(sp)      

8001e360:     0c00a256     jal    80028958 <SetSystemResouce>

8001e364:     24070058    li      a3,88            //参数4

8001e368:     8fbf0010      lw    ra,16(sp)

8001e36c:     03e00008    jr     ra

8001e370:     27bd0018    addiu      sp,sp,24

代码A访问的数组资源:

802735c8 <ExchangeRateTab>:       //常量数组的ROM保存地址

802735c8:     00000000 3ff00000 0e560419 401bb22d     .......?..V.-..@

802735d8:    f5c28f5c 403e5c28 66666666 405a8666     /...(/>@fffff.Z@

802735e8:     353f7cee 401f3a5e 3dd97f63 3fe05879     .|?5^:.@c..=yX.?

802735f8:     76c8b439 3ff09fbe f9db22d1 3ff26a7e     9..v...?."..~j.?

80273608:    3e425aee 3ff5d9e8 020c49ba 3fe4872b     .ZB>...?.I..+..?

80273618:    5e353f7d 3ff049ba 00000001 00000002     }?5^.I.?........

80273628:    00000002 00000004 00000001 00000002     ................

80273638:    00000003 00000004 00000006 00000008     ................

80273648:    0000000c 00000010 00000018 00000020     ............ ...

对比两段代码的编译结果,很明显的代码B的代码量大大缩减(由29条指令缩减到11条指令),没有一条多余的代码,同时提高了代码的执行速度(少了资源拷贝的循环体:见代码A的注释).而对于两种实现方法,保存常量的空间并没有发生任何变化.

五. 结束语

优化永无至境,但是一开始采用好的编码方法将获得更高的效率,降低不必要的工作和性能开销.

在此抛砖引玉,希望有更多的人在编写代码的时候获得一些灵感和方法,写出更加高效和精简的代码.

有一句话共勉: 好的设计不是无法再添加代码,而是再也没有办法减少代码

原创文章,欢迎转载,请注明来源,未经书面允许,请勿用于商业用途。

发布了13 篇原创文章 · 获赞 7 · 访问量 5411

猜你喜欢

转载自blog.csdn.net/huangbinvip/article/details/104717093
今日推荐