Может ли линкер встроить функции? - PullRequest
43 голосов
/ 13 мая 2011

В файле file1.c есть вызов функции, которая реализована в файле file2.c.Когда я связываю file1.o и file2.o в исполняемый файл, если функция в file2 очень мала, компоновщик автоматически обнаружит, что функция мала и встроен в свой вызов?

1 Ответ

68 голосов
/ 13 мая 2011

В дополнение к поддержке генерации временного кода канала (LTCG), о которой упоминал Джейм МакНеллис, набор инструментов GCC также поддерживает оптимизацию времени канала.Начиная с версии 4.5, GCC поддерживает переключатель -flto, который включает Link Time Optimization (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 поддерживает в LTCG , которая не поддерживается GCC (насколько я знаю) Оптимизация профиля (PGO).Эта технология позволяет компоновщику Microsoft оптимизировать данные профилирования, собранные в ходе предыдущих запусков программы.Это позволяет компоновщику выполнять такие действия, как сбор «горячих» функций на одних и тех же страницах памяти и редко используемые последовательности кода на других страницах памяти, чтобы уменьшить рабочий набор программы.


Редактирование (28 августа 2011 г.): Оптимизация профиля поддержки поддержки GCC с использованием таких опций, как -fprofile-generate и -fprofile-use, но я совершенно не информирован о них.

Спасибо Конраду Рудольфу за то, что он указал на этоя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...