Kann der Linker inline funktionieren?

Lesezeit: 7 Minuten

Benutzeravatar von Squall

In der Datei file1.cwird eine Funktion aufgerufen, die in der Datei implementiert ist file2.c. Wenn ich verlinke file1.o und file2.o in eine ausführbare Datei, wenn die Funktion in file2 sehr klein ist, erkennt der Linker automatisch, dass die Funktion klein ist, und integriert ihren Aufruf?

  • Einige Linker können das, ja (der Visual C++-Linker hat ein Feature namens „Link-Time Code Generation“, das modulübergreifendes Inlining und Optimierung durchführt). Ob der von Ihnen verwendete Linker dies tun kann oder wird, ist unmöglich zu sagen, da Sie uns nicht mitgeteilt haben, welchen Linker Sie verwenden (selbst dann ist der einzige wirkliche Weg, es sicher zu wissen, herauszufinden, welcher Code Ihr Linker generiert …).

    – James McNellis

    13. Mai 2011 um 3:44 Uhr


Benutzeravatar von Michael Burr
Michael Burr

Neben der von Jame McNellis erwähnten Unterstützung für Link Time Code Generation (LTCG) unterstützt die GCC-Toolchain auch die Optimierung der Linkzeit. Ab Version 4.5 unterstützt GCC die -flto Schalter, der Link Time Optimization (LTO) aktiviert, eine Form der Optimierung des gesamten Programms, die es ermöglicht, Funktionen aus separaten Objektdateien zu inlinen (und alle anderen Optimierungen, die ein Compiler vornehmen könnte, wenn er alle Objektdateien so kompilieren würde, als ob sie aus ihnen stammen würden eine einzelne C-Quelldatei).

Hier ist ein einfaches Beispiel:

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);
}

Kompilieren Sie sie zuerst mit GCC4.5.x – Beispiele aus der GCC-Dokumentation verwenden -O2aber um in meinem einfachen Test sichtbare Ergebnisse zu erzielen, musste ich verwenden -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

Um den Effekt von LTO zu erzielen, sollten Sie die Optimierungsoptionen sogar in der Verknüpfungsphase verwenden – der Linker ruft tatsächlich den Compiler auf, um Teile des Zwischencodes zu kompilieren, die der Compiler in den ersten Schritten oben in die Objektdatei eingefügt hat. Wenn Sie die Optimierungsoption zu diesem Zeitpunkt nicht weitergeben, führt der Compiler nicht das Inlining aus, nach dem Sie suchen würden.

# link using LTO
C:\temp>gcc -o test-lto.exe -flto -O3 print_int.o test.o

Disassemblierung der Version ohne Linkzeitoptimierung. Beachten Sie, dass die Anrufe an die getätigt werden print_int() Funktion:

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

Disassemblierung der Version mit Linkzeitoptimierung. Beachten Sie, dass die Anrufe an printf() werden direkt hergestellt:

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.

Und hier ist das gleiche Experiment mit MSVC (zuerst mit 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>

Jetzt ohne LTCG. Beachten Sie, dass Sie bei MSVC die .c-Datei ohne die kompilieren müssen /GL um zu verhindern, dass der Linker LTCG durchführt – andernfalls erkennt der Linker dies /GL angegeben wurde, und es wird die erzwingen /LTCG Option (hey, das hast du beim ersten Mal gesagt /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>

Eine Sache, die der Linker von Microsoft in LTCG unterstützt das wird von GCC nicht unterstützt (soweit ich weiß) ist die profilgeführte Optimierung (PGO). Diese Technologie ermöglicht es dem Linker von Microsoft, basierend auf Profildaten zu optimieren, die aus früheren Ausführungen des Programms gesammelt wurden. Dies ermöglicht dem Linker, Dinge zu tun, wie z. B. “heiße” Funktionen auf denselben Speicherseiten und selten verwendete Codesequenzen auf anderen Speicherseiten zu sammeln, um den Arbeitssatz eines Programms zu reduzieren.


Bearbeiten (28. August 2011): GCC-Unterstützungsprofil geführte Optimierung mit Optionen wie -fprofile-generate und -fprofile-useaber ich bin völlig uninformiert über sie.

Danke an Konrad Rudolph für den Hinweis.

  • GCC unterstützt auch PGO, via -fprofile-generate und -fprofile-use.

    – Konrad Rudolf

    28. August 2011 um 21:26 Uhr

  • @Konrad: wow – war mir völlig ahnungslos. Ich muss es mir ansehen. Vielen Dank!

    – Michael Burr

    28. August 2011 um 22:50 Uhr

  • Aber LTO wird nicht vom Linker (binutils/ld) gehandhabt; es ist ein Compiler (gcc/gcc).

    – osgx

    28. August 2011 um 23:41 Uhr

  • @osgx: Du hast Recht, aber es wird tatsächlich von implementiert collect2was eine Art Pre-Linker oder Linker-Wrapper ist (gcc.gnu.org/onlinedocs/gccint/Collect2.html). Siehe auch gcc.gnu.org/wiki/LinkTimeOptimization

    – Michael Burr

    29. August 2011 um 0:56 Uhr

1410010cookie-checkKann der Linker inline funktionieren?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy