0%

Link time optimization for C/C++

這是最近流行的最佳化技術之一。打破所謂的Source file隔離原則,可以在Link time時對程式作最佳化。

Without LTO

直接給個範例

demo.c
1
2
3
4
5
6
7
#include <stdio.h>
int add(int, int);
int main()
{
printf("%d\n", add(1, 1));
return 0;
}
add.c
1
2
3
4
int add(int a, int b)
{
return a + b;
}

按照一般的方式編譯它,用objdump觀察編譯結果。

1
2
$ gcc demo.c add.c -O2 -o demo
$ objdump -M intel -S demo

可以看到main被disassembler的code

1
2
3
4
5
6
7
8
400531:       bf 01 00 00 00          mov    edi,0x1
400536: be 01 00 00 00 mov esi,0x1
40053b: e8 20 00 00 00 call 400560 <add>
400540: 89 c1 mov ecx,eax
400542: bf f4 05 40 00 mov edi,0x4005f4
400547: 31 c0 xor eax,eax
400549: 89 ce mov esi,ecx
40054b: e8 c0 fe ff ff call 400410 <printf@plt>

可以看到就算開了O2最佳化之後,還是會先呼叫add,然後繼續呼叫printf。
每個Compilier的作法不一樣,因此分開介紹

LTO With GCC

GCC的方法比較簡單,所以最先介紹。只要在編義的時候加上-flto就可以了。注意-flto的選項對-O0無效,至少要-O1以上

1
$ gcc demo.c add.c -flto -O2 -o demo

然後同樣看一下disassembler的code

1
2
3
4
5
6
400470:       48 83 ec 08             sub    rsp,0x8
400474: ba 02 00 00 00 mov edx,0x2
400479: be 04 06 40 00 mov esi,0x400604
40047e: bf 01 00 00 00 mov edi,0x1
400483: 31 c0 xor eax,eax
400485: e8 d6 ff ff ff call 400460 <__printf_chk@plt>

可以看到printf之前的add不見了,取而代之的直接算出2,然後呼叫printf。

LTO With Clang

在Linux怎麼搞兜兜搞不定,不過在OS X一次就成功了。Gold linker好難裝啊
在Clang不需要開啟任何的最佳化。

1
2
$ clang -flto demo.c add.c -o demo
$ otool -tv demo

LTO With VC++

測試了一下,這方法只對Release Build有用,Debug Build的行為就類似於GCC -O0一樣不會最佳化。
編譯選項在於Link-time Code Generation直接修改就能用了。