Wann sollte ich den Rahmenzeiger weglassen?

Lesezeit: 5 Minuten

Benutzer-Avatar
PetrosB

Gibt es eine wesentliche Optimierung, wenn der Rahmenzeiger weggelassen wird? Wenn ich das beim Lesen richtig verstanden habe Dies Seite, -fomit-frame-pointer wird verwendet, wenn wir das Speichern, Einrichten und Wiederherstellen von Frame-Zeigern vermeiden möchten.

Wird das nur bei jedem Funktionsaufruf gemacht und wenn ja, lohnt es sich wirklich, ein paar Anweisungen für jede Funktion zu vermeiden? Ist das nicht trivial für eine Optimierung. Was sind die tatsächlichen Auswirkungen der Verwendung dieser Option, abgesehen von den Debugging-Einschränkungen?

Ich habe den folgenden C-Code mit und ohne diese Option kompiliert

int main(void)
{
        int i;

        i = myf(1, 2);
}

int myf(int a, int b)
{
        return a + b;
}

,

# gcc -S -fomit-frame-pointer code.c -o withoutfp.s
# gcc -S code.c -o withfp.s

.

diff -u Das Auslesen der beiden Dateien ergab den folgenden Assembler-Code:


--- withfp.s    2009-12-22 00:03:59.000000000 +0000
+++ withoutfp.s 2009-12-22 00:04:17.000000000 +0000
@@ -7,17 +7,14 @@
        leal    4(%esp), %ecx
        andl    $-16, %esp
        pushl   -4(%ecx)
-       pushl   %ebp
-       movl    %esp, %ebp
        pushl   %ecx
-       subl    $36, %esp
+       subl    $24, %esp
        movl    $2, 4(%esp)
        movl    $1, (%esp)
        call    myf
-       movl    %eax, -8(%ebp)
-       addl    $36, %esp
+       movl    %eax, 20(%esp)
+       addl    $24, %esp
        popl    %ecx
-       popl    %ebp
        leal    -4(%ecx), %esp
        ret
        .size   main, .-main
@@ -25,11 +22,8 @@
 .globl myf
        .type   myf, @function
 myf:
-       pushl   %ebp
-       movl    %esp, %ebp
-       movl    12(%ebp), %eax
-       addl    8(%ebp), %eax
-       popl    %ebp
+       movl    8(%esp), %eax
+       addl    4(%esp), %eax
        ret
        .size   myf, .-myf
        .ident  "GCC: (GNU) 4.2.1 20070719 

Könnte bitte jemand Licht ins Dunkel bringen Schlüssel Punkte des obigen Codes, wo -fomit-frame-pointer tatsächlich den Unterschied gemacht hat?

Bearbeiten: objdump‘s Ausgabe ersetzt durch gcc -S‘s

  • Versuchen Sie es erneut, indem Sie mit -S kompilieren. Vergleichen Sie die Assemblersprache: Sie wird viel besser lesbar sein.

    – Richard Pennington

    21. Dezember 2009 um 21:56 Uhr

  • @Richard: Fertig! Danke für den Hinweis!

    – PetrosB

    21. Dezember 2009 um 22:09 Uhr

  • Verwandte, siehe ARM: Linkregister und Rahmenzeiger.

    – jww

    26. Juni 2016 um 12:22 Uhr


Benutzer-Avatar
Erich Seppanen

-fomit-frame-pointer ermöglicht die Verfügbarkeit eines zusätzlichen Registers für allgemeine Zwecke. Ich würde annehmen, dass dies wirklich nur eine große Sache auf 32-Bit-x86 ist, das ein bisschen nach Registern hungert.*

Man würde erwarten, dass EBP nicht mehr bei jedem Funktionsaufruf gespeichert und angepasst wird, und wahrscheinlich eine zusätzliche Verwendung von EBP in normalem Code und weniger Stapeloperationen, wenn EBP als Allzweckregister verwendet wird.

Ihr Code ist viel zu einfach, um einen Nutzen aus dieser Art der Optimierung zu ziehen – Sie verwenden nicht genügend Register. Außerdem haben Sie den Optimierer nicht aktiviert, was möglicherweise erforderlich ist, um einige dieser Effekte zu sehen.

