C++编译器到底能帮我们把代码优化到什么程度?

原创 2012年01月02日 01:38:49

TODO: 用while写法的程序会不会循环展开?

本文地址:http://blog.csdn.net/hengyunabc/article/details/7170865

一个简单的累加求和程序:

	TYPE S=0;  
	for(int i = 0;i < SIZE; i++) {  
		S += a[i];  
	}


很多人都觉得这个程序写得不好,编译器不能生成很好的汇编代码。于是有了以下的几种“优化”:

#include <iostream>
using namespace std;

void main(int argc,char **argv)
{
#define TYPE int
#define SIZE 10000

	TYPE* a=new TYPE[SIZE];  
	for(int i = 0; i<SIZE; ++i){
		a[i] = i;
	}
	//求和,通常版本
	TYPE S=0;  
	for(int i = 0;i < SIZE; i++) {  
		S += a[i];  
	}
	cout<<S<<endl;

	TYPE S2 = 0;
	//版本1:认为中间产生的变量i是多余的,改为用移动指针
	TYPE* end = a + SIZE;
	for( ; a != end; ) {  
		S2 += *(a++);  
	}
	cout<<S2<<endl;

	//版本1中把a移到了数组的最后,现在移回原来的位置
	a = end - SIZE;

	//版本2:认为循环次数太多了,可以改为减少循环次数
	TYPE S3 = 0;
	for(int i = 0; i < SIZE; ){ //仅当SIZE为偶数时
		S3 += a[i++];
		S3 += a[i++];
	}
	cout<<S3<<endl;

	//版本3:认为版本2中会使CPU不能乱序执行,降低了效率,应该改为汇编,把中间结果放到独立的寄存器中
	//谢谢 menzi11 的文章,让我认识到程序中相关的数据会让CPU不能乱序执行。
	//这里用伪汇编代替
	TYPE S4 = 0;

	register TYPE r1 = 0;
	register TYPE r2 = 0;
	for(int i = 0; i < SIZE; ){ //仅当SIZE为偶数时
		r1 += a[i++];
		r2 += a[i++];
	}

	cout<<r1 + r2<<endl;
}

上面的几种版本都合理,但是这些优化都是建立在编译器不能生成高效的汇编代码的假设上的。

下面来看下编译器生成的结果(vs2010,release):

	for(int i = 0;i < SIZE; i++) {  
		S += a[i];  
013B1040  mov         ebx,dword ptr [eax+4]  //把a[0],a[4],a[8]...累加到ebx中
013B1043  add         ecx,dword ptr [eax-8]  //把a[1],a[5],a[9]...累加到ecx中
013B1046  add         edx,dword ptr [eax-4]  //把a[2],a[6],a[10]...累加到edx中
013B1049  add         esi,dword ptr [eax]    //把a[3],a[7],a[11]...累加到esi中
013B104B  add         dword ptr [ebp-4],ebx  
013B104E  add         eax,10h  
013B1051  dec         dword ptr [ebp-8]  
013B1054  jne         main+40h (13B1040h)  
	}
	cout<<S<<endl;
013B1056  mov         eax,dword ptr [ebp-4]  
013B1059  add         eax,esi  		      
013B105B  add         eax,edx  
013B105D  mov         edx,dword ptr [__imp_std::endl (13B204Ch)]  
013B1063  add         ecx,eax                //上面的3条add指令把ebx,ecx,edx,edi都加到ecx中,即ecx是累加结果

可见编译器生成的代码是最好的代码,消灭了中间变量i,减少了循环次数,消灭了会造成CPU不能乱序执行的因素。


BTW:

有人可能会有疑问:要是size不是偶数,编译器能生成类似的高效汇编代码不?

当SIZE = 9999时:

//当SIZE = 9999时,编译器把中间结果放到三个寄存器中,perfect
	for(int i = 0;i < SIZE; i++) {  
		S += a[i];  
01341030  add         ecx,dword ptr [eax-8]  
01341033  add         edx,dword ptr [eax-4]  
01341036  add         esi,dword ptr [eax]  
01341038  add         eax,0Ch  
0134103B  dec         ebx  
0134103C  jne         main+30h (1341030h)  
	}

当SIZE  = 9997 时:

//当SIZE = 9997时,有点复杂,先把a[0]到a[9995]累加到ecx和edx中
//再把a[9996]入到edi中,最后把ecx,edi都加到edx中
//edx压栈,调用operator<< 函数
	for(int i = 0;i < SIZE; i++) {  
00D31024  xor         eax,eax  
		S += a[i];  
00D31026  add         ecx,dword ptr [esi+eax*4]  
00D31029  add         edx,dword ptr [esi+eax*4+4]  
00D3102D  add         eax,2  
00D31030  cmp         eax,270Ch  
00D31035  jl          main+26h (0D31026h)  
	for(int i = 0;i < SIZE; i++) {  
00D31037  cmp         eax,270Dh  
00D3103C  jge         main+41h (0D31041h)  
		S += a[i];  
00D3103E  mov         edi,dword ptr [esi+eax*4]  
	}
	cout<<S<<endl;
00D31041  mov         eax,dword ptr [__imp_std::endl (0D3204Ch)]  
00D31046  add         edx,ecx  
00D31048  mov         ecx,dword ptr [__imp_std::cout (0D32050h)]  
00D3104E  push        eax  
00D3104F  add         edx,edi  
00D31051  push        edx  
00D31052  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0D32048h)]  

