Warum optimiert GCC keine Strukturen?

Lesezeit: 7 Minuten

Benutzeravatar von Alex Gartrell
Alex Gartrell

Systeme verlangen, dass bestimmte Primitive auf bestimmte Punkte innerhalb des Speichers ausgerichtet werden (ints auf Bytes, die ein Vielfaches von 4 sind, Shorts auf Bytes, die ein Vielfaches von 2 sind usw.). Diese können natürlich so optimiert werden, dass möglichst wenig Platz in der Polsterung verschwendet wird.

Meine Frage ist, warum GCC dies nicht automatisch tut? Fehlt die offensichtlichere Heuristik (Ordnen Sie Variablen von der größten zur kleinsten Größenanforderung) in irgendeiner Weise? Ist ein Code von der physischen Reihenfolge seiner Strukturen abhängig (ist das eine gute Idee)?

Ich frage nur, weil GCC in vielerlei Hinsicht super optimiert ist, aber nicht in dieser, und ich denke, es muss eine relativ coole Erklärung geben (die ich nicht kenne).

  • Sie können die versuchen -fipa-struct-reorg Option im struct-reorg-Zweig. Gibt es ein GCC-Schlüsselwort, um die Strukturumordnung zu ermöglichen?

    – phuklv

    15. Juli 2018 um 15:06 Uhr

Benutzeravatar von Damien Neil
Damien Neil

gcc ordnet die Elemente einer Struktur nicht neu, da dies gegen den C-Standard verstoßen würde. Abschnitt 6.7.2.1 des C99-Standards besagt:

Innerhalb eines Strukturobjekts haben die Mitglieder, die keine Bitfelder sind, und die Einheiten, in denen sich Bitfelder befinden, Adressen, die in der Reihenfolge aufsteigen, in der sie deklariert werden.

  • Ja, aber warum wurde es so definiert?

    – nes1983

    1. Januar 2014 um 22:51 Uhr

  • @ nes1983 Der Programmierer trifft möglicherweise Annahmen bezüglich der Reihenfolge der Daten in der Struktur und verwendet möglicherweise eine Maskierung, um jeden Teil zu erhalten. Wenn die Struktur neu geordnet wird, kann die Maskierung falsch sein.

    – Evo510

    2. Januar 2014 um 16:58 Uhr

  • @ Evo510: Ich bin verwirrt. Um Maskierung zu verwenden, müssen Sie auch Padding kennen, was durch die Sprache nicht garantiert wird. Sie können also keine Masken verwenden. Übersehe ich etwas?

    – nes1983

    3. Januar 2014 um 19:58 Uhr

  • @ nes1983 Ich habe numerischen Integrationscode gesehen, der davon ausgeht, dass alle seine Eingaben Floats in sequentieller Reihenfolge sind. Sie übergeben ihm den Zeiger auf den ersten und den letzten zu integrierenden Wert und es scannt zwischen ihnen. Sie behalten die Informationen jedoch in einer Struktur, da dies für alles außer der Integration ein bequemeres Format ist.

    – Cort Ammon

    15. April 2015 um 6:03 Uhr

  • Obwohl es gegen den Standard verstößt, gibt es eine nützliche Neuordnungsmethode, um den Linux-Kernel vor Rootkits/Exploits zu schützen: Teil von Linux KSPP (kernsec.org/wiki/index.php/Kernel_Self_Protection_Project) ist eine Randomisierung/Neuordnung einiger Strukturfelder: openwall.com/lists/kernel-hardening/2017/05/26/8 (Introduce struct layout randomization plugin), zugehöriges Papier: sec.taylor.edu/doc/… (“Verbesserte Kernel-Sicherheit durch Randomisierung des Speicherlayouts” – DM Stanley – ‎2013)

    – osgx

    5. Juni 2017 um 15:47 Uhr

