维库电子市场网
登录 免费注册 首页 | 行业黑名单 | 委托交易 | 帮助 | English
技术交流 | 电路欣赏 | 工控天地 | 数字广电 | 通信技术 | 电源技术 | 测控之家 | EMC技术 | ARM技术 | EDA技术 | PCB技术 | 嵌入式系统
驱动编程 | 集成电路 | 器件替换 | 模拟技术 | 新手园地 | 单 片 机 | DSP技术 | MCU技术 | IC 设计 | IC 产业 | CAN-bus/DeviceNe

GCCAVR C++散转代码效率之分析

作者:hotpower 栏目:单片机
GCCAVR C++散转代码效率之分析
      GCCAVR C++散转代码效率之分析

HotPower@126.com  2005.6.29 于西安大雁塔村队部

从以下三种常用的散转代码反汇编列表及HEX文件中可以看出:
1.if-else-endif代码命令执行相对列表从+00000094~+000000D0不等,
  命令执行相对条数从0x09~0x45条指令,即9~69条指令.
  HEX文件代码优化编译长度为1660字节。
  
2.SWITCH代码命令执行相对列表从+000000B2~+000000F4不等,
  命令执行相对条数从0x27~0x69条指令,即39~105条指令.
  HEX文件代码优化编译长度为1732字节。
  
3.函数指针代码命令全部执行相对列表都在+00000081处.
  命令执行相对条数恒为0x19条指令,即25条指令.
  HEX文件代码优化编译长度为1602字节。
  
所以,在本例中(散转个数为15时),函数指针代码的综合效率是最高的,SWITCH代码效率最差。

当然命令执行相对条数并不代表命令执行的实际周期数,这里有命令跳转的原因所在。

但是可以肯定或断定:当散转个数大于一定数目时,函数指针代码的综合效率肯定是最高的,
当只有几个散转时,用函数指针肯定是吃饱了没事干了。
但有一点可以肯定:函数指针代码的结构是最美的...(不用看的,拿来只需加入事件处理的函数名及代码即可)

这篇“水文”就算是我对www.ouravr.com有位高手对我“招安”---劝不要用函数指针的回复吧...

“我说HotPower,你何必要这么做呢。你这么大的车,单片机那个老牛,她拉着,恐怕有点费力吧。 建议不要用函数指针!”

可他的“建议”有依据吗???我虽在“传播非典”,但不想误人子弟!!!看我的帖应该先打些“预防针”。。。

在这里也想送他一句话: "胡整就是硬道理,不整永远拉牛车"。

再次谢谢21IC的各位网友!!!

我此帖的目的就是让人们知道“三个臭皮匠如何熏走诸葛亮”的。。。

实际上用不用不是关键,关键的是会选择性的使用更好的方法和手段。

在菜单程序中,也可应用函数指针以简化和优化程序,至少可以压缩代码长度。

此帖将收录到[HotPower的水潭里]的“非奠应用怪潭”中,有时间的话会继续论证此“零耗时消除键盘抖动”
是如何实现任意键组合编程的,会出些“怪招”的,当然是瞎想出来的,也有“理论依据”的依托。

好了,也累了。就灌到这里吧。有耐心的网友可以继续研究下面的汇编代码。。。反正我看了是头晕。。。

函数指针反汇编列表分析

可以看出:
程序入口: +00000069
Key00()到Key24()全部在+00000081处执行,命令执行相对条数0x0081-0x0069+1=0x0019

