实验目的

在编程语言中,对比不同编程风格的代码写法,或者通过使用不同的编译器和编译优化参数,通过编译器生成汇编代码,静态分析所生成汇编代码的运行效率。

 实验平台、工具

在window 7平台下,采用vc++ 6.0编译器来编写相应的C程序,然后通过UltraCompare工具来比较vc++编译程序后产生的汇编文件的异同(win-TC在win7 64位下无法正常运行)。

 

实验设计

      方向:

· 1. 相同编译器下相同编译参数,不同的源代码写法(源代码结果一样,但实现不同,例如递归和通常的实现等)的对比;

代码1:(for循环实现的代码)

 

void main()
{
  int i,n=0;
  for(i=0; i<10000;i++)
  {
         n++;
  }
  printf("%d",n);
}
 
   代码2:(递归实现的代码)
   int recursion(int n, int i)
{
       if(i>0)
       {
              n++;
              i--;
              n = recursion(n,i);
       }
       else
       {
              return n;
       }
       return n;
}
 
void main()
{
       int k;
       k = recursion(0,10000);
       printf("%d",k);
}
 
       通过转换为汇编语言,对比以上代码的异同(通过UlTraCompare)。 
4、实验步骤
首先是得到的汇编代码(通过VC++得到的)
For循环对应的汇编代码:
       TITLE     E:\资料\各科资料\嵌入式软件开发\c程序\1.c
       .386P
includelisting.inc
if @Version gt510
.model FLAT
else
_TEXT    SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT    ENDS
_DATA    SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA    ENDS
CONST   SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST   ENDS
_BSS      SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS      ENDS
$$SYMBOLS SEGMENT BYTE USE32 'DEBSYM'
$$SYMBOLS ENDS
$$TYPES       SEGMENT BYTE USE32 'DEBTYP'
$$TYPES       ENDS
_TLS      SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS      ENDS
;      COMDAT ??_C@_02MECO@?$CFd?$AA@
CONST   SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST   ENDS
;      COMDAT _main
_TEXT    SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT    ENDS
FLAT      GROUP _DATA, CONST, _BSS
       ASSUME       CS:FLAT, DS: FLAT, SS: FLAT
endif
PUBLIC  _main
PUBLIC  ??_C@_02MECO@?$CFd?$AA@                            ; `string'
EXTRN   _printf:NEAR
EXTRN   __chkesp:NEAR
;      COMDAT ??_C@_02MECO@?$CFd?$AA@
; File E:\资料\各科资料\嵌入式软件开发\c程序\1.c
CONST   SEGMENT
??_C@_02MECO@?$CFd?$AA@DB '%d', 00H                     ;`string'
CONST   ENDS
;      COMDAT _main
_TEXT    SEGMENT
_i$ = -4
_n$ = -8
_main     PROC NEAR                              ;COMDAT
 
; 4    : {
 
       push ebp
       mov ebp,esp
       sub  esp,72                                ;00000048H
       push ebx
       push esi
       push edi
       lea   edi,DWORD PTR [ebp-72]
       mov ecx,18                                ;00000012H
       mov eax,-858993460                         ;ccccccccH
       rep stosd
 
; 5    : inti,n=0;
 
       mov DWORDPTR _n$[ebp], 0
 
; 6    : for(i=0;i<10000; i++)
 
       mov DWORDPTR _i$[ebp], 0
       jmp  SHORT$L339
$L340:
       mov eax,DWORD PTR _i$[ebp]
       add  eax,1
       mov DWORDPTR _i$[ebp], eax
$L339:
       cmp DWORDPTR _i$[ebp], 10000            ; 00002710H
       jge   SHORT$L341
 
; 8    :         n++;
 
       mov ecx,DWORD PTR _n$[ebp]
       add  ecx,1
       mov DWORDPTR _n$[ebp], ecx
 
; 9    : }
 
       jmp  SHORT$L340
$L341:
 
