Link Time Code Generation

来源:互联网 发布:上海师范大学网络续费 编辑:程序博客网 时间:2024/05/21 09:41

强大的Link Time Code Generation

(2008-10-15 16:10:04)
转载
标签:

杂谈

分类:MSN搬家
这两天正在研究Link Time CodeGeneration(以下称LTCG),不看不知道,看了很激动。这个功能对于广大的程序员,特别是为了提高程序的性能而苦苦烦恼的程序员来说,简直可以说是拨开云雾见太阳。
废话少说,让我们直奔主题。
现在有下面的示例代码
interface.h
 int func1(int);
 int func2(int);
func1.cpp
 #include "interface.h"
 int func1(int v)
 {
     return v * 2;
 }
func2.cpp
 #include "interface.h"
 int func2(int v)
 {
     return func1(v) * 2;
 }
main.cpp
 #include <tchar.h>
 #include <stdio.h>
 #include "interface.h"
 int _tmain(int argc, TCHAR* argv[])
 {
     _tprintf(_T("Begin\n"));
     int sum = func1(5);
     sum += func2(10);
     return sum;
 }
结构很简单,在main函数里,插入了一个printf语句,是为了方便定位程序对应指令地址的,而之所以引入变量sum,也是为了防止编译器在优化的时候直接把函数调用去除了。
接下来,使用最新的VC++编译器,打开优化开关/Ox(最大优化,/Og即将过时),在分别关闭和打开LTCG的情况下进行编译,并对编译的结果进行反汇编,分别得到main函数的指令如下
1、LTCG关闭
push      40373Ch
call        dword ptr[__imp__printf]
push      5
call        @ILT+0(?func1@@YAHH@Z)
push      0Ah
mov       esi,eax
call        @ILT+5(?func2@@YAHH@Z)
add        esp,0Ch
add        eax,esi
pop        esi
ret
其中 ?func1@@YAHH@Z对应代码如下
mov       eax,dword ptr[esp+4]
add        eax,eax
ret
而 ?func2@@YAHH@Z代码如下
mov       eax,dword ptr[esp+4]
push       eax
call        @ILT+0(?func1@@YAHH@Z)
add         esp,4
add         eax,eax
ret
从上面结果可以看到,编译器大致作了以下优化
1、将原来的乘法操作(*2),改成了一个加法操作
2、优化了寄存器分配,直接对eax(函数返回值寄存器)进行操作,减少了mov指令。
 
接下来,让我们看看另一种情况
2、LTCG打开
push      402108h
call        dword ptrds:[004020A0h]
add        esp,4
mov       eax,32h
ret
前面的两条指令,就是调用printf对应的指令,接下来的事情让人吃惊,
mov eax, 32h
32h是什么?50。50是什么?就是sum的最终结果!
什么func1,func2,统统见鬼去!
相比第一种情况大约花费了20多条指令才能得到的结果,这里就只剩下一个简简单单的32h,简单的让人感到害怕和无地自容。
 
为什么?为什么?为什么?(想起了马景涛声嘶力竭的样子)
原因在于传统的编译模式(词法解析-〉代码生成-〉链接),决定了在生成代码时,编译器只能看到同一个obj文件的内容,因此,优化也只能在obj文件里进行,因此,在第一种情况下,每个函数都已经被优化到极致。而LTCG的引入,则颠覆了这种模式,将链接器提前到代码生成之前,并由之来调用代码生成,这样,在生成代码的时候,编译器便不太容易受到obj文件边界的限制,因此在第二种情况下,编译器可以发现,虽然经过了多次的函数调用和算术操作,但是由于所有的输入都是常量,因此最终结果也一定是一个常量,因此,完全事先将结果算好来加快速度。也就是说,有些函数,虽然没有被标上inline,却被编译器自动inline了。
0 0
原创粉丝点击