В файле file1.c
есть вызов функции, которая реализована в файле file2.c
.
Когда я связываю file1.o
и file2.o
с исполняемым файлом, если функция из file2
очень мала, будет ли компоновщик автоматически обнаруживать, что эта функция мала и встроен в ее вызов?
Может ли функтор компоновщика работать?
Ответ 1
В дополнение к поддержке поколения Time Time Generation (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>
Единственное, что поддерживает Microsoft linker в LTCG , которое не поддерживается GCC (насколько мне известно) - это профилированная оптимизация (PGO). Эта технология позволяет компоновщику Microsoft оптимизировать на основе профилирующих данных, собранных из предыдущих прогонов программы. Это позволяет компоновщику делать такие вещи, как сбор "горячих" функций на одни и те же страницы памяти и редко используемые последовательности кода на другие страницы памяти, чтобы уменьшить рабочий набор программы.
Изменить (28 августа 2011 г.): оптимизация профиля профиля GCC с использованием таких параметров, как -fprofile-generate
и -fprofile-use
, но я совершенно не проинформирован о них.
Спасибо Конраду Рудольфу за то, что указали мне это.