for循环和递归算法的运行效率比较(c语言)

实验目的

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

 实验平台、工具

在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:

for循环和递归算法的运行效率比较(c语言)_第1张图片

图2


实验结果讨论

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

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

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

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

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

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

你可能感兴趣的:(算法与数据结构)