Strukturen werden häufig als Repräsentationen der Packreihenfolge von binären Dateiformaten und Netzwerkprotokollen verwendet. Dies würde brechen, wenn das getan würde. Darüber hinaus würden verschiedene Compiler die Dinge unterschiedlich optimieren und das Verknüpfen von Code aus beiden wäre unmöglich. Das ist einfach nicht machbar.

  • das hat nichts mit Netzwerken oder Dateistrukturen zu tun. Tatsächlich IST der Header einer BMP-Struktur dicht gepackt mit Elementen, die auf nicht natürliche Grenzen fallen, die dem Compiler fremd sind.

    – Andrew Grant

    22. September 2008 um 23:02 Uhr

  • Ähm, ja? Du hast die Frage falsch interpretiert. Lesen Sie noch einmal den zweiten Absatz, in dem er über die Anordnung von Strukturen spricht. Dies ist völlig anders als Polsterung.

    – Serafina Brocious

    22. September 2008 um 23:03 Uhr

  • Ihr erster Punkt ist sehr gültig. aber ich denke, deine zweite ist es nicht. kompilierter Code von verschiedenen Compilern ist sowieso nicht kompatibel.

    – Johannes Schaub – litb

    18. Februar 2009 um 19:29 Uhr

  • @JohannesSchaub-litb das hängt davon ab; Wenn beide Compiler dieselbe ABI einhalten, gibt es für sie keinen Grund, inkompatiblen Code zu erzeugen. Beispiele sind GCC und Clang sowie 32-Bit-GCC und MSVC für C unter Windows.

    – rubenvb

    23. Mai 2013 um 7:57 Uhr

GCC ist klüger als die meisten von uns bei der Erstellung von Maschinencode aus unserem Quellcode; Ich zittere jedoch, wenn es klüger war als wir, unsere Strukturen neu anzuordnen, da es sich um Daten handelt, die zB in eine Datei geschrieben werden können. Eine Struktur, die mit 4 Zeichen beginnt und dann eine 4-Byte-Ganzzahl enthält, wäre nutzlos, wenn sie auf einem anderen System gelesen würde, auf dem GCC entschieden hat, dass die Strukturmitglieder neu angeordnet werden sollten.

  • Das direkte Lesen/Schreiben von Strukturen in eine Datei ist aufgrund der Ausrichtung (die zulässig ist) sowieso nicht für den Compiler/die Plattform portierbar, siehe diese SO-Antwort.

    – Forumgeber

    30. Januar 2018 um 19:57 Uhr

gcc SVN hat zwar eine Strukturreorganisationsoptimierung (-fipa-struct-reorg), aber es erfordert eine Analyse des gesamten Programms und ist im Moment nicht sehr leistungsfähig.

C-Compiler packen Strukturen nicht automatisch gerade weil von Ausrichtungsproblemen, wie Sie sie erwähnen. Zugriffe nicht über Wortgrenzen (32-Bit auf den meisten CPUs) bringen auf x86 schwere Strafen mit sich und verursachen fatale Traps auf RISC-Architekturen.

  • Ich habe nicht davon gesprochen, die Pufferung loszuwerden, ich spreche davon, alle Longs/Zeiger von Ende zu Ende zu bringen, dann alle Shorts von Ende zu Ende, dann alle Zeichen von Ende zu Ende usw ., damit Sie am Ende nur Platz verlieren.

    – Alex Gartrell

    22. September 2008 um 22:53 Uhr

  • Nun, das ist halb wahr. Der C-Compiler packt sie standardmäßig, sie tun es einfach, ausgerichtet an den natürlichen Wortgrenzen der Architektur. Aus diesem Grund müssen Sie Strukturen #pragma pack(0) packen, die Zeichen/Shorts in gepackten Protokollen verwenden, um zu verhindern, dass Auffüllungen hinzugefügt werden.

    – Serafina Brocious

    22. September 2008 um 22:54 Uhr

  • @Alex, äh. Sie werden die gleiche Menge an Platz verschwenden, da Ihr Charakter um die gleiche Menge gepolstert werden müsste. Sie würden weder platz- noch leistungsmäßig davon profitieren.

    – Serafina Brocious

    22. September 2008 um 22:55 Uhr

  • Oh. Ja, das verursacht Probleme mit Binärformaten, wie Cody bestätigte. Außerdem garantiert ANSI, dass Strukturelement-Offsets in aufsteigender Reihenfolge sein müssen.

    – Alex M

    22. September 2008 um 22:57 Uhr

  • Sie verlieren keinen der Vorteile der Polsterung, wenn Sie die Struktur richtig anordnen. Mit einem kurzen, char, char können Sie 0 auffüllen, aber alle Elemente fallen auf den richtigen Offset. In der Regel verlieren Sie dadurch keine Geschwindigkeit, da sie in ihre natürlichen Grenzen geraten

    – Alex Gartrell

    23. September 2008 um 0:53 Uhr

