GCC 编译试验:引用变量和循环优化
2010-02-27 15:19
399 查看
(1)引用变量
C++中的指针和引用就像两兄弟,他们几乎有着相同的功能,在大多数情况下都可以互相替换。指针可用CPU间接寻址来实现,其执行效率会比寄存器寻址要慢。引用常被说成是一种别名,可是CPU指令系统里并没有引用和别名这类东西,那么它是如何实现的呢?它会比指针更快吗?为搞明白这个问题,我做了一次试验。
对下面这点代码:
void test1(int *i)
{
(*i) ++;
}
void test2(int &i)
{
i ++;
}
先用gcc编译,然后用objdump反编译
$ gcc -O2 -c pointer_and_reference.cpp
$ objdump -d pointer_and_reference.o
得到:
00000000 <_Z5test1Pi>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 8b 45 08 mov 0x8(%ebp),%eax
6: 83 00 01 addl $0x1,(%eax)
; i++
9: 5d pop %ebp
a: c3 ret
b: 90 nop ; 我没有看懂
这两句干什么用的
c: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
00000010 <_Z5test2Ri>:
10: 55 push %ebp
11: 89 e5 mov %esp,%ebp
13: 8b 45 08 mov 0x8(%ebp),%eax
16: 83 00 01 addl $0x1,(%eax)
; i++
19: 5d pop %ebp
1a: c3 ret
除了我没有看懂的两行代码(在ret之后的奇怪指令),其余代码完全一样。可见引用其实就是用指针实现,两者在效率上并没有差别。
(2)循环优化
C语言写循环还是优点麻烦,通常是会这样写:
int i;
for (i = 0; i < 100; i ++)
{
......
}
其实这个循环很简单,就是要里面的语句循环100遍,i=0、i<100、i++这三条语句显得有些累赘。不知道GCC能否明白我的意图,用最高效的方式实现循环呢?
对下面这段程序:
int test_for()
{
int i;
int j=0;
for (i = 0; i < 100; i ++)
j += i + 1;
return j;
}
函数的作用就是返回从1加到100的结果,小学生都知道结果是5050。若不启用编译优化,其编译结果是:
00000000 <test_for>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 10 sub $0x10,%esp
6: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp) ;j=0
d: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%ebp) ;i=0
14: eb 0d jmp 23 <test_for+0x23> ;跳到23
16: 8b 45 f8 mov -0x8(%ebp),%eax ;j = i + 1
19: 83 c0 01 add $0x1,%eax
1c: 01 45 fc add %eax,-0x4(%ebp)
1f: 83 45 f8 01 addl $0x1,-0x8(%ebp) ;i ++
23: 83 7d f8 63 cmpl $0x63,-0x8(%ebp) ;i < 100就跳到16
27: 7e ed jle 16 <test_for+0x16>
29: 8b 45 fc mov -0x4(%ebp),%eax ;返回值=j
2c: c9 leave
2d: c3 ret
这几乎就是把上面源程序组条编译的结果,没有任何优化。没关系,用编译优化再试一次(加-O2参数),得到结果:
00000000 <test_for>:
0: 55 push %ebp
1: b8 ba 13 00 00 mov $0x13ba,%eax ;返回值=5050
6: 89 e5 mov %esp,%ebp
8: 5d pop %ebp
9: c3 ret
a: 8d b6 00 00 00 00 lea 0x0(%esi),%esi ;这句再次没看懂
不得了了!GCC不需要生成循环代码,直接把正确结果编译出来了!实在是太神奇了!
可是上面几乎是最标准的循环写法,在实际编程中,我们常常写一些不标准的循环语句,GCC能否有一样的优化结果呢?
编译下面这段程序:
int test_for2()
{
int i=0;
int j=0;
for (; i < 100; j += i + 1, i ++);
return j;
}
int test_while()
{
int i=0;
int j=0;
while (i < 100) {
j += i + 1;
i ++;
}
return j;
}
int test_while2()
{
int i=0;
int j=0;
while (1) {
j += i + 1;
if (++ i >= 100)
break;
}
return j;
}
每个函数编译得到的结果和上一次得到的完全一样,在这里就不打印出来了。此刻,我心里对GCC佩服得五体投地。
C++中的指针和引用就像两兄弟,他们几乎有着相同的功能,在大多数情况下都可以互相替换。指针可用CPU间接寻址来实现,其执行效率会比寄存器寻址要慢。引用常被说成是一种别名,可是CPU指令系统里并没有引用和别名这类东西,那么它是如何实现的呢?它会比指针更快吗?为搞明白这个问题,我做了一次试验。
对下面这点代码:
void test1(int *i)
{
(*i) ++;
}
void test2(int &i)
{
i ++;
}
先用gcc编译,然后用objdump反编译
$ gcc -O2 -c pointer_and_reference.cpp
$ objdump -d pointer_and_reference.o
得到:
00000000 <_Z5test1Pi>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 8b 45 08 mov 0x8(%ebp),%eax
6: 83 00 01 addl $0x1,(%eax)
; i++
9: 5d pop %ebp
a: c3 ret
b: 90 nop ; 我没有看懂
这两句干什么用的
c: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
00000010 <_Z5test2Ri>:
10: 55 push %ebp
11: 89 e5 mov %esp,%ebp
13: 8b 45 08 mov 0x8(%ebp),%eax
16: 83 00 01 addl $0x1,(%eax)
; i++
19: 5d pop %ebp
1a: c3 ret
除了我没有看懂的两行代码(在ret之后的奇怪指令),其余代码完全一样。可见引用其实就是用指针实现,两者在效率上并没有差别。
(2)循环优化
C语言写循环还是优点麻烦,通常是会这样写:
int i;
for (i = 0; i < 100; i ++)
{
......
}
其实这个循环很简单,就是要里面的语句循环100遍,i=0、i<100、i++这三条语句显得有些累赘。不知道GCC能否明白我的意图,用最高效的方式实现循环呢?
对下面这段程序:
int test_for()
{
int i;
int j=0;
for (i = 0; i < 100; i ++)
j += i + 1;
return j;
}
函数的作用就是返回从1加到100的结果,小学生都知道结果是5050。若不启用编译优化,其编译结果是:
00000000 <test_for>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 10 sub $0x10,%esp
6: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp) ;j=0
d: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%ebp) ;i=0
14: eb 0d jmp 23 <test_for+0x23> ;跳到23
16: 8b 45 f8 mov -0x8(%ebp),%eax ;j = i + 1
19: 83 c0 01 add $0x1,%eax
1c: 01 45 fc add %eax,-0x4(%ebp)
1f: 83 45 f8 01 addl $0x1,-0x8(%ebp) ;i ++
23: 83 7d f8 63 cmpl $0x63,-0x8(%ebp) ;i < 100就跳到16
27: 7e ed jle 16 <test_for+0x16>
29: 8b 45 fc mov -0x4(%ebp),%eax ;返回值=j
2c: c9 leave
2d: c3 ret
这几乎就是把上面源程序组条编译的结果,没有任何优化。没关系,用编译优化再试一次(加-O2参数),得到结果:
00000000 <test_for>:
0: 55 push %ebp
1: b8 ba 13 00 00 mov $0x13ba,%eax ;返回值=5050
6: 89 e5 mov %esp,%ebp
8: 5d pop %ebp
9: c3 ret
a: 8d b6 00 00 00 00 lea 0x0(%esi),%esi ;这句再次没看懂
不得了了!GCC不需要生成循环代码,直接把正确结果编译出来了!实在是太神奇了!
可是上面几乎是最标准的循环写法,在实际编程中,我们常常写一些不标准的循环语句,GCC能否有一样的优化结果呢?
编译下面这段程序:
int test_for2()
{
int i=0;
int j=0;
for (; i < 100; j += i + 1, i ++);
return j;
}
int test_while()
{
int i=0;
int j=0;
while (i < 100) {
j += i + 1;
i ++;
}
return j;
}
int test_while2()
{
int i=0;
int j=0;
while (1) {
j += i + 1;
if (++ i >= 100)
break;
}
return j;
}
每个函数编译得到的结果和上一次得到的完全一样,在这里就不打印出来了。此刻,我心里对GCC佩服得五体投地。
相关文章推荐
- GCC对编译的优化试验
- 存储过程中引用的常规表,临时表以及表变量是否会导致存储过程的重编译
- FFMPEG库引用的顺序在GCC编译时还有影响
- GCC通过变量的引用识别类型
- 【PHP】 foreach循环中变量引用的一道面试题
- GCC 编译优化指南
- GCC 编译优化指南(转)
- GCC编译优化指南
- 【Smarty】Smarty引用、常用内置变量、判断、循环、JavaScript脚本
- oc block 中直接引用成员变量 循环引用
- GCC编译优化选项
- gcc编译时对'xxxx'未定义的引用问题
- GCC编译优化指南
- GCC 编译优化指南
- GCC编译优化指南
- ant笔记(二)ant的属性使用与引用外部变量,集成一个编译,拷贝,打包,运行的任务链
- [收藏] GCC编译优化指南 & 作者的其他资源
- GCC编译选项和环境变量
- 根据自己的cpu写gcc优化编译参数
- GCC通过变量的引用识别类型