測試c語言函數調用性能因素之測試三


函數調用:即調用函數調用被調用函數,調用函數壓棧,被調用函數執行,調用函數出棧,調用函數繼續執行的一個看似簡單的過程,系統底層卻做了大量操作。

操作:

1,               調用函數幀指針(函數參數,局部變量,棧幀狀態值,函數返回地址)入棧,棧指針自減

2,               保存調用函數的狀態數據入寄存器

3,               被調用函數幀指針入棧,執行當前的被調用函數

4,               被調用函數執行結束,退棧,返回到調用函數的幀指針,從寄存器中恢復當時狀態數據

5,               繼續執行調用函數,直至結束

即整個調用操作有一個壓棧出棧,保存和恢復狀態數據的過程。而系統棧內存是有默認的固有大小。有多少次函數調用就會分配多少棧幀。故,函數調用性能影響有如下因素:

1,函數遞歸層數;

2,參數個數(參數簽名所占內存大小)

         2.1同類型不同參數個數;

         2.2同參數個數不同參數類型;

         2.3同參數類型同參數個數,但參數類型所占內存大小不同;

3,函數棧大小,即函數局部變量所占棧大小。

為了測試C語言函數調用性能(時間消耗)因素,編寫了一個簡單程序運行在如下環境中:

                                    Intel(R) Core(TM) i5-2400 CPU @ 3.10GHz  memery size:7833700 kB(7.47GB)

在函數調用的開始與結束處,用time.h中的clock()函數返回CPU時鍾計時單位數(下表中的starttime和endtime),用durationtime=endtime-starttime表示函數調用的時間消耗。如下:

clock_t  starttime=clock();

函數調用…

clock_t  endtime=clock();

//除以CLOCKS_PER_SEC,得到以秒為單位的時間結果

double durationtime=(double)(endtime-starttime)/CLOCKS_PER_SEC;//表示函數調用占用cpu的時間,不包括子進程或者printf等的操作的時間

 

注:clock()記錄的是進程占用cpu的時間,精確度為微秒;詳細講解clock()函數的網址:http://site.douban.com/199048/widget/notes/12005386/note/253542964/