* ISA-Register, keine Mikroarchitekturregister.

  • Wenn ich explizit andere Optimierungsoptionen festlegen muss, was bedeutet es, dass diese Option getrennt ist? Ihr Punkt, dass mein Code einfach ist, scheint jedoch gültig zu sein!

    – PetrosB

    21. Dezember 2009 um 22:01 Uhr

  • Diese Option ist separat, da sie erhebliche Nachteile für das Debugging hat.

    – Anonym.

    21. Dezember 2009 um 22:04 Uhr

  • Es ist separat, weil es funktionale Auswirkungen auf andere Dinge hat, z. B. das Ausführen Ihres Codes in einem Debugger oder das Verknüpfen mit anderem Code. Ich nehme an, Sie würden selbst bei ausgeschaltetem Optimierer eine Verringerung der Registerüberläufe feststellen, aber da ich es nicht sicher weiß, sichere ich meine Wetten ab.

    – Eric Seppanen

    21. Dezember 2009 um 22:05 Uhr

  • @EricSeppanen können Sie den Unterschied zwischen ISA- und Mikroarchitekturregistern erläutern? Meinst du vielleicht x87/MMX/SSE/etc. mit letzterem? Oder “interne” Register (wie eip)? Vielen Dank!

    – andree

    15. Dezember 2015 um 17:24 Uhr


  • @andreee: Ja, diese Antwort bedeutet ziemlich eindeutig architektonische vs. physische Register (auf die Architektur umbenannt wird). Die Umbenennung von Registern vermeidet falsche Abhängigkeiten, wenn Sie dasselbe architektonische Register für einen anderen Wert wiederverwenden (wenn Sie mit dem alten Wert fertig sind, den Sie dort hatten), aber es hilft Ihnen nicht, wenn Sie 9 verschiedene Variablen “leben” müssen Schleife.

    – Peter Cordes

    20. Januar 2021 um 1:05 Uhr


Der einzige Nachteil des Weglassens ist, dass das Debuggen viel schwieriger ist.

Der große Vorteil ist, dass es ein zusätzliches Allzweckregister gibt, das einen großen Unterschied in der Leistung machen kann. Offensichtlich wird dieses zusätzliche Register nur bei Bedarf verwendet (in Ihrer sehr einfachen Funktion wahrscheinlich nicht); in einigen Funktionen macht es mehr Unterschied als in anderen.

  • Es macht nicht nur das Debuggen viel schwieriger. Gnu docsonline sagt, dass es das Debuggen unmöglich macht

    – PetrosB

    21. Dezember 2009 um 21:57 Uhr

  • Sie liegen falsch. printf() Debuggen (was IST noch Debuggen) ist zum Beispiel sehr gut möglich.

    – Andreas Bonini

    21. Dezember 2009 um 21:58 Uhr

  • Sie können immer noch auf der Anweisungsebene (Assemblersprache) debuggen, unabhängig von den verwendeten Compileroptionen. Sicherlich nicht so einfach wie das Debuggen auf Source-Ebene, aber “unmöglich” ist definitiv das falsche Wort.

    – Ben Voigt

    21. Dezember 2009 um 22:10 Uhr

  • Diese Antwort ist veraltet (wahrscheinlich sogar, als sie gepostet wurde). Moderne Debug-Infoformate wie DWARF ermöglichen es einem Debugger, Variablenwerte zu finden, selbst wenn sie sich in Registern in optimiertem Code befinden (falls sie überhaupt existieren). Für nicht optimierten Code gcc -O0 -fomit-frame-pointer -g sollte eine vernachlässigbare Auswirkung auf das Debugging unter GNU/Linux haben. Nicht dass du sollte Mühe zu verwenden -fomit-frame-pointer in Debug-Builds, aber das beweist, dass es das Debuggen nicht viel, wenn überhaupt, schwieriger macht.

    – Peter Cordes

    20. Januar 2021 um 1:01 Uhr


Sie können häufig aussagekräftigeren Assemblercode von GCC erhalten, indem Sie die verwenden -S Argument zur Ausgabe der Assembly:

$ gcc code.c -S -o withfp.s
$ gcc code.c -S -o withoutfp.s -fomit-frame-pointer
$ diff -u withfp.s withoutfp.s

GCC kümmert sich nicht um die Adresse, sodass wir die tatsächlich generierten Anweisungen direkt vergleichen können. Für Ihre Blattfunktion ergibt dies:

 myf:
-       pushl   %ebp
-       movl    %esp, %ebp
-       movl    12(%ebp), %eax
-       addl    8(%ebp), %eax
-       popl    %ebp
+       movl    8(%esp), %eax
+       addl    4(%esp), %eax
    ret

GCC generiert keinen Code, um den Frame-Zeiger auf den Stack zu schieben, und dies ändert die relative Adresse der Argumente, die an die Funktion auf dem Stack übergeben werden.

Profilieren Sie Ihr Programm, um festzustellen, ob es einen signifikanten Unterschied gibt.

Erstellen Sie als Nächstes ein Profil Ihres Entwicklungsprozesses. Ist das Debuggen einfacher oder schwieriger? Verbringen Sie mehr Zeit mit der Entwicklung oder weniger?

Optimierungen ohne Profiling sind Zeit- und Geldverschwendung.

1385100cookie-checkWann sollte ich den Rahmenzeiger weglassen?

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

Privacy policy