一线研发之声 https://bbs.21ic.com/?567930 [收藏] [复制] [RSS] ------最底层、最深邃、最负重的编程之歌

日志

一线研发之声 之 C51和M3的while语句效率分析

热度 14已有 11802 次阅读2014-11-18 23:08 |个人分类:一线研发之声|系统分类:嵌入式系统| while, 效率分析, 嵌入式 C51 M3

#include "stdint.h"

uint8_t while_inc(uint8_t   ch)

{

    uint8_t i = 0;

    do

    {

         ch ++;

    }while (++i < 8);

    return ch;

}

#include "stdint.h"

uint8_t  while_dec(uint8_t ch)

{

    uint8_t i = 0;/*不产生汇编代码*/

    i = 8;

    do

    {

       ch ++;

    } while (--i);

    return ch;

}

代码-1  用“++”判断的while代码段

代码-2  用“--”判断的while代码段

上面两段示例代码中,循环次数都一样,但是在有些平台中,时空效率却大不相同。以51架构单片机为例,我们来分析它编译出来的汇编代码。

             ; FUNCTION _while_inc

;---- Variable 'ch' assigned to Register 'R7' ---- /* R7表示形参ch */

;---- Variable 'i' assigned to Register 'R6' ----/*R6表示局部变量i*/

0000 E4                CLR     A        /* i = 0; */

0001 FE                MOV     R6,A

0002         ?C0003:

0002 0F                INC     R7       /* ch++ */

;  代码“while (++i < 8)”对应汇编语句

0003 0E                INC     R6

0004 EE                MOV    A, R6

0005 C3                CLR     C

0006 9408              SUBB   A, #08H

0008 40F8              JC      ?C0003

000A         ?C0004:

000A 22                RET   

代码-3  C51平台++”编译得到的汇编代码段(C51平台)

             ; FUNCTION _while_dec

;---- /* R7表示形参ch   R6表示局部变量i */

0000 E4                CLR     A         /* uint8_t i = 0 令人困惑?? */

0001 7E08              MOV     R6,#08H  /* i = 8; */

0003         ?C0003:

0003 0F                INC     R7       /* ch++ */

;  代码“while (--i)”对应汇编语句

0004 DEFD              DJNZ    R6,?C0003

0006         ?C0004:

0006 22                RET     

代码-4  C51平台--”编译得到的汇编代码段(C51平台)

从上述的汇编代码中,我们发现用“++”和“--”的两段while循环代码,在空间上前者比后者多了4条指令共5个字节,时间上多花费了8次*4=32个指令周期如果循环次数进一步加大,这一差距就不能忽视。

为何会有如此之多的差异?其本质原因是因为C51拥有一条“复合指令DJNZ”,其定义如下,意为:将“寄存器或者某个ram内的byte变量”的值减1,完事后判断不为0则跳转”。该指令融合了“算术运算 + 逻辑运算 + 跳转”三个功能。

所以,你该知道,大牛的代码while用"--和0比较",一般的代码就是“++和var比较”或者用“for循环”。

一般来说,不管什么平台,判断0总是效率较高的,毕竟cpu的状态寄存器都有个Zero状态位


------------------------------------------------------------------------------------------------------------------------------------------------

而对于M3平台,两段代码并没有带来明显的时空差异。

while_inc PROC

        MOVS     r1,#0

|L1.2|

        ADDS     r0,r0,#1

        ADDS     r1,r1,#1

        UXTB     r1,r1

        UXTB     r0,r0

        CMP      r1,#8

        BCC      |L1.2|

        BX       lr

        ENDP

while_dec PROC

        MOVS     r1,#8

|L1.2|

        ADDS     r0,r0,#1

        SUBS     r1,r1,#1

        UXTB     r0,r0

        ANDS     r1,r1,#0xff

        BNE      |L1.2|

        BX       lr

        ENDP

M3平台“++”的汇编代码段(M3平台)

M3平台--”的汇编代码段(M3平台)

这两段汇编,前者之所以比后者多了一句“UXTB     r1,r1”,因为加法的值需要32位和8位的数值转换,归零的减法不需要。这又应那个原则:“零值”操作效率较高。