; 10   : printf("%d",n);
 
       mov edx,DWORD PTR _n$[ebp]
       push edx
       push OFFSETFLAT:??_C@_02MECO@?$CFd?$AA@     ;`string'
       call  _printf
       add  esp,8
 
; 11   : }
 
       pop  edi
       pop  esi
       pop  ebx
       add  esp,72                                ;00000048H
       cmp ebp,esp
       call  __chkesp
       mov esp,ebp
       pop  ebp
       ret   0
_main     ENDP
_TEXT    ENDS
END
 
递归代码的汇编代码:
       TITLE     E:\资料\各科资料\嵌入式软件开发\c程序\2.c
       .386P
includelisting.inc
if @Version gt510
.model FLAT
else
_TEXT    SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT    ENDS
_DATA    SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA    ENDS
CONST   SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST   ENDS
_BSS      SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS      ENDS
$$SYMBOLS SEGMENT BYTE USE32 'DEBSYM'
$$SYMBOLS ENDS
$$TYPES       SEGMENT BYTE USE32 'DEBTYP'
$$TYPES       ENDS
_TLS      SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS      ENDS
;      COMDAT ??_C@_02MECO@?$CFd?$AA@
CONST   SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST   ENDS
;      COMDAT _recursion
_TEXT    SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT    ENDS
;      COMDAT _main
_TEXT    SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT    ENDS
FLAT      GROUP _DATA, CONST, _BSS
       ASSUME       CS:FLAT, DS: FLAT, SS: FLAT
endif
PUBLIC  _recursion
EXTRN   __chkesp:NEAR
;      COMDAT _recursion
_TEXT    SEGMENT
_n$ = 8
_i$ = 12
_recursion PROCNEAR                                   ;COMDAT
 
; 4    : {
 
       push ebp
       mov ebp,esp
       sub  esp,64                                ;00000040H
       push ebx
       push esi
       push edi
       lea   edi,DWORD PTR [ebp-64]
       mov ecx,16                                ; 00000010H
       mov eax,-858993460                         ;ccccccccH
       rep stosd
 
; 5    : if(i>0)
 
       cmp DWORDPTR _i$[ebp], 0
       jle    SHORT$L341
 
; 7    :         n++;
 
       mov eax,DWORD PTR _n$[ebp]
       add  eax,1
       mov DWORDPTR _n$[ebp], eax
 
; 8    :         i--;
 
       mov ecx,DWORD PTR _i$[ebp]
       sub  ecx,1
       mov DWORDPTR _i$[ebp], ecx
 
; 9    :         n= recursion(n,i);
 
       mov edx,DWORD PTR _i$[ebp]
       push edx
       mov eax,DWORD PTR _n$[ebp]
       push eax
       call  _recursion
       add  esp,8
       mov DWORDPTR _n$[ebp], eax
 
; 11   : else
 
       jmp  SHORT$L342
$L341:
 
; 13   :         returnn;
 
       mov eax,DWORD PTR _n$[ebp]
       jmp  SHORT$L340
$L342:
 
; 15   : returnn;
 
       mov eax,DWORD PTR _n$[ebp]
$L340:
 
; 16   : }
 
       pop  edi
       pop  esi
       pop  ebx
       add  esp,64                                ;00000040H
       cmp ebp,esp
       call  __chkesp
       mov esp,ebp
       pop  ebp
       ret   0
_recursion ENDP
_TEXT    ENDS
PUBLIC  _main
PUBLIC  ??_C@_02MECO@?$CFd?$AA@                            ; `string'
EXTRN   _printf:NEAR
;      COMDAT ??_C@_02MECO@?$CFd?$AA@
; File E:\资料\各科资料\嵌入式软件开发\c程序\2.c
CONST   SEGMENT
??_C@_02MECO@?$CFd?$AA@DB '%d', 00H                     ;`string'
CONST   ENDS
;      COMDAT _main
_TEXT    SEGMENT
_k$ = -4
_main     PROC NEAR                              ;COMDAT
 
; 19   : {
 
       push ebp
       mov ebp,esp
       sub  esp,68                                ;00000044H
       push ebx
       push esi
       push edi
       lea   edi,DWORD PTR [ebp-68]
       mov ecx,17                                ;00000011H
       mov eax,-858993460                         ;ccccccccH
       rep stosd
 
; 20   : intk;
; 21   : k =recursion(0,10000);
 
       push 10000                                 ; 00002710H
       push 0
       call  _recursion
       add  esp,8
       mov DWORDPTR _k$[ebp], eax
 
; 22   : printf("%d",k);
 
       mov eax,DWORD PTR _k$[ebp]
       push eax
       push OFFSETFLAT:??_C@_02MECO@?$CFd?$AA@     ;`string'
       call  _printf
       add  esp,8
 
; 23   : }
 
       pop  edi
       pop  esi
       pop  ebx
       add  esp,68                                ;00000044H
       cmp ebp,esp
       call  __chkesp
       mov esp,ebp
       pop  ebp
       ret   0
_main     ENDP
_TEXT    ENDS
END

通过UltaCompare比较:

图1:

fori递归java 递归和for循环_优化

图2

fori递归java 递归和for循环_c语言_02

实验结果讨论

两段代码分别通过循环(for)、递归来实现一个值为0的变量累加到10000的过程。

抛开两端源代码在C文件的比较中存在差异不说,只是通过Ultracompare比较两段汇编代码,会发现,较之for循环,在递归的汇编代码中,存在大量的push、pop操作,可见,在运行递归算法程序时,计算机要对相应的参数进行不断的出栈、入栈操作,以保存递归的上一层的参数。每累加一次,都要做一次相应的操作,10000次的累加操作就要进行10000次出入栈的操作,消耗巨大,程序运行时间长,影响机器的运行效率。

另外,仔细观察汇编代码,会发现,相比于循环的代码,递归代码中要使用更多的寄存器,同样会对机器的运行效率影响较大。

递归运行可能在某些大数据量的程序中使用的话虽然说对程序员来说很方便,只需要调用相关的函数(方法)即可,但是对于机器来说却是高消耗的,而且效率低,不管是应用程序还是科学验证程序,不管是从用户体验还是机器底层结构来看,都是要慎重考虑的。

从算法的角度来看,应该活用动态规划以及贪心算法,不能死套递归。

想起之前在算法课的综合性试验,老师让我们就一个问题,通过不同的算法去实现,同事比较各个算法的运行时间的差别,结果出来发现,1000000级的运算量,动态规划算法在30秒以内就可完成,但是递归方法就要用到5分钟的运算时间,而且期间电脑CPU发热很高。