一.函數遞歸層數(循環1000000次

棧(字節)

參數(字節)

遞歸次數

總函數調用時間消耗(秒)

每循環函數調用時間消耗(微秒)

每次函數調用平均時間消耗(納秒)

1024

24

10

2.9

2.9

290

1024

24

20

5.713

5.713

285.65

1024

24

30

9.025

9.025

300.83

1024

24

50

16.0767

16.0767

321.534

1024

24

80

21.79

21.79

272.375

1024

24

100

30.73

30.73

307.3

1024

24

200

66.24

66.24

331.2

 

 

 

 

 

 

 

 

 

 

 

 

 

注:平均每次函數調用時間消耗=durationtime/調用層數/ 循環次數

      每循環函數調用時間消耗=durationtime/ 循環次數

 

函數調用根據不同的調用層數不同的時間平均消耗,如下折線圖:

                                                                                    圖1

每次函數調用平均時間消耗,如下折線圖:

                                                                               圖2

結論:1,在參數所占內存相同和函數棧大小相同的情況下,函數調用的時間消耗隨着函數調用層數增加而增加;如圖1;

        2,在參數所占內存相同和函數棧大小相同的情況下,每次函數調用的時間消耗大概在300納秒左右;如圖2;

 

二,函數棧大小

循環次數

棧(字節)

參數

(字節)

遞歸次數

總函數調用時間消耗(秒)

每循環函數調用時間消耗(微秒)

平均每次函數調用(納秒)

1000000

16

24

50

9.4

9.4

184

1000000

32

24

50

9.37

9.37

187.4

1000000

64

24

50

9.5

9.5

190

1000000

128

24

50

10.415

10.415

208.3

1000000

256

24

50

11.805

11.805

236.1

1000000

512

24

50

14

14

280

1000000

1024

24

50

16.0767

16.0767

321.534

1000000

2048

24

50

18.42

18.42

368.4

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

注:平均每次函數調用時間消耗=durationtime/調用層數/ 循環次數

      每循環函數調用時間消耗=durationtime/ 循環次數

 

函數調用根據不同的調用層數不同的時間平均消耗,如下折線圖:

                                                                            圖3

 

 

每次函數調用平均時間消耗,如下折線圖:

 

                                                                                圖4

 

結論:1,在函數參數相同和函數調用層數相同的情況下,函數調用時間消耗隨函數棧大小的增加而增加;如圖3;

        2,在函數參數相同和函數調用層數相同的情況下,每次函數調用時間消耗隨函數棧大小的增加而增加;如圖4

 

三,參數個數

棧(字節)

參數

(字節)

遞歸次數

總函數調用時間消耗(秒)

每循環函數調用時間消耗(微秒)

平均每次函數調用(納秒)

1024

24

50

16.0767

16.0767

321.5

1024

36

50

16.245

16.245

324.9

1024

48

50

16.345

16.345

326.9

1024

60

50

15.915

15.915

318.3

1024

72

50

14.29

14.29

285.8

1024

84

50

15.76

15.76

315.2

1024

96

50

15.14

15.14

302.8

1024

108

50

13.975

13.975

279.5

1024

120

50

16.68

16.68

333.6

1024

144

50

15.37

15.37

307.4

1024

180

50

14.42

14.42

288.4

1024

192

50

14.62

14.62

292.4

    

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

注:平均每次函數調用時間消耗=durationtime/調用層數/ 循環次數

      每循環函數調用時間消耗=durationtime/ 循環次數

函數調用根據不同的函數參數大小的時間平均消耗,如下折線圖:

每次函數調用平均時間消耗,如下折線圖:

 

 

    結論:  經過前幾次的函數測試,雖然存在誤差,但是仍然可以得出參數對於函數調用的時間消耗的影響,在於參數所占內存大小;函數傳參存在兩種方式:值傳參和引用傳參;兩種方式在一般情況下,不會占用過多的內存;故,在一般情況下,參數對函數調用的時間消耗的影響不明顯;

 

四,結論:

    1,在函數參數大小為24字節和函數棧大小為1024字節的情況下,遞歸50次的函數時間消耗為16.0767微秒,可以粗略得出每次函數調用(壓棧出棧)的時間消耗為320納秒左右;

 

思路:1,函數參數大小:函數參數分為值傳參和引用傳參(參數的指針);一般值傳參為常用的值類型,這樣的參數一般不會占用過多的內存;引用參數是參數地址也不會占用過多內存;所以在一般情況下,函數參數對函數調用時間消耗影響不大;

        2,計數:循環1000000次函數遞歸,是為了想提高數據的精確性和便於計算;1秒=1000000微秒;

        3,遞歸層數:選擇可能常規下遞歸的層數(24--35)

       4,函數棧大小:按照以太網的最大字節1500字節,選擇在1024字節左右做以上實驗;

代碼:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #include<time.h>
 5 #define array_len 256
 6 typedef struct {
 7  int typeone;
 8  int typetwo;
 9 }struct_type;
10 long call_back(int call_num,int typeone,int typetwo,int typethree,long p_recorde)
11 {
12  if(call_num<=0)return p_recorde;
13  int i_rand[array_len];
14  int i=0;
15  clock_t start_time,end_time;
16  start_time=clock();
17  for(i=0;i<array_len;i++)
18  {
19     i_rand[i]=rand(); 
20  }
21  end_time=clock();
22  p_recorde+=(long)(end_time-start_time);
23  call_back(call_num-1,typeone,typetwo,typethree,p_recorde);
24 }
25 void main(int argc,char *argv[])
26 {
27   int loop_num=atoi(argv[1]),call_num=atoi(argv[2]);
28   long p_recorde=0,sum=0;
29   clock_t start_time,end_time;
30   start_time=clock();
31   int i;
32   for(i=0;i<loop_num;i++)
33   {
34    sum_loop+=call_back(call_num,0,0,0,p_recorde);
35   }
36   end_time=clock();
37   double duration_time=(double)(end_time-start_time)/CLOCKS_PER_SEC-(double)sum_loop/CLOCKS_PER_SEC;
38   printf("sum=%f   duration=%f\n",sum_loop,duration_time);
39 }

 

 

代碼思路:1,為了減少數據cache命中的影響,在每次函數調用中用了rand()獲取隨機數,並記錄時間消耗a;

              2,記錄函數調用的時間總消耗b,b-a的差即為函數調用的時間總消耗;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM