關於嵌套循環效率研究

本文爲原創,轉載請註明:http://www.cnblogs.com/tolimit/

 

引言

  你們都知道,當進行嵌套循環時,大循環放最外面和放最裏面所形成的執行效率會不一樣,本篇文章會經過彙編代碼進行分析具體狀況。ubuntu

 

測試環境

  • 操做系統:ubuntu-14.04.1
  • 編譯器:gcc-4.8
  • 編譯命令:gcc test.c -o test -g -Wall

 

測試代碼:測試

 1 #include <stdio.h>
 2 
 3 /* 大循環在外 */
 4 void big_in_out (void)
 5 {
 6     int i;
 7     int j;
 8     int k;
 9 
10     for (i = 10000; i != 0; i--)
11         for (j = 1000; j != 0; j--)
12             for (k = 100; k != 0; k--)
13                 ;
14 }
15 
16 /* 大循環在內 */
17 void big_in_in (void)
18 {
19     int i;
20     int j;
21     int k;
22 
23     for (i = 100; i != 0; i--)
24         for (j = 1000; j != 0; j--)
25             for (k = 10000; k != 0; k--)
26                 ;
27 }
28 
29 int main (int argc, char * argv[])
30 {
31     return 0;
32 }

 

經過objdump命令,獲取其彙編代碼,以下:spa

  1 #include <stdio.h>
  2 
  3 void big_in_out (void)
  4 {
  5   4004ed:    55     push %rbp
  6   4004ee:    48 89 e5     mov %rsp,%rbp
  7     int i;
  8     int j;
  9     int k;
 10 
 11     for (i = 10000; i != 0; i--)
 12   4004f1:    c7 45 f4 10 27 00 00     movl $0x2710,-0xc(%rbp)            # i = 10000         
 13   4004f8:    eb 2a     jmp 400524 <big_in_out+0x37>                      # 跳轉至400524         
 14         for (j = 1000; j != 0; j--)
 15   4004fa:    c7 45 f8 e8 03 00 00     movl $0x3e8,-0x8(%rbp)             # j = 1000                 
 16   400501:    eb 17     jmp 40051a <big_in_out+0x2d>                      # 跳轉至40051a         
 17             for (k = 100; k != 0; k--)
 18   400503:    c7 45 fc 64 00 00 00     movl $0x64,-0x4(%rbp)              # k = 100         
 19   40050a:    eb 04     jmp 400510 <big_in_out+0x23>                      # 跳轉至400510             
 20   40050c:    83 6d fc 01     subl $0x1,-0x4(%rbp)                        # k = k - 1             
 21   400510:    83 7d fc 00     cmpl $0x0,-0x4(%rbp)                        # 判斷k是否爲0             
 22   400514:    75 f6     jne 40050c <big_in_out+0x1f>                      # 不爲0跳轉至40050c            
 23     int i;
 24     int j;
 25     int k;
 26 
 27     for (i = 10000; i != 0; i--)
 28         for (j = 1000; j != 0; j--)
 29   400516:    83 6d f8 01     subl $0x1,-0x8(%rbp)                        # j = j - 1             
 30   40051a:    83 7d f8 00     cmpl $0x0,-0x8(%rbp)                        # 判斷j是否爲0             
 31   40051e:    75 e3     jne 400503 <big_in_out+0x16>                      # j不爲0跳轉至400503     
 32 {
 33     int i;
 34     int j;
 35     int k;
 36 
 37     for (i = 10000; i != 0; i--)
 38   400520:    83 6d f4 01     subl $0x1,-0xc(%rbp)                        # i = i - 1             
 39   400524:    83 7d f4 00     cmpl $0x0,-0xc(%rbp)                        # 判斷i是否爲0             
 40   400528:    75 d0     jne 4004fa <big_in_out+0xd>                       # i不爲0跳轉至4004fa     
 41         for (j = 1000; j != 0; j--)
 42             for (k = 100; k != 0; k--)
 43                 ;                                                                        
 44 }
 45   40052a:    5d     pop %rbp
 46   40052b:    c3     retq 
 47 
 48 000000000040052c <big_in_in>:
 49 
 50 void big_in_in (void)
 51 {
 52   40052c:    55     push %rbp
 53   40052d:    48 89 e5     mov %rsp,%rbp
 54     int i;
 55     int j;
 56     int k;
 57 
 58     for (i = 100; i != 0; i--)
 59   400530:    c7 45 f4 64 00 00 00     movl $0x64,-0xc(%rbp)              # i = 100                 
 60   400537:    eb 2a     jmp 400563 <big_in_in+0x37>                       # 跳轉至400563            
 61         for (j = 1000; j != 0; j--)
 62   400539:    c7 45 f8 e8 03 00 00     movl $0x3e8,-0x8(%rbp)             # j = 1000             
 63   400540:    eb 17     jmp 400559 <big_in_in+0x2d>                       # 跳轉至400559             
 64             for (k = 10000; k != 0; k--)
 65   400542:    c7 45 fc 10 27 00 00     movl $0x2710,-0x4(%rbp)            # k = 10000         
 66   400549:    eb 04     jmp 40054f <big_in_in+0x23>                       # 跳轉至40054f         
 67   40054b:    83 6d fc 01     subl $0x1,-0x4(%rbp)                        # k = k - 1         
 68   40054f:    83 7d fc 00     cmpl $0x0,-0x4(%rbp)                        # 判斷k是否爲0         
 69   400553:    75 f6     jne 40054b <big_in_in+0x1f>                       # 不爲0跳轉至40054b 
 70     int i;
 71     int j;
 72     int k;
 73 
 74     for (i = 100; i != 0; i--)
 75         for (j = 1000; j != 0; j--)
 76   400555:    83 6d f8 01     subl $0x1,-0x8(%rbp)                        # j = j - 1         
 77   400559:    83 7d f8 00     cmpl $0x0,-0x8(%rbp)                        # 判斷j是否爲0         
 78   40055d:    75 e3     jne 400542 <big_in_in+0x16>                       # j不爲0跳轉至400542     
 79 {
 80     int i;
 81     int j;
 82     int k;
 83 
 84     for (i = 100; i != 0; i--)
 85   40055f:    83 6d f4 01     subl $0x1,-0xc(%rbp)                        # i = i - 1         
 86   400563:    83 7d f4 00     cmpl $0x0,-0xc(%rbp)                        # 判斷i是否爲0         
 87   400567:    75 d0     jne 400539 <big_in_in+0xd>                        # i不爲0跳轉至400539     
 88         for (j = 1000; j != 0; j--)
 89             for (k = 10000; k != 0; k--)
 90                 ;                                                                 
 91 }
 92   400569:    5d     pop %rbp
 93   40056a:    c3     retq 
 94 
 95 000000000040056b <main>:
 96 
 97 int main (int argc, char * argv[])
 98 {
 99   40056b:    55     push %rbp
100   40056c:    48 89 e5     mov %rsp,%rbp
101   40056f:    89 7d fc     mov %edi,-0x4(%rbp)
102   400572:    48 89 75 f0     mov %rsi,-0x10(%rbp)
103     return 0;
104   400576:    b8 00 00 00 00     mov $0x0,%eax
105 }

 

循環結果

  因爲是嵌套循環,即便循環0次,好比for(i = 0; i != 0; i--)狀況,都須要執行4條指令,分別是:賦值、跳轉、比較、判斷跳轉。具體的例子如18行~22行彙編代碼所體現的狀況(假設k賦值爲0)。而for的主循環結構爲3條指令,分別爲:賦值、比較、判斷跳轉。具體例子一樣也是18行~22行的彙編代碼所體現。因此在嵌套循環中,假如其中一個循環結構須要循環n次,它所須要執行的指令量爲:操作系統

指令量 = 4 + 3n

 

大循環在外

  好的,根據以上所得的結論,咱們能夠很輕鬆的計算出大循環在外的整個三層循環所須要執行的指令數量,以下:code

i = 10000
j = 1000
k = 100
i循環結構指令數量 = 4 + i * 3 = 30004
j循環結構指令數量 = 4 + j * 3 = 3004
k循環結構指令數量 = 4 + k * 3 = 304
i循環結構被循環次數 = 1
j循環結構被循環次數 = i
k循環結構被循環次數 = i * j
整個結構指令數量 = i循環結構指令數量 * i循環結構被循環次數 + j循環結構指令數量 * j循環結構被循環次數 + k循環結構指令數量 * k循環結構被循環次數
整個結構指令數量 = 30004 * 1 + 3004 * 10000 + 304 * 1000 * 10000 = 3070070004

 

大循環在內

  同上,咱們也能夠計算出大循環在內的整個三層循環所須要執行的指令數量,以下:blog

i = 100
j = 1000
k = 10000
i循環結構指令數量 = 4 + i * 3 = 304
j循環結構指令數量 = 4 + j * 3 = 3004
k循環結構指令數量 = 4 + k * 3 = 30004
i循環結構被循環次數 = 1
j循環結構被循環次數 = i
k循環結構被循環次數 = i * j
整個結構指令數量 = i循環結構指令數量 * i循環結構被循環次數 + j循環結構指令數量 * j循環結構被循環次數 + k循環結構指令數量 * k循環結構被循環次數
整個結構指令數量 = 304 * 1 + 3004 * 100 + 30004 * 100 * 1000 = 3000700704

 

結論

  能夠很清楚得看出來,大循環在內所須要執行的指令數量 < 大循環在外所需執行的指令數量。表示在嵌套循環中,把大循環放入內層比把大循環放入外層的代碼要高。而爲何會這樣,咱們能夠經過數學進行計算,以下:get

假設 X1,X2,X3,X4,X5,...,Xn都爲正整數,他們表明着循環次數,而且 0 < X1 < X2 < X3 < X4 < X5 < ..... < Xn
大循環在外的狀況
第n層(最內層)的循環結構所須要執行的指令次數爲: (4 + 3X1)X2X3X4X5...Xn
第n-1層循環結構所須要執行的指令次數爲: (4 + 3X2)X3X4X5...Xn
第n-2層循環結構所須要執行的指令次數爲: (4 + 3X3)X4X5...Xn
....................
第2層循環結構所須要執行指令次數爲: (4 + 3Xn-1)Xn
第1層循環結構所須要執行的指令次數爲: (4 + 3Xn)
總指令數爲編譯器

ALL1 = (4 + 3X1)X2X3X4X5...X+ (4 + 3X2)X3X4X5...X+ (4 + 3X3)X4X5...X+ (4 + 3X4)X5...X+...+ (4 + 3Xn-1)X+ (4 + 3Xn)數學

ALL1 = 4X2X3X4X5...Xn + 3X1X2X3X4X5...Xn + 4X3X4X5...Xn + 3X2X3X4X5...Xn + 4X4X5...Xn + 3X2X3X4...Xn + 4X5...Xn + 3X4X5...Xn +...+ 4Xn + 3Xn-1Xn + 4 + 3Xnit

合併同類項後,得

ALL1 = 4 + 3X1X2X3X4X5...Xn + 7X2X3X4X5...Xn + 7X3X4X5...Xn + 7X4X5...Xn +7X5...Xn + ... + 7Xn-1Xn + 7Xn

 

大循環在內的狀況
第n層(最內層)的循環結構所須要執行的指令次數爲: (4 + 3X n)X n-1X n-2X n-3X n-4...X 1
第n-1層循環結構所須要執行的指令次數爲: (4 + 3X n-1)X n-2X n-3X n-4...X 1
第n-2層循環結構所須要執行的指令次數爲: (4 + 3X n-2)X n-3X n-4...X 1
....................
第2層循環結構所須要執行指令次數爲: (4 + 3X 2)X 1
第1層循環結構所須要執行的指令次數爲: (4 + 3X 1)

總指令數爲

ALL2 = (4 + 3Xn)Xn-1Xn-2Xn-3Xn-4...X+ (4 + 3Xn-1)Xn-2Xn-3Xn-4...X+ (4 + 3Xn-2)Xn-3Xn-4...X+ (4 + 3Xn-3)Xn-4...X+...+ (4 + 3X2)X+ (4 + 3X1)

ALL2 = 4X1X2X3X4...Xn-1 + 3X1X2X3X4X5...Xn + 4X1X2X3...Xn-2 + 3X1X2X3X4...Xn-1 + 4X1X2...Xn-3 + 3X1X2X3...Xn-2 + 4X1...Xn-4 + 3X1X2...Xn-3 +...+ 4X1 + 3X2X1 + 4 + 3X1

合併同類項後,得

ALL2 = 4 + 3X1X2X3X4X5...Xn + 7X1X2X3X4...Xn-1 + 7X1X2X3...Xn-2 + 7X1X2...Xn-3 +7X1...Xn-4 + ... + 7X2X1 + 7X1

 

結果
大循環在外的總指令數爲ALL1,大循環在內的總指令數爲ALL2,咱們用ALL1的每一項除以ALL2中對應的每一項,結果爲R,以下

R1 = 4 / 4 = 1

R2 = 3X1X2X3X4X5...Xn / 3X1X2X3X4X5...Xn = 1

R3 = 7X2X3X4X5...Xn / 7X1X2X3X4...Xn-1 = Xn / X1 > 1

R4 = 7X3X4X5...Xn / 7X1X2X3...Xn-2 = XnXn-1 / X1X2 > 1

R5 = 7X4X5...Xn / 7X1X2...Xn-3 = XnXn-1Xn-2 / X1X2X3 > 1

R6 = 7X5...Xn / 7X1...Xn-4 = XnXn-1Xn-2Xn-3 / X1X2X3X4 > 1

......

Rm-1 = 7Xn-1Xn / 7X2X1 > 1

Rm = 7Xn / 7X1 > 1

 

  從以上結果能夠很明顯的看出,除了ALL1和ALL2公有項4,3X1X2X3X4X5...Xn相除爲1,其餘ALL1的每一項對應除以ALL2的每一項,結果R都大於1,說明ALL1中的每一項都大於ALL2中對應的每一項,即說明了ALL1 > ALL2 ,同時也證實了大循環在外所須要執行的指令數量大於大循環在內所須要執行的指令數量,也就是將大循環放在內層時比大循環放在外層的循環效率要高的。

相關文章
相關標籤/搜索