- 浏览: 1386500 次
- 性别:
- 来自: 火星
文章分类
最新评论
-
aidd:
内核处理time_wait状态详解 -
ahtest:
赞一下~~
一个简单的ruby Metaprogram的例子 -
itiProCareer:
简直胡说八道,误人子弟啊。。。。谁告诉你 Ruby 1.9 ...
ruby中的类变量与类实例变量 -
dear531:
还得补充一句,惊群了之后,数据打印显示,只有一个子线程继续接受 ...
linux已经不存在惊群现象 -
dear531:
我用select试验了,用的ubuntu12.10,内核3.5 ...
linux已经不存在惊群现象
我的gcc版本是4.4.1
先来看const和define以及enum定义,编译器会做什么样的优化:
ok,我们然后来看对应的汇编代码(我这里用O2编译):
1 可以看到编译器会将它们的值直接计算然后再调用函数a,也就是+那一步被编译器优化掉了。
2 没有static修饰的const变量的那块内存被保留了,尽管foo并没有引用这块内存。
接下来看宏和内联,编译器会做什么不同的优化:
先看测试代码:
然后来看汇编代码。这里只是汇编代码·片断:
bar和foo2的代码完全一样,由此可见bar函数自动被内联了。
下面这个测试我用的gcc是4.3
现在我们来去掉static,然后来看会出现什么情况,源码:
来看汇编:
可以看到依然会被内联,可是和没有加static相比,会生成abs2这个段.这是因为编译器要做最保守的处理,如果不加static的话,abs2可能还被其他的文件所调用,因此会保留这个abs2.
这里来做个总结吧.实验代码就不贴了,有兴趣可以自己去试试.
1 用O3编译,函数加不加static ,都会被内联.
2 如果函数被调用一次,并且函数体很大
如果为static修饰的函数,他无论如何都会被内联.
如果不加static的话,就需要你强制O3优化了,用O3也会生成abs2那个段.如果O2编译,则会生成跳转指令.
3 如果函数被调用多次.
加不加static修饰的函数,调用次数超过一定的数值都会生成跳转指令.
因此我们一个文件内的内部函数尽量都声明为static的。
接下来来看数组的边界检测,编译器会如何优化:
然后来看对应的汇编代码,其中L6表示第一个赋值,L7为第二个赋值:
可以看到生成了相同的代码,边界检测被编译器优化了(也就是删除掉了).也就是循环的时候(如果我们for循环的边界检测刚好包含本身的边界检测的话,我们不需要多余的边界检测.)如果将for循环的边界检测改为大于100000的话,我们就会看到,编译器会生成相应的边界检测的.
汇编代码(只看write_to那部分):
可以看到当for的边界检测包含write_to的边界的时候就会生成这段.
再来看另外的一些边界检测优化:
来看regular的汇编代码:
可以看到i>5 && i<100被优化为 i-6 <93。并且编译器知道exit不会有返回值,因此还会设置正确的返回值。
接下来来看for和while,编译器会有什么不一样的处理:
来看生成的汇编:
可以看到完全一模一样,我记得在老的版本的gcc中,for和while生成的汇编是不一样的。
还有gcc的switch优化为跳转表可以看我前面的blog.
还有一些就是数值计算的优化了,不过这里就不介绍了。
先来看const和define以及enum定义,编译器会做什么样的优化:
enum { constant=23 }; #define CONSTANT 23 static const int Static_Constant=23; const int Constant = 23; int foo() { a(constant+3); a(CONSTANT+4); a(Static_Constant+5); return Static_Constant + Constant; }
ok,我们然后来看对应的汇编代码(我这里用O2编译):
foo: pushl %ebp movl %esp, %ebp subl $24, %esp //constant+3=26 movl $26, (%esp) call a ///CONSTANT+4=27 movl $27, (%esp) call a ///Static_Constant+5=28 movl $28, (%esp) call a ///Static_Constant + Constant = 23 + 23 = 46 movl $46, %eax leave ret .size foo, .-foo ....................................... ///被保留。 Constant: .long 23 .ident "GCC: (Ubuntu 4.4.1-4ubuntu8) 4.4.1"
1 可以看到编译器会将它们的值直接计算然后再调用函数a,也就是+那一步被编译器优化掉了。
2 没有static修饰的const变量的那块内存被保留了,尽管foo并没有引用这块内存。
接下来看宏和内联,编译器会做什么不同的优化:
先看测试代码:
#define abs(x) ((x)>0?(x):-(x)) static long abs2(long x) { return x >= 0 ? x : -x; } long foo2(long a) { return abs(a); } long bar(long a) { return abs2(a); }
然后来看汇编代码。这里只是汇编代码·片断:
foo2: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax popl %ebp movl %eax, %edx sarl $31, %edx xorl %edx, %eax subl %edx, %eax ret .size foo2, .-foo2 .p2align 4,,15 .globl bar .type bar, @function bar: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax popl %ebp movl %eax, %edx sarl $31, %edx xorl %edx, %eax subl %edx, %eax ret .size bar, .-bar .p2align 4,,15
bar和foo2的代码完全一样,由此可见bar函数自动被内联了。
下面这个测试我用的gcc是4.3
现在我们来去掉static,然后来看会出现什么情况,源码:
long abs2(long x) { return x >= 0 ? x : -x; } long foo2(long a) { return abs(a); } long bar(long a) { return abs2(a); }
来看汇编:
abs2: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax popl %ebp movl %eax, %edx sarl $31, %edx xorl %edx, %eax subl %edx, %eax ret bar: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax popl %ebp movl %eax, %edx sarl $31, %edx xorl %edx, %eax subl %edx, %eax ret
可以看到依然会被内联,可是和没有加static相比,会生成abs2这个段.这是因为编译器要做最保守的处理,如果不加static的话,abs2可能还被其他的文件所调用,因此会保留这个abs2.
这里来做个总结吧.实验代码就不贴了,有兴趣可以自己去试试.
1 用O3编译,函数加不加static ,都会被内联.
2 如果函数被调用一次,并且函数体很大
如果为static修饰的函数,他无论如何都会被内联.
如果不加static的话,就需要你强制O3优化了,用O3也会生成abs2那个段.如果O2编译,则会生成跳转指令.
3 如果函数被调用多次.
加不加static修饰的函数,调用次数超过一定的数值都会生成跳转指令.
因此我们一个文件内的内部函数尽量都声明为static的。
接下来来看数组的边界检测,编译器会如何优化:
static char array[100000]; static int write_to(int ofs,char val) { ///检测边界(这个会被优化掉) if (ofs>=0 && ofs<100000) array[ofs]=val; } int main() { int i; for (i=0; i<100000; ++i) array[i]=0;//<这里没有检测边界 for (i=0; i<100000; ++i) write_to(i,-1); }
然后来看对应的汇编代码,其中L6表示第一个赋值,L7为第二个赋值:
.L6: movl $0, array(%eax) addl $4, %eax cmpl $100000, %eax jne .L6 xorl %eax, %eax .p2align 4,,7 .p2align 3 .L7: movl $-1, array(%eax) addl $4, %eax cmpl $100000, %eax ///可以看到边界检测被优化掉了 jne .L7 popl %ebp ret .size main, .-main .p2align 4,,15
可以看到生成了相同的代码,边界检测被编译器优化了(也就是删除掉了).也就是循环的时候(如果我们for循环的边界检测刚好包含本身的边界检测的话,我们不需要多余的边界检测.)如果将for循环的边界检测改为大于100000的话,我们就会看到,编译器会生成相应的边界检测的.
static char array[100000]; static int write_to(int ofs,char val){ if(ofs>=0&&ofs<99999) array[ofs]=val; } int main(){ int i; for(i=0;i<99998;++i)array[i]=0; for(i=1;i<100000;++i)write_to(i,-1); }
汇编代码(只看write_to那部分):
.L6: movb $-1, array(%eax) addl $1, %eax cmpl $100000, %eax je .L4 cmpl $99999, %eax jne .L6 .L4: popl %ecx popl %ebp leal -4(%ecx), %esp
可以看到当for的边界检测包含write_to的边界的时候就会生成这段.
再来看另外的一些边界检测优化:
int regular(int i) { ///这里判断i的边界。 if (i>5 && i<100) return 1; exit(0); }
来看regular的汇编代码:
regular: pushl %ebp movl %esp, %ebp subl $24, %esp movl 8(%ebp), %eax ///先将i减去6 subl $6, %eax ///然后和93比较 cmpl $93, %eax ja .L18 movl $1, %eax leave ret .L18: ///设置返回值为0 movl $0, (%esp) call exit
可以看到i>5 && i<100被优化为 i-6 <93。并且编译器知道exit不会有返回值,因此还会设置正确的返回值。
接下来来看for和while,编译器会有什么不一样的处理:
char array[100000]; int foo5(int a) { int i; for (i=1; i<a; i++) array[i]=array[i-1]+1; } void foo6(int a) { int i =1; while (i<a) { array[i]=array[i-1]+1; i++; } }
来看生成的汇编:
foo5: pushl %ebp movl %esp, %ebp movl 8(%ebp), %ecx pushl %ebx cmpl $1, %ecx jle .L23 movzbl array, %ebx movl $1, %eax foo6: pushl %ebp movl %esp, %ebp movl 8(%ebp), %ecx pushl %ebx cmpl $1, %ecx jle .L29 movzbl array, %ebx movl $1, %eax
可以看到完全一模一样,我记得在老的版本的gcc中,for和while生成的汇编是不一样的。
还有gcc的switch优化为跳转表可以看我前面的blog.
还有一些就是数值计算的优化了,不过这里就不介绍了。
评论
2 楼
lurker0
2010-02-13
》》可以看到i>5 && i<100被优化为 i-6 <93。
如果i为99 左侧条件为真,右侧条件为假,就不等价。
这里是不是有问题?
如果i为99 左侧条件为真,右侧条件为假,就不等价。
这里是不是有问题?
1 楼
seen
2010-02-03
>>可以看到i>5 && i<100被优化为 i-6 <93
从数学上来说是说不通的。
这里有点意思。。。以前不知道还有这一手。。。在CF上玩了点小花样
从数学上来说是说不通的。
这里有点意思。。。以前不知道还有这一手。。。在CF上玩了点小花样
发表评论
-
gdb学习笔记(一)
2009-10-17 14:11 11663这里只是一个摘要。具体的细节还需要去看manual。 1 ... -
ydb的内存模型
2009-09-06 18:02 1904阿宝同学推荐了这个东 ... -
glibc中strlen的实现
2009-08-04 09:10 4479glibc中的strlen的实现主要的思想就是每次检测4个字节 ... -
libevent源码浅析(四)
2009-05-15 23:02 4381最近刚刚一个项目自己用libevent,因此这几天又把libe ... -
libevent源码浅析(三)
2009-03-17 00:08 4510这次我们来看libevent的信号的处理。 在libeven ... -
libevent源码浅析(二)
2009-02-22 00:11 4043我们来看下libevent的定时器的实现 在libevent ... -
libevent源码浅析(一)
2009-02-14 13:23 7348这里分析的是libevent-1.4.9。 PS:前面还看了 ... -
linux下的time处理
2009-01-04 18:02 6789在内核中有3个不同的时间: Wall time(real t ... -
libev简单使用介绍
2008-12-30 09:52 11414更详细的用法请看他的 ... -
linux下的elf结构
2008-12-12 00:20 5135可以看到链接器和加载器看待elf是完全不同的,链接器看到 ... -
php的c扩展
2008-12-07 18:24 4528在php中最核心的一个数据结构就是这个: typedef u ... -
linux下的管理内存相关的函数
2008-11-27 00:56 4420malloc的实现,在linux下的实现是这样的,当所需 ... -
linux下的数据对齐
2008-11-25 12:15 3600数据对齐也就是通过硬件来估算在数据的地址和内存块之间的联系。当 ... -
linux下检测ip冲突
2008-11-16 20:18 8073原理其实很简单,那就是广播一个arp包,然后recv,如果没有 ... -
今天碰到的一个问题
2008-10-29 22:33 1222将位图用 bmptopnm 转成pcl6的打印语言,然后直接c ... -
ftruncate和msync
2008-10-23 22:10 3438int ftruncate(int fd, off_t le ... -
GUN C正则表达式
2008-09-25 23:47 6144最近项目中要处理文本,因此就用了gun的正则表达式,它是pos ... -
看代码看的头晕
2008-09-06 01:04 1815最近工作需要在看ghostscript的代码,看得我头晕眼花, ... -
[转帖]MISRA--作为工业标准的C编程规范
2008-08-21 13:19 2799本文档转贴自孟岩的blog ... -
代码大全读书笔记1
2008-04-26 19:16 3786这么好的书,觉得写点东西,记录一下比较好。 4.1选择编程语 ...
相关推荐
其强大的功能体现在以下几个方面: 2010-10-26 更新 1.程序代码简洁严谨, 整个系统程序仅2M左右大小. 2.中英文双语版共用一套网站程序, 双语页面实现自由切换. 3.GB2312和UTF-8双编码格式, 方便用户使用不同编码...
13.5.4 修改GCC源程序的窥孔优化 练习 第14章 面向对象语言的编译 14.1 面向对象语言的基本概念 14.2 面向对象语言语法结构及语义处理的特征 14.2.1 面向对象语言的类的语法结构及语义 14.2.2 面向对象语言的有效类...
LLVM是Apple官方支持的编译器,而该编译器的前端是Clang,这...我没有上传clang.dll,这个几十M大,自己安装个llvm,到安装目录下 D:\Program Files\LLVM\bin\libclang.dll (这个是我的路径)复制到源码下就可以玩了。
由于实验持续了几个月,包括使用多个编译器进行的广泛测试,我们获得了十万个二进制中间文件,我们可以想象文件的大小,因此我们试图寻找其他方式来上传和共享中间文件文件和实验结果。 BinTuner的体系结构: ...
1. Xcode带了一个自动转换工具,可以迁移源代码至ARC 你可以手动转换源文件 3.你可以在 Xcode中禁用某些文件使用ARC,这点对于第三方库非常有用。 Xcode的自动迁移工具 ARC是LLⅧM3.0编译器的特性,而现有工程可能使用...
我在设计一个状态机,用函数表示每种状态,每个函数都会返回一个指向下一个状态的函数的指针。可我找不到任何方法来声明这样的函数——感觉我需要一个返回指针的函数,返回的指针指向的又是返回指针的函数……,如此...
o 3.7 是否有自动比较结构的方法? o 3.8 如何向接受结构参数的函数传入常数值? o 3.9 怎样从/向数据文件读/写结构? o 3.10 我的编译器在结构中留下了空洞, 这导致空间浪费而且无法与外部数据文件进行 "二进制...
2.22 有没有一种自动方法来跟踪联合的哪个域在使用? 30 枚举 31 2.23 枚举和一组预处理的#define有什么不同? 31 2.24 枚举可移植吗? 31 2.25 有什么显示枚举值符号的容易方法吗? 31 位域 31 2.26 ...
2.7 是否有自动比较结构的方法? . . . . . . . . . . . . . . . . . . . . 8 2.8 如何向接受结构参数的函数传入常数值? . . . . . . . . . . . . . . 8 2.9 怎样从/向数据文件读/写结构? . . . . . . . . . . . ....
然后,当通过 free() 将该指针传递回来时,我们只需要倒退几个内存字节就可以再次找到这个结构。 在讨论分配内存之前,我们将先讨论释放,因为它更简单。为了释放内存,我们必须要做的惟一一件事情就是,获得我们...