在文件file1.c
中,有一個調用函數,該函數在文件file2.c
中實現。 當我將file1.o
和file2.o
鏈接到一個可執行文件中時,如果file2
中的函數非常小,鏈接器是否會自動檢測到該函數很小並且內聯其調用?鏈接器內聯函數可以嗎?
回答
除了支持Jame McNellis提到的鏈接時間碼生成(LTCG)外,GCC工具鏈還支持鏈接時間優化。從版本4.5開始,GCC支持-flto
交換機,該交換機支持鏈路時間優化(LTO),這是一種整體程序優化的形式,它允許從單獨的目標文件內聯函數(以及編譯器在編譯時可能做出的任何其他優化所有的對象文件就好像它們來自單個C源文件)。
這裏有一個簡單的例子:
test.c的:
void print_int(int x);
int main(){
print_int(1);
print_int(42);
print_int(-1);
return 0;
}
print_int.c:
#include <stdio.h>
void print_int(int x)
{
printf("the int is %d\n", x);
}
首先使用GCC4.5.x編譯它們 - 實例從GCC文檔使用-O2
,但爲了在我的簡單測試中獲得可見的結果,我必須使用-O3
:
C:\temp>gcc --version
gcc (GCC) 4.5.2
# compile with preparation for LTO
C:\temp>gcc -c -O3 -flto test.c
C:\temp>gcc -c -O3 -flto print_int.c
# link without LTO
C:\temp>gcc -o test-nolto.exe print_int.o test.o
爲了得到你應該甚至在鏈接階段使用的優化選項LTO的效果 - 鏈接實際上調用編譯器編譯的中間代碼段編譯器放入對象文件在上面的第一步。如果您在這個階段沒有通過優化選項,編譯器將不會執行您要查找的內聯。
# link using LTO
C:\temp>gcc -o test-lto.exe -flto -O3 print_int.o test.o
反彙編不包含鏈接時間優化的版本。需要注意的是呼叫到print_int()
功能提出:
C:\temp>gdb test-nolto.exe
GNU gdb (GDB) 7.2
(gdb) start
Temporary breakpoint 1 at 0x401373
Starting program: C:\temp/test-nolto.exe
[New Thread 3324.0xdc0]
Temporary breakpoint 1, 0x00401373 in main()
(gdb) disassem
Dump of assembler code for function main:
0x00401370 <+0>: push %ebp
0x00401371 <+1>: mov %esp,%ebp
=> 0x00401373 <+3>: and $0xfffffff0,%esp
0x00401376 <+6>: sub $0x10,%esp
0x00401379 <+9>: call 0x4018ca <__main>
0x0040137e <+14>: movl $0x1,(%esp)
0x00401385 <+21>: call 0x401350 <print_int>
0x0040138a <+26>: movl $0x2a,(%esp)
0x00401391 <+33>: call 0x401350 <print_int>
0x00401396 <+38>: movl $0xffffffff,(%esp)
0x0040139d <+45>: call 0x401350 <print_int>
0x004013a2 <+50>: xor %eax,%eax
0x004013a4 <+52>: leave
0x004013a5 <+53>: ret
拆卸與連接時間優化的版本。請注意,以printf()
呼叫被直接製作:
C:\temp>gdb test-lto.exe
GNU gdb (GDB) 7.2
(gdb) start
Temporary breakpoint 1 at 0x401373
Starting program: C:\temp/test-lto.exe
[New Thread 1768.0x126c]
Temporary breakpoint 1, 0x00401373 in main()
(gdb) disassem
Dump of assembler code for function main:
0x00401370 <+0>: push %ebp
0x00401371 <+1>: mov %esp,%ebp
=> 0x00401373 <+3>: and $0xfffffff0,%esp
0x00401376 <+6>: sub $0x10,%esp
0x00401379 <+9>: call 0x4018da <__main>
0x0040137e <+14>: movl $0x1,0x4(%esp)
0x00401386 <+22>: movl $0x403064,(%esp)
0x0040138d <+29>: call 0x401acc <printf>
0x00401392 <+34>: movl $0x2a,0x4(%esp)
0x0040139a <+42>: movl $0x403064,(%esp)
0x004013a1 <+49>: call 0x401acc <printf>
0x004013a6 <+54>: movl $0xffffffff,0x4(%esp)
0x004013ae <+62>: movl $0x403064,(%esp)
0x004013b5 <+69>: call 0x401acc <printf>
0x004013ba <+74>: xor %eax,%eax
0x004013bc <+76>: leave
0x004013bd <+77>: ret
End of assembler dump.
而這裏的相同實驗MSVC(先用LTCG):
C:\temp>cl -c /GL /Zi /Ox test.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
test.c
C:\temp>cl -c /GL /Zi /Ox print_int.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
print_int.c
C:\temp>link /LTCG test.obj print_int.obj /out:test-ltcg.exe /debug
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
Generating code
Finished generating code
C:\temp>"\Program Files (x86)\Debugging Tools for Windows (x86)"\cdb test-ltcg.exe
Microsoft (R) Windows Debugger Version 6.12.0002.633 X86
Copyright (c) Microsoft Corporation. All rights reserved.
CommandLine: test-ltcg.exe
// ...
0:000> u main
*** WARNING: Unable to verify checksum for test-ltcg.exe
test_ltcg!main:
00cd1c20 6a01 push 1
00cd1c22 68d05dcd00 push offset test_ltcg!__decimal_point_length+0x10 (00cd5dd0)
00cd1c27 e8e3f3feff call test_ltcg!printf (00cc100f)
00cd1c2c 6a2a push 2Ah
00cd1c2e 68d05dcd00 push offset test_ltcg!__decimal_point_length+0x10 (00cd5dd0)
00cd1c33 e8d7f3feff call test_ltcg!printf (00cc100f)
00cd1c38 6aff push 0FFFFFFFFh
00cd1c3a 68d05dcd00 push offset test_ltcg!__decimal_point_length+0x10 (00cd5dd0)
00cd1c3f e8cbf3feff call test_ltcg!printf (00cc100f)
00cd1c44 83c418 add esp,18h
00cd1c47 33c0 xor eax,eax
00cd1c49 c3 ret
0:000>
現在沒有LTCG。請注意,使用MSVC,您必須編譯.c文件而不使用/GL
以防止鏈接器執行LTCG - 否則鏈接器會檢測到指定了/GL
,並且會強制選擇/LTCG
選項(嘿,這就是您所說的你想要的第一次與周圍/GL
):
C:\temp>cl -c /Zi /Ox test.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
test.c
C:\temp>cl -c /Zi /Ox print_int.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
print_int.c
C:\temp>link test.obj print_int.obj /out:test-noltcg.exe /debug
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
C:\temp>"\Program Files (x86)\Debugging Tools for Windows (x86)"\cdb test-noltcg.exe
Microsoft (R) Windows Debugger Version 6.12.0002.633 X86
Copyright (c) Microsoft Corporation. All rights reserved.
CommandLine: test-noltcg.exe
// ...
0:000> u main
test_noltcg!main:
00c41020 6a01 push 1
00c41022 e8e3ffffff call test_noltcg!ILT+5(_print_int) (00c4100a)
00c41027 6a2a push 2Ah
00c41029 e8dcffffff call test_noltcg!ILT+5(_print_int) (00c4100a)
00c4102e 6aff push 0FFFFFFFFh
00c41030 e8d5ffffff call test_noltcg!ILT+5(_print_int) (00c4100a)
00c41035 83c40c add esp,0Ch
00c41038 33c0 xor eax,eax
00c4103a c3 ret
0:000>
的一件事,微軟的鏈接器支持在LTCG
不是由GCC支持(據我所知)
是檔案導引優化(PGO)。該技術允許微軟的鏈接器根據從以前的程序運行中收集的分析數據進行優化。這允許鏈接器執行諸如將「熱」功能聚集到相同的內存頁面上以及很少將代碼序列使用到其他內存頁面上以減少程序的工作集合的事情。
編輯(2011年8月28日):GCC支持輪廓使用選項,如-fprofile-generate
和-fprofile-use
嚮導式優化,但我完全不瞭解他們。
感謝Konrad Rudolph爲我指出這一點。
GCC還通過'-fprofile-generate'和'-fprofile-use'支持PGO。 – 2011-08-28 21:26:57
@Konrad:哇 - 我完全沒有意識到。我得看看它。謝謝! – 2011-08-28 22:50:53
但LTO不是由鏈接器(binutils/ld)處理;它是一個編譯器(gcc/gcc)。 – osgx 2011-08-28 23:41:10
- 1. C/C++編譯器可以內聯malloc()內聯函數嗎?
- 2. 內聯函數鏈接器錯誤
- 3. 內聯函數鏈接
- 4. 內聯失敗:函數體可以在鏈接時被覆蓋
- 5. 靜態模板函數可以通過編譯器內聯嗎?
- 6. 通過函數指針使用的函數可以內聯嗎?
- 7. 內聯函數和外部鏈接
- 8. 內聯函數的鏈接錯誤
- 9. 代碼編輯器內聯函數鏈接器錯誤
- 10. 在C++中,構造函數和析構函數可以是內聯函數嗎?
- 11. codeigniter可以支持內聯函數。
- 12. 何時可以不內聯函數?
- 13. 可以HotSpot內聯lambda函數調用?
- 14. 是否可以重寫內聯函數?
- 15. 有什麼方法可以內聯遞歸函數嗎?
- 16. C++內聯函數可以防止複製嗎?
- 17. 你可以內聯靜態成員函數嗎?
- 18. OpenCL內聯函數可以返回OpenCL類型嗎?
- 19. C++內聯函數調用函數可以在頭文件後面聲明嗎?
- 20. 由xsl:function定義的函數可以替代xpath 3.0內聯函數嗎?
- 21. Java流可以鏈接而不使用構造函數嗎?
- 22. 我可以在jQuery中鏈接函數調用嗎?
- 23. Linux:函數可以用作符號鏈接的源代碼嗎?
- 24. 我可以創建一個JavaScript函數的鏈接點嗎?
- 25. 遞歸調用指令的鏈接函數可以嗎?
- 26. 我可以在鏈接庫中掛鉤函數嗎?
- 27. 我可以在Oracle中鏈接數據庫鏈接嗎?
- 28. gcc可以通過一個常量函數指針數組內聯一個間接函數嗎?
- 29. 可以通過指令中的鏈接函數完全替換控制器嗎?
- 30. 可以在OCaml中連接函數嗎?
你試過了嗎? – 2011-05-13 03:40:45
有些鏈接器可以,(Visual C++鏈接器有一個稱爲「鏈接時間代碼生成」的功能,可執行跨模塊內聯和優化)。無論您使用的鏈接程序是否可以執行此操作,都不可能說,因爲您沒有告訴我們您使用的是哪種鏈接程序(即便如此,唯一真正瞭解的方法是找出代碼你的鏈接器生成...)。 – 2011-05-13 03:44:11