263:      void KeyObj::KeyCommandExec(unsigned CHAR mode)
264:      {
+00000069:   01FC        MOVW    R30,R24          Copy register pair
267:        KeyCount &= 0x07;//取键盘序号//使用1602字节(.text),运行正确
+0000006A:   8121        LDD     R18,Z+1          Load indirect with displacement
+0000006B:   7027        ANDI    R18,0x07         Logical AND with immediate
+0000006C:   8321        STD     Z+1,R18          Store indirect with displacement
268:        if (KeyCount <= 4) {//只有5个键,KeyCount>=5为3个短接线.
+0000006D:   3025        CPI     R18,0x05         Compare with immediate
+0000006E:   F498        BRCC    PC+0x14          Branch if carry cleared
269:          func = (PFV)pgm_read_word (&KeyCommandTab[mode][KeyCount]);//取FLASH键盘放事件处理表
+0000006F:   2F86        MOV     R24,R22          Copy register
+00000070:   2799        CLR     R25              Clear Register
+00000071:   01FC        MOVW    R30,R24          Copy register pair
+00000072:   0FEE        LSL     R30              Logical Shift Left
+00000073:   1FFF        ROL     R31              Rotate Left Through Carry
+00000074:   0FEE        LSL     R30              Logical Shift Left
+00000075:   1FFF        ROL     R31              Rotate Left Through Carry
+00000076:   0FE8        ADD     R30,R24          Add without carry
+00000077:   1FF9        ADC     R31,R25          Add with carry
+00000078:   0FE2        ADD     R30,R18          Add without carry
+00000079:   1DF1        ADC     R31,R1           Add with carry
+0000007A:   0FEE        LSL     R30              Logical Shift Left
+0000007B:   1FFF        ROL     R31              Rotate Left Through Carry
+0000007C:   5CE6        SUBI    R30,0xC6         Subtract immediate
+0000007D:   4FFF        SBCI    R31,0xFF         Subtract immediate with carry
+0000007E:   9185        LPM     R24,Z+           Load program MEMORY and postincrement
+0000007F:   9194        LPM     R25,Z            Load program MEMORY
270:          func();//运行KeyX0()~KeyX4()
+00000080:   01FC        MOVW    R30,R24          Copy register pair
---- No Source --------------
2楼: >>参与讨论
农民讲习所
题外话:
PFV KeyObj::KeyCommandTab[3][5] PROGMEM = {//键盘放事件处理表
  {Key00, Key01, Key02, Key03, Key04}, //键释放事件处理
  {Key10, Key11, Key12, Key13, Key14}, //压键事件处理
  {Key20, Key21, Key22, Key23, Key24}  //长压键事件处理
};

只能和特定任务绑定在一起,结构不好。采用消息处理结构,可以和所有未来处理绑定,实现一对N。

题外话,不影响本课题,请hotpower继续。

3楼: >>参与讨论
hotpower
谢谢所长大人的指导和教育
说实话我主要是想优化几个字节罢了...

前些时候用M8L和T26L构成TWI/USI多机通讯及LCD菜单时把我搞惨了...

最好是---不是程序编不好,而是如何进行代码复用而将好端端的程序结构打乱才能满足空间的要求...

哈哈,那时没一条程序都需考虑如何节约字节...

DDRC  |= (1 << KeyCP) | (1 << KeyCLK);//三条指令

就必须:(2条指令)
  DDRC  |= (1 << KeyCP);
  DDRC  |= (1 << KeyCLK);

您说我"背不背"...

实际本帖不是目的,解决了问题我心里就高兴,哪怕不吃不睡...

再次谢谢所长大人的指导和教育,农讲所学员向所长致意!!!


4楼: >>参与讨论
hotpower
多维数组对函数指针本身就不太公平
多维数组只能加速if和SWITCH的分检能力,却加深了pgm_read_word计算数组的负担.

如果改为一维函数数组:
  static PFV KeyCommandTab[15];//必须声明为static!!!

PFV KeyObj::KeyCommandTab[15] PROGMEM = {//键盘放事件处理表
  Key00, Key01, Key02, Key03, Key04, //键释放事件处理
  Key10, Key11, Key12, Key13, Key14, //压键事件处理
  Key20, Key21, Key22, Key23, Key24  //长压键事件处理
};

void KeyObj::KeyCommandExec(unsigned CHAR num)
{
PFV func;
  func = (PFV)pgm_read_word (&KeyCommandTab[num]);//取FLASH键盘放事件处理表
  func();//运行KeyX0()~KeyX4()
}

这样对函数指针将提高一点效率,但对if和SWITCH执行最后的Key24()就很难受了...

汇编结果如下:
268:          func = (PFV)pgm_read_word (&KeyCommandTab[num]);//取FLASH键盘放事件处理表
+00000068:   2FE6        MOV     R30,R22          Copy register
+00000069:   27FF        CLR     R31              Clear Register
+0000006A:   0FEE        LSL     R30              Logical Shift Left
+0000006B:   1FFF        ROL     R31              Rotate Left Through Carry
+0000006C:   5CE6        SUBI    R30,0xC6         Subtract immediate
+0000006D:   4FFF        SBCI    R31,0xFF         Subtract immediate with carry
+0000006E:   9185        LPM     R24,Z+           Load program MEMORY and postincrement
+0000006F:   9194        LPM     R25,Z            Load program MEMORY
269:          func();//运行KeyX0()~KeyX4()
+00000070:   01FC        MOVW    R30,R24          Copy register pair
---- No Source ------------------------------------------------------------------------------------
+00000071:   9509        ICALL                    Indirect call to (Z)
+00000072:   9508        RET                      Subroutine return

5楼: >>参与讨论
宇宙飞船
不用指针的也算高手?庆幸的是已经被 hotpower降级作低手了!
JAVA 我末用过,但据我所知 JAVA 就不能用指针,所有的东西只能用大数组,以速度换取稳定性,(注:这个稳定性是相对的,只是有些用C和C++ 的编程人员的习惯和熟练程度问题造成的)。

C和C++ 的精华就是指针,C++更扩充了类,把一切大自然的现象表达在程序设计当中。。。。 

6楼: >>参与讨论
51avr
我的LCD菜单中使用程序指针,感觉比较简洁。支持楼主。
 
7楼: >>参与讨论
pheavecn
用ICALL指令既占用寄存器,又慢。我一般用堆栈的方式散转。
    ;用堆栈的散转程序。
    ;16/17个时钟周期(16-bit PC为16),9条指令空间
    ;入口为EntryNo。
SubXXX:
    lds    al,EntryNo    ;17clocks,9ins
    andi    al,0x7       ;边界检查
    subi    al,low(-(Branch_Table))
    PUSH    al
    ldi    al,0
    sbci    al,high(-(Branch_Table))
    PUSH    al
    ret
Branch_Table:    
    rjmp    Branch0
    rjmp    Branch1
    rjmp    Branch2
    rjmp    Branch3
    rjmp    Branch4
    rjmp    Branch5
    rjmp    Branch6
    rjmp    Branch7
Branch0:    
         rjmp     Sub_End
Branch1:    
         rjmp     Sub_End
Branch2:    
         rjmp     Sub_End
Branch3:    
         rjmp     Sub_End
Branch4:    
         rjmp     Sub_End
Branch5:    
         rjmp     Sub_End
Branch6:    
         rjmp     Sub_End
Branch7:    
         rjmp     Sub_End
Sub_End:
         ret
看来编译器还不够聪明。

* - 本贴最后修改时间:2005-6-30 13:23:47 修改者:pheavecn

8楼: >>参与讨论
hotpower
哈哈...二教主.C++能用ICALL已算做"极品"了
肯定汇编可以打遍天下无敌手,用C++就将就点吧...

AVR我可是"极品的菜鸟"---连汇编都不会而直接用C++(而且拒绝用C)
                        三月"出徒"乱搞了几个"产品".

9楼: >>参与讨论
pheavecn
不管执行速度还是占ROM/Register空间,这种散转都好很多。
至于占用的2byte 堆栈...
AVR最不缺的就是这个了。

10楼: >>参与讨论
hotpower
51中我喜欢2个PUSH+ret散转
可在"纯情"C或C++中很难实现...光取这个类成员函数指针就让我晕了近3天...

不过icall确实也不错,但要实现2PUSH+ret散转,很难.特别是"拒绝用A"的C人们...

回见...该上班受罪了...羡慕二教主的"自由身"...

11楼: >>参与讨论
zhousd
收藏!拿回家慢慢吃。。。
 
参与讨论
昵称:
讨论内容:
 
 
相关帖子
双龙AVRISP下载线在ATmega128情况下的连线问题?
AVR 的C编译器到底选那个呀?
isp下载线最长允许有多长?
ATmega128看门狗问题,请大侠帮忙。谢谢!
这就是AVR!!!
免费注册为维库电子开发网会员,参与电子工程师社区讨论,点此进入


Copyright © 1998-2006 www.dzsc.com 浙ICP证030469号