因此,一个软件工程师,需要对其操作平台的指令系统有足够的熟悉,对基本C语句的汇编转换了然于心,才能写出高质、高效的代码。老工程师的价值,也体现在此。

另外值得一提的是,在这个实验中我发现一个比较有意思的现象:在函数while_dec中变量声明兼初始化代码“uint8_t i = 0;”,紧随其后就是“i = 8”的覆盖赋值代码。而它在C51平台编译器上,却产生了一个冗余代码“CLR   A”,令人困惑。在M3平台的编译中,就没有这样的现象了。不管怎样,随着编译器越来越智能,变量声明时即刻初始化,是一个非常好的编程习惯,它不会对代码空间增加额外的负担。


路过

鸡蛋
9

鲜花

握手

雷人

刚表态过的朋友 (9 人)

发表评论 评论 (18 个评论)

回复 sedatefire 2014-11-18 23:17
#include <stdint.h>
博客一直解析不了
只好用“”代替了
回复 sedatefire 2014-11-18 23:18
C51中++和--的效率差异,相信50%以上的人不知道。
回复 sedatefire 2014-11-18 23:32
地址|机器码            汇编明文
----------------------------------
0003 0E                INC     R6
0004 EE                MOV    A, R6
0005 C3                CLR     C
0006 9408              SUBB   A, #08H
0008 40F8              JC      ?C0003
000A    ?C0004:  /* 内部链接标签 */
000A 22                RET
回复 研发之声 2014-11-28 09:08
虽然c51感觉没落了
但“零运算效率更高”这个原则,不会过气
回复 ecoren 2014-12-3 22:10
搞硬件的路过,表示知道++ --,除此之外,能否编译出高效的代码编译器也很重要
回复 cauhorse 2014-12-11 19:02
很多人也不知道while(--i)和while(i--)的差异。
回复 sedatefire 2014-12-11 23:17
cauhorse: 很多人也不知道while(--i)和while(i--)的差异。
这个先减还是后减,算是比较基础的啦
回复 cauhorse 2014-12-12 12:16
sedatefire: 这个先减还是后减,算是比较基础的啦
其实主要也是对应汇编代码的区别,原来做“软件延时”的时候试过,果然还是“--i”最省。
回复 sedatefire 2014-12-12 13:44
cauhorse: 其实主要也是对应汇编代码的区别,原来做“软件延时”的时候试过,果然还是“--i”最省。
嗯  哈 我都没想到这一层  我比较少用这个硬延时(初始化用用)
我遇到更邪门的事儿呢
同样的while (i--) 放在不同的位置,发现耗时不同,后来发现是指令集对齐的问题
回复 cauhorse 2014-12-12 15:11
sedatefire: 嗯  哈 我都没想到这一层  我比较少用这个硬延时(初始化用用)
我遇到更邪门的事儿呢
同样的while (i--) 放在不同的位置,发现耗时不同,后来发现是指令集对齐的 ...
我8位机用的最多,可能没遇到过对齐问题,不过编译器有时会闹点鬼,估计还是自己学艺不精。
回复 sedatefire 2014-12-15 10:11
cauhorse: 我8位机用的最多,可能没遇到过对齐问题,不过编译器有时会闹点鬼,估计还是自己学艺不精。
STM32有个 fetch预取指的东西 涉及到指令周期    和8byte对齐有关  
是指令周期影响了延时精度   深究原理的话   和体系结构有关
回复 cauhorse 2014-12-15 17:12
sedatefire: STM32有个 fetch预取指的东西 涉及到指令周期    和8byte对齐有关  
是指令周期影响了延时精度   深究原理的话   和体系结构有关 ...
这个能通过预编译语句强制对齐的不?
回复 sedatefire 2014-12-16 11:17
cauhorse: 这个能通过预编译语句强制对齐的不?
M3有个预取指延迟周期设定的,在初始化的时候搞一下,就可以解。
我朋友是这样告诉我的,不过我现在转行做手机驱动了,还没有机会去验证。