关于嵌套循环效率研究

本文为原创,转载请注明:http://www.cnblogs.com/tolimit/

引言

  大家都知道,当进行嵌套循环时,大循环放最外面和放最里面所造成的执行效率会不同,本篇文章会通过汇编代码进行分析具体情况。

测试环境

  • 操作系统: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命令,获取其汇编代码,如下:

  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

大循环在外

  好的,根据以上所得的结论,我们可以很轻松的计算出大循环在外的整个三层循环所需要执行的指令数量,如下:

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

大循环在内

  同上,我们也可以计算出大循环在内的整个三层循环所需要执行的指令数量,如下:

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

结论

  可以很清楚得看出来,大循环在内所需要执行的指令数量 < 大循环在外所需执行的指令数量。表示在嵌套循环中,把大循环放入内层比把大循环放入外层的代码要高。而为什么会这样,我们可以通过数学进行计算,如下:

假设 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 + 3Xn

合并同类项后,得

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

大循环在内的情况
第n层(最内层)的循环结构所需要执行的指令次数为: (4 + 3Xn)Xn-1Xn-2Xn-3Xn-4...X1
第n-1层循环结构所需要执行的指令次数为: (4 + 3Xn-1)Xn-2Xn-3Xn-4...X1
第n-2层循环结构所需要执行的指令次数为: (4 + 3Xn-2)Xn-3Xn-4...X1
....................
第2层循环结构所需要执行指令次数为: (4 + 3X2)X1
第1层循环结构所需要执行的指令次数为: (4 + 3X1)

总指令数为

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 +7X5...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 ,同时也证明了大循环在外所需要执行的指令数量大于大循环在内所需要执行的指令数量,也就是将大循环放在内层时比大循环放在外层的循环效率要高的。

时间: 2024-10-11 10:29:02

关于嵌套循环效率研究的相关文章

C# json提取多层嵌套到数组-- C# json 数组

json比一般格式再复杂点的就像数组一样,有多层嵌套,研究了一下,记录代码如下: string jsonText = "{'name':'test','phone':'18888888888','image':[{'name':'img1','data':'data1'},{'name':'img2','data':'data2'},{'name':'img3','data':'data3'}]}"; JObject jo = (JObject)JsonConvert.Deserial

(转)as3效率优化

1.改进算法无论对于那一种程序,好的算法总是非常重要的,而且能够极大地提高程序性能,所以任何性能的优化第一步就是从算法或者说程序逻辑的优化开始,检查自己的程序是否有多余的运算,是否在没有必要的时候做了无用功,往往从这些方面就能找到那些导致性能低下的地方. 2.优化细节代码针对细节总是好的,有一些小技巧比如:用 var obj:Object = {}; 要比 var obj:Object = new Object();要好:var arr:Array = []; 要比 var arr:Array

as3效率优化

1.改进算法无论对于那一种程序,好的算法总是非常重要的,而且能够极大地提高程序性能,所以任何性能的优化第一步就是从算法或者说程序逻辑的优化开始,检查自己的程序是否有多余的运算,是否在没有必要的时候做了无用功,往往从这些方面就能找到那些导致性能低下的地方. 2.优化细节代码针对细节总是好的,有一些小技巧比如:用 var obj:Object = {}; 要比 var obj:Object = new Object();要好:var arr:Array = []; 要比 var arr:Array

嵌套for循环效率问题

方法一 for(int i=0;i<20;i++){ for(int j=0;j<200;j++){ System.out.println("第"+i+"次时间:"+new Date().toString()); } } 方法二 int i=0: int j=0: for(;i<20;i++){ for(;j<200;j++){ System.out.println("第"+i+"次时间:"+new D

深度理解div+css布局嵌套盒子

1. 网页布局概述 网页布局的概念是把即将出现在网页中的所有元素进行定位,而CSS网页排版技术有别于传统的网页排版方法,它将页面首先在整体上使用<div>标记进行分块,然后对每个快进行CSS定位以及设置显示效果,最后在每个块中添加相应的内容.利用CSS排版方法更容易地控制页面每个元素的效果,更新也更容易,甚至页面的拓扑结构也可以通过修改相应的CSS属性来重新定位.  2. 盒子模型 盒子模型是CSS控制页面元素的一个重要概念,只有掌握了盒子模型,才能让CSS很好地控制页面上每一个元素,达到我们

Windwos Hyper-v嵌套虚拟化

现在微软的虚拟化越来越成熟,许多人也渐渐的开始研究微软的Hyper-V的虚拟化,但是苦于没有太多的电脑提供部署环境,再加上之前的windwos版本是不支持在虚拟机中安装Hyper-V,让大家头大. 现在的win10和win server 2016都已经支持嵌套虚拟化,也就是说可以在虚拟机中安装Hyper-V. 首先当然是安装Hyper-V,安装过程就省略了,相信大家肯定都知道. 安装完Hyper-V后新建一台虚拟机.在这里我建立一台名为test的虚拟机. 然后以管理员方式运行powershell

从ViewPager嵌套RecyclerView再嵌套RecyclerView看安卓事件分发机制

这两天伟大的PM下了一个需求,在一个竖滑列表里实现一个横向滑动的列表,没错,又是这种常见但是又经常被具有着强烈责任心和职业操守程序员所嗤之以鼻的效果,废话不多说,先上图: 实现的方式很多,因为项目中已经ViewPager+RV实现基本框架,所以现我也选择再添加一个RV实现相应的效果. 不过在写代码之前,先预估一下这个效果所有的坑. VP是横向滑动的,RV是竖向滑动的,那么现在再添加一个横向滑动的RV,肯定会有滑动冲突,主要表现在 VP和横向滑动RV 的冲突,因为两者都是横向滑动的,肯定有冲突,无

尽量少嵌套无用的div;外部文件尽量使用link而不要使用用@import

最近的工作又学到了很多东西,在这里记录一下. 1,尽量少嵌套无用的div,这个问题领导很严肃的跟我提过很多次,因为我很喜欢用很多div,而且有很多div都是无存在意义的.后来领导给了我一些资料,我看了一下,发现这样做确实存在很大的问题,原因如下:(以下蓝底文字摘自搜狐WEB标准) 2.1 节约运营成本 采用 WEB 标准制作,我们可以做到表现和形式及内容的分离,我们采用XHTML 语言来表现(数据),用CSS 来控制(页面元素呈现的)形式.写的好的页面,XHTML 代码中基本上都是用户要看的数据

深入研究虚幻4反射系统实现原理(一)

上一篇翻译的文章里面提到了UE4反射系统的基本原理与应用,这次我们通过代码来深入研究一下UE4的反射系统,因为反射系统在UE4中牵扯的东西较多,所以我打算分几篇文章分析.我这里假定读者对UE4有一定的了解并且有一定的C++基础,如果不了解UE4如何使用,那么请先学会如何使用UE4引擎,否则看起来可能会比较困难. 以下是我整理的一个跟反射系统相关的类图: 从上面可以看出UObject是整个反射系统核心,UE4中支持反射的类型在上一篇文章中已经说过,包括 C++类.结构体.函数 .成员变量以及枚举,