上面的分析都是SIZE,即数组的大小是已知情况下,那个数组大小是未知情况下,编译器又会怎样?

TYPE mySum(TYPE* a, int size){
	TYPE s = 0;
	for(int i = 0; i < size; ++i){
		s += a[i];
	}
	return s;
}

生成的汇编代码:

//先累加a[0] 到 a[size-2]
	TYPE s = 0;
00ED100C  xor         esi,esi  
	for(int i = 0; i < size; ++i){
00ED100E  xor         eax,eax  
00ED1010  cmp         ebx,2  
00ED1013  jl          mySum+27h (0ED1027h)  
00ED1015  dec         ebx  
		s += a[i];
00ED1016  add         ecx,dword ptr [edi+eax*4]    //a[0],a[2],a[4]...加到ecx中
00ED1019  add         edx,dword ptr [edi+eax*4+4]  //a[1],a[3],a[5]...加到edx中
00ED101D  add         eax,2  
00ED1020  cmp         eax,ebx  
00ED1022  jl          mySum+16h (0ED1016h)  
00ED1024  mov         ebx,dword ptr [size]  
	for(int i = 0; i < size; ++i){
00ED1027  cmp         eax,ebx                       //判断最后一个元素有没有加上
00ED1029  jge         mySum+2Eh (0ED102Eh)  
		s += a[i];
00ED102B  mov         esi,dword ptr [edi+eax*4]     //当size是奇数是会执行,偶数时不会执行
00ED102E  add         edx,ecx  
	}


总结:C++的编译器生成的汇编代码在绝大多数情况下都和人写出的最好的汇编代码相当。

关键的一点是编译器会不断升级,适应新的cpu指令,体系等,手写的汇编代码则通常悲剧了。

知道编译器能优化到什么程度,编译器到底怎样优化,是程序员很重要的素质。


版权声明:本文为博主原创文章,未经博主允许不得转载。

给开源编译器插入后门

原文地址:https://ring0.me/2014/11/insert-backdoor-into-compiler/ 说起 Ken Thompson,我们首先想到的是他发明的 UNIX ...
  • Man_OC
  • Man_OC
  • 2015年09月21日 15:09
  • 1112

让你的代码帮你写代码--编译期注解

编译期注解的项目实践
  • hjceo
  • hjceo
  • 2016年10月22日 18:31
  • 166

C++编译器到底能帮我们把代码优化到什么程度?

一个简单的累加求和程序: 01.TYPE S=0; 02.for(int i = 0;i 03. S += a[i]; 04.} 很多人都觉得这个程序写得不好,编译器不能生成很好的汇编代码...
  • xumaojun
  • xumaojun
  • 2012年04月07日 10:59
  • 274

用C++进行开发要学到什么程度

  • jisucpu
  • jisucpu
  • 2010年05月31日 13:27
  • 1497

让VC不再编译帮助文件的方法

让VC不再编译帮助文件的方法 VC帮助虽然很好,但有时很烦,每一次编译都会自动编译帮助文件,不理我们有没有修改过RTF文件,耗神又耗时间,实在让人烦。怎么弄掉这帮助呢?上网上去问,没人回答。在Proj...
  • kind_li
  • kind_li
  • 2003年02月26日 09:47
  • 1299

android 中的一些资源注解,让编译器帮你检查代码

android 中的一些资源注解,让编译器帮你检查代码 写方便的时候可以用注解来声明一些参数,以明确的指示参数的类型,让代码更安全。我们看到,在android源代码里大量使用了注解。我整理了一些...
  • qq_34884729
  • qq_34884729
  • 2016年08月25日 14:26
  • 117

浅析代码优化——编译器优化原理

开篇 相信有过编码经验的人都知道,程序的正常运行,只是最基本的要求。更多的,还要考虑程序的性能,运行效率,组织结构,和重用性等等。 今天将简单的讨论一下如何优化程序性能。 要写出高效的程序,可能...
  • John_ToStr
  • John_ToStr
  • 2016年08月25日 14:57
  • 897

机器学习到底学到了什么

在上一篇文章中,我介绍了一个关于在应用中结合机器学习的小案例,并阐明了一个观点:可以利用机器学习模拟人类的思维方式去处理问题。很多读者反馈只是看到了几个专业名词,却依然不知道机器学习在学什么,今天我就...
  • lxf_44944
  • lxf_44944
  • 2017年03月30日 17:18
  • 618

C++编译器到底能帮我们把代码优化到什么程度?

本文地址:http://blog.csdn.net/hengyunabc/article/details/7170865 一个简单的累加求和程序: TYPE S=0; for(int i = ...
  • hengyunabc
  • hengyunabc
  • 2012年01月02日 01:38
  • 3728

修改Go语言(golang)编译器源代码让它支持UTF-8 BOM

Go语言(golang)第一个正式版Go1发布了,但是这个新兴的编程语言还是非常不完善。这不,我(Liigo)又发现它的编译器竟然不支持编译带BOM的UTF-8编码的.go源文件。这就很奇怪,该语言明...
  • liigo
  • liigo
  • 2012年04月16日 22:34
  • 26544
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++编译器到底能帮我们把代码优化到什么程度?
举报原因:
原因补充:

(最多只允许输入30个字)