这是一篇在网上看到的技术文章,它把一个原来需要6000毫秒的代码段优化到400毫秒,这种优化效果让我震撼,所以我决定把它翻译共享出来。
下面是原文链接:http://www.codeproject.com/Articles/381630/Code-optimization-tutorial-Part-1
简介:
这篇文章是尝试把代码优化技术介绍给软件开发者。为些,我们将探究各种优化的方法。
第一步,我选取一段容易理解的算法代码段,并在上面运用各种不同的优化方法。
我们要解决的问题是3n+1猜想(关于这个问题的详细解释大家可以点这个链接 3n+1问题详细),
我们执行一个从1到1000000的循环,每个数都套用这个函数:
直到n变成1,我们再执行下一个数,我们不需要从键盘上读取任何数据程序就可以打印结果,并且我们可以计算出运行的时间,用来做测试的设备是: AMD Athlon 2 P340 Dual Core 2.20 GHz, 4 GB of RAM, Windows 7 Ultimate x64。测试的语言是:C#和C++
代码的第一个版本:为1到1000000中的每个数执行上面的算法,算法会产生一系列的数字,直到等于1时才会停止,归1的步骤数将会被记录下来,并且最大的步骤数将会被确定下来。
C++代码段:
for (int i = nFirstNumber; i < nSecondNumber; ++i) { int nCurrentCycleCount = 1; long long nNumberToTest = i; while (nNumberToTest != 1) { if ((nNumberToTest % 2) == 1) { nNumberToTest = nNumberToTest * 3 + 1; } else { nNumberToTest = nNumberToTest / 2; } nCurrentCycleCount++; } if (nCurrentCycleCount > nMaxCycleCount) { nMaxCycleCount = nCurrentCycleCount; } }C#代码段:
for (int i = FirstNumber; i < SecondNumber; ++i) { int iCurrentCycleCount = 1; long iNumberToTest = i; while (iNumberToTest != 1) { if ((iNumberToTest % 2) == 1) { iNumberToTest = iNumberToTest * 3 + 1; } else { iNumberToTest = iNumberToTest / 2; } iCurrentCycleCount++; } if (iCurrentCycleCount > MaxCycleCount) { MaxCycleCount = iCurrentCycleCount; } }
我用Debug和Release模式以及32位和64位分别执行了这段代码,然后我每次都运行100次取平均值(单位是ms)。下面是运行的结果:
C++ Debug | C++ Release | C#Debug | C# Release | |
x86 version | 6882 | 6374 | 6358 | 5109 |
x64 versiong | 1020 | 812 | 1890 | 742 |
我们从表里面看到的第一点是,32位的程序要比64位的慢5到7倍,因为x64设计是一个寄存器可以存储一个long long数据,而x86而需要两个寄存器,所以在后面我们不再研究32位的程序。第二点是,Debug和Release所用的时间差距很大,特别是C#。第三点是,通过观察,C#编译器貌似比C++编译器在优化方面更胜一筹。
我提供的第一个优化动作是与数学操作符有关的,我用非传统的方式替换传统的方式。我们可以看到上面的代码里面只有三个复杂的数学操作符:%,*,/。首先我要优化的是%2操作,我们发现只要与0x1做&操作就可以判断一个数是否是奇数。所以当我们执行下面的替换时:
我提供的第一个优化动作是与数学操作符有关的,我用非传统的方式替换传统的方式。我们可以看到上面的代码里面只有三个复杂的数学操作符:%,*,/。首先我要优化的是%2操作,我们发现只要与0x1做&操作就可以判断一个数是否是奇数。所以当我们执行下面的替换时:
if ((nNumberToTest % 2) == 1)替换成:
if ((nNumberToTest & 0x1) == 1)下面是新的结果表:
C++ Debug | C++ Release | C# Debug | C# Relase |
922 | 560 | 1641 | 714 |
我们发现,C++ Release版本得到了很大的优化,Release和Debug版本的优化程序不同让我相信,在Release版本下编译器会删除多余的指令。C#则好像没有太大的优化。下一个优化的动作是/2操作,我们发现用位操作>>1与/2操作得到的是相同的效果,
所以当我们执行下面的替换时:
nNumberToTest = nNumberToTest / 2;
替换成:
nNumberToTest = nNumberToTest >> 1;
下面是新的结果:
C++ Debug | C++ Release | C# Debug | C# Relase |
821 | 555 | 1432 | 652 |
我们发现C++ Debug, C# Debug, C# Release有差不多65到200毫秒的优化,而C++ Release版本则好像没有优化一样,那是因为在这种模式下编译器已经做了这个转换操作。最我们能做的变换只能是把*换成+操作,
所以当我们执行下面的替换时:
nNumberToTest = nNumberToTest * 3 + 1;
替换成:
nNumberToTest = nNumberToTest + nNumberToTest + nNumberToTest + 1;
下面是新的结果:
C++ Debug | C++ Release | C# Debug | C# Relase |
820 | 548 | 1535 | 629 |