Benutzeravatar von Michel
Michel

Ich sage nicht, dass es eine gute Idee ist, aber Sie können sicherlich Code schreiben, der auf der Reihenfolge der Mitglieder einer Struktur basiert. Als Hack setzen die Leute beispielsweise oft einen Zeiger auf eine Struktur als Typ eines bestimmten Felds darin, auf das sie zugreifen möchten, und verwenden dann Zeigerarithmetik, um dorthin zu gelangen. Für mich ist dies eine ziemlich gefährliche Idee, aber ich habe gesehen, dass sie verwendet wird, insbesondere in C++, um eine als privat deklarierte Variable öffentlich zugänglich zu machen, wenn sie sich in einer Klasse aus einer Bibliothek eines Drittanbieters befindet und nicht öffentlich gekapselt ist. Eine Neuordnung der Mitglieder würde das völlig brechen.

  • Ich habe nicht davon gesprochen, die Pufferung loszuwerden, ich spreche davon, alle Longs/Zeiger von Ende zu Ende zu bringen, dann alle Shorts von Ende zu Ende, dann alle Zeichen von Ende zu Ende usw ., damit Sie am Ende nur Platz verlieren.

    – Alex Gartrell

    22. September 2008 um 22:53 Uhr

  • Nun, das ist halb wahr. Der C-Compiler packt sie standardmäßig, sie tun es einfach, ausgerichtet an den natürlichen Wortgrenzen der Architektur. Aus diesem Grund müssen Sie Strukturen #pragma pack(0) packen, die Zeichen/Shorts in gepackten Protokollen verwenden, um zu verhindern, dass Auffüllungen hinzugefügt werden.

    – Serafina Brocious

    22. September 2008 um 22:54 Uhr

  • @Alex, äh. Sie werden die gleiche Menge an Platz verschwenden, da Ihr Charakter um die gleiche Menge gepolstert werden müsste. Sie würden weder platz- noch leistungsmäßig davon profitieren.

    – Serafina Brocious

    22. September 2008 um 22:55 Uhr

  • Oh. Ja, das verursacht Probleme mit Binärformaten, wie Cody bestätigte. Außerdem garantiert ANSI, dass Strukturelement-Offsets in aufsteigender Reihenfolge sein müssen.

    – Alex M

    22. September 2008 um 22:57 Uhr

  • Sie verlieren keinen der Vorteile der Polsterung, wenn Sie die Struktur richtig anordnen. Mit einem kurzen, char, char können Sie 0 auffüllen, aber alle Elemente fallen auf den richtigen Offset. In der Regel verlieren Sie dadurch keine Geschwindigkeit, da sie in ihre natürlichen Grenzen geraten

    – Alex Gartrell

    23. September 2008 um 0:53 Uhr

Benutzeravatar von AK
AK

Vielleicht möchten Sie den neuesten gcc-Trunk oder struct-reorg-branch ausprobieren, der sich in aktiver Entwicklung befindet.

https://gcc.gnu.org/wiki/cauldron2015?action=AttachFile&do=view&target=Olga+Golovanevsky_+Memory+Layout+Optimizations+of+Structures+and+Objects.pdf

1410210cookie-checkWarum optimiert GCC keine Strukturen?

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

Privacy policy