paint-brush
Compiler-Optimierungen: Steigern Sie die Code-Leistung mit minimalen Optimierungen!von@durganshu
1,418 Lesungen
1,418 Lesungen

Compiler-Optimierungen: Steigern Sie die Code-Leistung mit minimalen Optimierungen!

von Durganshu Mishra13m2023/11/30
Read on Terminal Reader

Zu lang; Lesen

Entwickler, die die Leistung ihres C++-Codes optimieren möchten, sollten Compiler-Optimierungen entdecken: eine Reihe sehr effektiver C++-Flags, die die Codeleistung ohne großen Aufwand verbessern. Die einzige Voraussetzung ist, dass Sie wissen, was Sie tun. Entdecken Sie Flags, die für Intel C++-Compiler geeignet sind, wie z. B. -fno-alias, -xHost, -xCORE-AVX512, IPO usw., mit einem praktischen Showdown an einem C++-Code der Jacobi-Iteration.
featured image - Compiler-Optimierungen: Steigern Sie die Code-Leistung mit minimalen Optimierungen!
Durganshu Mishra HackerNoon profile picture
0-item
1-item


Das Erzielen höchster Leistung aus Ihrem C++-Code kann entmutigend sein und eine sorgfältige Profilerstellung, komplizierte Anpassungen des Speicherzugriffs und eine Cache-Optimierung erfordern. Gibt es einen Trick, um das etwas zu vereinfachen? Glücklicherweise gibt es eine Abkürzung, um mit minimalem Aufwand bemerkenswerte Leistungssteigerungen zu erzielen – vorausgesetzt, Sie haben die richtigen Erkenntnisse und wissen, was Sie tun. Geben Sie Compiler-Optimierungen ein, die die Leistung Ihres Codes erheblich steigern können.


Moderne Compiler sind unverzichtbare Verbündete auf diesem Weg zur optimalen Leistung, insbesondere bei der automatischen Parallelisierung. Diese hochentwickelten Tools verfügen über die Fähigkeit, komplizierte Codemuster, insbesondere innerhalb von Schleifen, zu untersuchen und Optimierungen nahtlos auszuführen.


Ziel dieses Artikels ist es, die Leistungsfähigkeit von Compiler-Optimierungen hervorzuheben, wobei der Schwerpunkt auf den Intel C++-Compilern liegt, die für ihre Beliebtheit und weite Verbreitung bekannt sind.


In dieser Geschichte enthüllen wir die Schichten der Compiler-Magie, die Ihren Code in ein Hochleistungs-Meisterwerk verwandeln können, das weniger manuelle Eingriffe erfordert, als Sie vielleicht denken.


Highlights: Was sind Compiler-Optimierungen? | -Ein | Architektur gezielt | Interprozedurale Optimierung | -fno-aliasing | Compiler-Optimierungsberichte

Was sind Compileroptimierungen?

Compiler-Optimierungen umfassen verschiedene Techniken und Transformationen, die ein Compiler während der Kompilierung auf den Quellcode anwendet. Aber warum? Zur Verbesserung der Leistung, Effizienz und in manchen Fällen der Größe des resultierenden Maschinencodes. Diese Optimierungen sind von entscheidender Bedeutung für die Beeinflussung verschiedener Aspekte der Codeausführung, einschließlich Geschwindigkeit, Speichernutzung und Energieverbrauch.


Jeder Compiler führt eine Reihe von Schritten aus, um den High-Level-Quellcode in den Low-Level-Maschinencode umzuwandeln. Dazu gehören lexikalische Analyse, Syntaxanalyse, semantische Analyse, Zwischencodegenerierung (IR), Optimierung und Codegenerierung.


Während der Optimierungsphase sucht der Compiler akribisch nach Möglichkeiten, ein Programm zu transformieren, um eine semantisch äquivalente Ausgabe zu erzielen, die weniger Ressourcen verbraucht oder schneller ausgeführt wird. Zu den in diesem Prozess eingesetzten Techniken gehören unter anderem konstante Faltung, Schleifenoptimierung, Funktions-Inlining und Eliminierung von totem Code .


Ich werde nicht alle verfügbaren Optionen besprechen, sondern wie wir den Compiler anweisen können, bestimmte Optimierungen vorzunehmen, die die Codeleistung verbessern könnten. Also, die Lösung???? Compiler-Flags.

Entwickler können während des Kompilierungsprozesses eine Reihe von Compiler-Flags angeben, eine Vorgehensweise, die denjenigen vertraut ist, die Optionen wie „ -g“ oder „-pg“ mit GCC zum Debuggen und Profilieren von Informationen verwenden. Im weiteren Verlauf besprechen wir ähnliche Compiler-Flags, die wir beim Kompilieren unserer Anwendung mit dem Intel C++-Compiler verwenden können. Diese können Ihnen dabei helfen, die Effizienz und Leistung Ihres Codes zu verbessern.


Go Kick Off GIF von CAF



Womit arbeiten wir also?

Ich werde mich nicht mit trockener Theorie befassen oder Sie mit einer langwierigen Dokumentation überschwemmen, in der alle Compiler-Flags aufgeführt sind. Versuchen wir stattdessen zu verstehen, warum und wie diese Flags funktionieren.


Wie schaffen wir das???


Wir nehmen eine nicht optimierte C++-Funktion, die für die Berechnung einer Jacobi- Iteration verantwortlich ist, und entschlüsseln Schritt für Schritt die Auswirkungen jedes Compiler-Flags. Im Rahmen dieser Untersuchung messen wir die Beschleunigung, indem wir jede Iteration systematisch mit der Basisversion vergleichen – beginnend ohne Optimierungsflags (-O0).


Die Beschleunigungen (oder Ausführungszeit) wurden auf einer Maschine mit Intel® Xeon® Platinum 8174 Prozessor gemessen. Hier löst die Jacobi-Methode eine 2D-partielle Differentialgleichung (Poisson-Gleichung) zur Modellierung der Wärmeverteilung auf einem rechteckigen Gitter.


Die Jacobi-Methode


u(x,y,t) ist die Temperatur am Punkt (x,y) zum Zeitpunkt t.


Wir lösen den stabilen Zustand, wenn sich die Verteilung nicht mehr ändert:

Den stabilen Zustand lösen


An der Grenze wurde eine Reihe von Dirichlet-Randbedingungen angewendet.


Wir verfügen im Wesentlichen über eine C++-Codierung, die die Jacobi-Iterationen auf Gittern variabler Größe (die wir Auflösungen nennen) durchführt. Grundsätzlich bedeutet eine Rastergröße von 500 das Lösen einer Matrix der Größe 500x500 und so weiter.


Die Funktion zum Durchführen einer Jacobi-Iteration lautet wie folgt:


 /* * One Jacobi iteration step */ void jacobi(double *u, double *unew, unsigned sizex, unsigned sizey) { int i, j; for (j = 1; j < sizex - 1; j++) { for (i = 1; i < sizey - 1; i++) { unew[i * sizex + j] = 0.25 * (u[i * sizex + (j - 1)] + // left u[i * sizex + (j + 1)] + // right u[(i - 1) * sizex + j] + // top u[(i + 1) * sizex + j]); // bottom } } for (j = 1; j < sizex - 1; j++) { for (i = 1; i < sizey - 1; i++) { u[i * sizex + j] = unew[i * sizex + j]; } } }


Wir führen die Jacobi-Iteration so lange durch, bis das Residuum einen Schwellenwert erreicht (innerhalb einer Schleife). Die Residuenberechnung und die Schwellenwertauswertung erfolgen außerhalb dieser Funktion und sind hier nicht von Belang. Reden wir also jetzt über den Elefanten im Raum!

Wie funktioniert der Basiscode?

Ohne Optimierungen (-O0) erhalten wir folgende Ergebnisse:


Laufzeit in Sekunden und MFLOP/s für den Basisfall („-O0“)


Hier messen wir die Leistung anhand der MFLOP/s. Dies wird die Grundlage unseres Vergleichs sein.


MFLOP/s steht für „Million Floating Point Operations Per Second“. Dabei handelt es sich um eine Maßeinheit zur Quantifizierung der Leistung eines Computers oder Prozessors im Hinblick auf Gleitkommaoperationen. Bei Gleitkommaoperationen handelt es sich um mathematische Berechnungen mit dezimalen oder reellen Zahlen, die in einem Gleitkommaformat dargestellt werden.


MFLOP/s wird häufig als Benchmark oder Leistungsmetrik verwendet, insbesondere in wissenschaftlichen und technischen Anwendungen, bei denen komplexe mathematische Berechnungen vorherrschen. Je höher der MFLOP/s-Wert ist, desto schneller führt das System oder der Prozessor Gleitkommaoperationen aus.


Hinweis 1: Um ein stabiles Ergebnis zu erzielen, führe ich die ausführbare Datei fünfmal für jede Auflösung aus und nehme den Durchschnittswert der MFLOP/s-Werte.

Hinweis 2: Es ist wichtig zu beachten, dass die Standardoptimierung auf dem Intel C++-Compiler -O2 ist. Daher ist es wichtig, beim Kompilieren des Quellcodes -O0 anzugeben.


Lassen Sie uns weitermachen und sehen, wie sich diese Laufzeiten ändern, wenn wir verschiedene Compiler-Flags ausprobieren!

Die häufigsten: -O1, -O2, -O3 und -Ofast

Dies sind einige der am häufigsten verwendeten Compiler-Flags, wenn man mit Compiler-Optimierungen beginnt. Im Idealfall ist die Leistung von Ofast > O3 > O2 > O1 > O0 . Dies geschieht jedoch nicht unbedingt. Die kritischen Punkte dieser Optionen sind wie folgt:


-O1:

  • Ziel: Geschwindigkeit optimieren und gleichzeitig eine Erhöhung der Codegröße vermeiden.
  • Hauptmerkmale: Geeignet für Anwendungen mit großen Codegrößen, vielen Verzweigungen und bei denen die Ausführungszeit nicht durch Code in Schleifen dominiert wird.

-O2:

  • Verbesserungen gegenüber -O1:
    • Aktiviert die Vektorisierung.
    • Ermöglicht das Inlining von Intrinsics und die interprozedurale Optimierung innerhalb der Datei.

-O3:

  • Verbesserungen gegenüber -O2:
    • Ermöglicht aggressivere Schleifentransformationen (Fusion, Block-Unroll-and-Jam).
    • Optimierungen können -O2 nur dann dauerhaft übertreffen, wenn Schleifen- und Speicherzugriffstransformationen auftreten. Es kann sogar den Code verlangsamen.
  • Empfohlen für:
    • Anwendungen mit schleifenlastigen Gleitkommaberechnungen und großen Datenmengen.

-Ofast:

  • Setzt die folgenden Flags:
    • „-O3“
    • „- no-prec-div“ : Es ermöglicht Optimierungen, die schnelle und etwas ungenauere Ergebnisse liefern als die vollständige IEEE-Division. Beispielsweise wird A/B als A * (1/B) berechnet, um die Berechnungsgeschwindigkeit zu verbessern.
    • -fp-model fast=2“ : ermöglicht aggressivere Gleitkommaoptimierungen.


Welche Optimierungen diese Optionen genau bieten, erläutert der offizielle Ratgeber ausführlich.


Wenn wir diese Optionen in unserem Jacobi-Code verwenden, erhalten wir diese Ausführungslaufzeiten:

Vergleich der -On-Flags

Es ist deutlich zu erkennen, dass alle diese Optimierungen viel schneller sind als unser Basiscode (mit „-O0“). Die Ausführungslaufzeit ist 2–3x niedriger als im Basisfall. Was ist mit MFLOP/s?


Vergleich der -On-Flags


Nun ja, das ist was!!!


Es gibt einen großen Unterschied zwischen den MFLOP/s des Basisfalls und denen mit der Optimierung.


Insgesamt schneidet „-O3“ am besten ab, wenn auch nur leicht.


Die von „ -Ofast “ verwendeten zusätzlichen Flags („ -no-prec-div -fp-model fast=2 “) führen nicht zu einer zusätzlichen Beschleunigung.

Zielarchitektur (-xHost,-xCORE-AVX512)

Die Architektur der Maschine ist ein entscheidender Faktor, der Compiler-Optimierungen beeinflusst. Es kann die Leistung erheblich steigern, wenn der Compiler die verfügbaren Befehlssätze und die von der Hardware unterstützten Optimierungen (wie Vektorisierung und SIMD) kennt.


Beispielsweise verfügt meine Skylake-Maschine über 3 SIMD-Einheiten: 1 AVX 512 und 2 AVX-2-Einheiten.


Kann ich mit diesem Wissen wirklich etwas anfangen???


Die Antwort liegt in strategischen Compiler-Flags. Das Experimentieren mit Optionen wie „ -xHost “ und genauer „ -xCORE-AVX512 “ kann es uns ermöglichen, das volle Potenzial der Maschinenfunktionen auszuschöpfen und Optimierungen für eine optimale Leistung anzupassen.


Hier ist eine kurze Beschreibung, worum es bei diesen Flaggen geht:


-xHost:

  • Ziel: Gibt an, dass der Compiler Code generieren soll, der für den höchsten Befehlssatz des Hostcomputers optimiert ist.
  • Hauptmerkmale: Nutzt die neuesten Funktionen und Fähigkeiten der Hardware. Es kann zu einer erstaunlichen Beschleunigung des Zielsystems führen.
  • Überlegungen: Dieses Flag ist zwar für die Host-Architektur optimiert, kann jedoch dazu führen, dass Binärdateien nicht auf verschiedene Maschinen mit unterschiedlichen Befehlssatzarchitekturen portierbar sind.

-xCORE-AVX512:

  • Ziel: Den Compiler explizit anweisen, Code zu generieren, der den Befehlssatz Intel Advanced Vector Extensions 512 (AVX-512) nutzt.

  • Hauptmerkmale: AVX-512 ist ein fortschrittlicher SIMD-Befehlssatz (Single Instruction, Multiple Data), der im Vergleich zu früheren Versionen wie AVX2 breitere Vektorregister und zusätzliche Operationen bietet. Durch die Aktivierung dieses Flags kann der Compiler diese erweiterten Funktionen für eine optimierte Leistung nutzen.

  • Überlegungen: Auch hier ist die Portabilität der Übeltäter. Die mit AVX-512-Anweisungen generierten Binärdateien laufen möglicherweise nicht optimal auf Prozessoren, die diesen Befehlssatz nicht unterstützen. Möglicherweise funktionieren sie überhaupt nicht!


AVX-512-Set-Anweisungen verwenden Zmm-Register, bei denen es sich um einen Satz von 512 Bit breiten Registern handelt. Diese Register dienen als Grundlage für die Vektorverarbeitung.


Standardmäßig geht „ -xCORE-AVX512 “ davon aus, dass das Programm wahrscheinlich nicht von der Verwendung der ZMM-Register profitieren wird. Der Compiler vermeidet die Verwendung von ZMM-Registern, es sei denn, ein Leistungsgewinn ist garantiert.


Wenn man plant, die zmm-Register ohne Einschränkungen zu nutzen, kann „ -qopt-zmm-usage “ auf high gesetzt werden. Das werden wir auch tun.


Vergessen Sie nicht, den offiziellen Leitfaden für detaillierte Anweisungen zu lesen.


Sehen wir uns an, wie diese Flags für unseren Code funktionieren:

Auswirkungen von -xHost und -xCORE-AVX512

Juhu!


Wir überschreiten nun die 1200 MFLOP/s-Marke für die kleinste Auflösung. Auch die MFLOP/s-Werte für andere Auflösungen sind gestiegen.


Das Bemerkenswerte daran ist, dass wir diese Ergebnisse ohne wesentliche manuelle Eingriffe erzielt haben – einfach durch die Einbindung einer Handvoll Compiler-Flags während des Anwendungskompilierungsprozesses.


Es muss jedoch unbedingt betont werden, dass die kompilierte ausführbare Datei nur mit einer Maschine kompatibel ist, die denselben Befehlssatz verwendet.


Der Kompromiss zwischen Optimierung und Portabilität ist offensichtlich, da Code, der für einen bestimmten Befehlssatz optimiert ist, die Portabilität über verschiedene Hardwarekonfigurationen hinweg beeinträchtigen kann. Stellen Sie also sicher, dass Sie wissen, was Sie tun!!


Hinweis: Machen Sie sich keine Sorgen, wenn Ihre Hardware AVX-512 nicht unterstützt. Der Intel C++ Compiler unterstützt Optimierungen für AVX, AVX-2 und sogar SSE. Die Dokumentation enthält alles, was Sie wissen müssen!

Interprozedurale Optimierung (IPO)

Bei der interprozeduralen Optimierung geht es um die Analyse und Transformation von Code über mehrere Funktionen oder Prozeduren hinweg, wobei der Blick über den Rahmen einzelner Funktionen hinausgeht.


IPO ist ein mehrstufiger Prozess, der sich auf die Interaktionen zwischen verschiedenen Funktionen oder Verfahren innerhalb eines Programms konzentriert. IPO kann viele verschiedene Arten von Optimierungen umfassen, einschließlich Forward-Substitution, indirekte Anrufkonvertierung und Inlining.


Intel Compiler unterstützt zwei gängige IPO-Typen: Kompilierung einzelner Dateien und Kompilierung mehrerer Dateien (Whole Program Optimization) [ 3 ]. Es gibt zwei gängige Compiler-Flags, die jeweils diese ausführen:


-ipo:

  • Ziel: Ermöglicht interprozedurale Optimierung, sodass der Compiler während der Kompilierung das gesamte Programm über einzelne Quelldateien hinaus analysieren und optimieren kann.

  • Hauptmerkmale: – Optimierung des gesamten Programms: „ -ipo “ führt eine Analyse und Optimierung aller Quelldateien durch und berücksichtigt dabei die Interaktionen zwischen Funktionen und Prozeduren im gesamten Programm. – Funktions- und modulübergreifende Optimierung: Das Flag erleichtert das Inlining von Funktionen und die Synchronisierung von Optimierungen und Datenflussanalysen über verschiedene Programmteile hinweg.

  • Überlegungen: Es ist ein separater Verknüpfungsschritt erforderlich. Nach dem Kompilieren mit „ -ipo “ ist ein bestimmter Verknüpfungsschritt erforderlich, um die endgültige ausführbare Datei zu generieren. Der Compiler führt beim Linken zusätzliche Optimierungen basierend auf der gesamten Programmansicht durch.


-ip:

  • Ziel: Ermöglicht die interprozedurale Analyse-Weitergabe, sodass der Compiler einige interprozedurale Optimierungen durchführen kann, ohne dass ein separater Verknüpfungsschritt erforderlich ist.

  • Hauptmerkmale: - Analyse und Weitergabe: „ -ip “ ermöglicht es dem Compiler, während der Kompilierung Recherchen und Datenweitergabe über verschiedene Funktionen und Module hinweg durchzuführen. Es führt jedoch nicht alle Optimierungen durch, die die vollständige Programmansicht erfordern. – Schnellere Kompilierung: Im Gegensatz zu „ -ipo “ erfordert „ -ip “ keinen separaten Verknüpfungsschritt, was zu schnelleren Kompilierungszeiten führt. Dies kann während der Entwicklung von Vorteil sein, wenn schnelles Feedback wichtig ist.

  • Überlegungen: Es finden nur einige begrenzte interprozedurale Optimierungen statt, einschließlich Funktions-Inlining.


-ipo bietet im Allgemeinen umfassendere interprozedurale Optimierungsmöglichkeiten, da es einen separaten Verknüpfungsschritt erfordert, jedoch mit längeren Kompilierungszeiten verbunden ist. [ 4 ]

-ip ist eine schnellere Alternative, die einige interprozedurale Optimierungen durchführt, ohne dass ein separater Verknüpfungsschritt erforderlich ist, wodurch sie für Entwicklungs- und Testphasen geeignet ist.[ 5 ]


Da wir nur über Leistung sprechen und verschiedene Optimierungen, Kompilierzeiten oder die Größe der ausführbaren Datei nicht unser Anliegen sind, konzentrieren wir uns auf „ -ipo “.

Wirkung von -ipo

-fno-alias

Alle oben genannten Optimierungen hängen davon ab, wie gut Sie Ihre Hardware kennen und wie viel Sie experimentieren würden. Aber das ist nicht alles. Wenn wir versuchen herauszufinden, wie der Compiler unseren Code sehen würde, können wir möglicherweise andere mögliche Optimierungen identifizieren.


Schauen wir uns noch einmal unseren Code an:


 /* * One Jacobi iteration step */ void jacobi(double *u, double *unew, unsigned sizex, unsigned sizey) { int i, j; for (j = 1; j < sizex - 1; j++) { for (i = 1; i < sizey - 1; i++) { unew[i * sizex + j] = 0.25 * (u[i * sizex + (j - 1)] + // left u[i * sizex + (j + 1)] + // right u[(i - 1) * sizex + j] + // top u[(i + 1) * sizex + j]); // bottom } } for (j = 1; j < sizex - 1; j++) { for (i = 1; i < sizey - 1; i++) { u[i * sizex + j] = unew[i * sizex + j]; } } }


Die Funktion jacobi() benötigt ein paar Zeiger als Parameter und führt dann etwas innerhalb der verschachtelten for-Schleifen aus. Wenn ein Compiler diese Funktion in der Quelldatei sieht, muss er sehr vorsichtig sein.


Warum??


Der Ausdruck zur Berechnung von unew unter Verwendung von u beinhaltet den Durchschnitt von 4 benachbarten u -Werten. Was passiert, wenn sowohl u als auch unew auf denselben Ort verweisen? Dies würde zum klassischen Problem von Alias-Zeigern werden [ 7 ].


Moderne Compiler sind sehr intelligent und gehen aus Sicherheitsgründen davon aus, dass Aliasing möglich sein könnte. Und für solche Szenarien vermeiden sie jegliche Optimierungen, die sich auf die Semantik und die Ausgabe des Codes auswirken könnten.


In unserem Fall wissen wir, dass u und unew unterschiedliche Speicherorte sind und unterschiedliche Werte speichern sollen. So können wir dem Compiler ganz einfach mitteilen, dass es hier kein Aliasing geben wird.


Wie machen wir das?


Es gibt zwei Methoden. Das erste ist das C- Schlüsselwort „ restrict . Es erfordert jedoch eine Änderung des Codes. Das wollen wir vorerst nicht.


Etwas Einfaches? Versuchen wir es mit „ -fno-alias “.


-fno-alias:

  • Ziel: Den Compiler anweisen, im Programm kein Aliasing anzunehmen.

  • Hauptmerkmale: Unter der Annahme, dass kein Aliasing erfolgt, kann der Compiler den Code freier optimieren und so möglicherweise die Leistung verbessern.

  • Überlegungen: Der Entwickler muss bei der Verwendung dieses Flags vorsichtig sein, da das Programm im Falle eines ungerechtfertigten Aliasings möglicherweise unerwartete Ausgaben liefert.


Weitere Details finden Sie in der offiziellen Dokumentation .


Wie funktioniert das für unseren Code?

Wirkung von -fno-alias

Nun, jetzt haben wir etwas!!!


Wir haben hier eine bemerkenswerte Beschleunigung erreicht, fast das Dreifache der vorherigen Optimierungen. Was ist das Geheimnis hinter diesem Boost?


Indem wir den Compiler anweisen, kein Aliasing anzunehmen, haben wir ihm die Freiheit gegeben, leistungsstarke Schleifenoptimierungen auszulösen.


Eine genauere Untersuchung des Assembler-Codes (obwohl hier nicht geteilt) und des generierten Kompilierungsoptimierungsberichts (siehe unten ) zeigt die geschickte Anwendung des Schleifenaustauschs und Schleifenabrollens durch den Compiler. Diese Transformationen tragen zu einer hochgradig optimierten Leistung bei und verdeutlichen den erheblichen Einfluss von Compiler-Anweisungen auf die Codeeffizienz.

Endgültige Grafiken

So verhalten sich alle Optimierungen zueinander:


Vergleich aller Optimierungsflags

Compiler-Optimierungsbericht (-qopt-report)

Der Intel C++-Compiler bietet eine wertvolle Funktion, die es Benutzern ermöglicht, einen Optimierungsbericht zu erstellen, der alle zu Optimierungszwecken vorgenommenen Anpassungen zusammenfasst [ 8 ]. Dieser umfassende Bericht wird im YAML-Dateiformat gespeichert und enthält eine detaillierte Liste der vom Compiler im Code angewendeten Optimierungen. Eine detaillierte Beschreibung finden Sie in der offiziellen Dokumentation zu „ -qopt-report “.

Was als nächstes?

Wir haben eine Handvoll Compiler-Flags besprochen, die die Leistung unseres Codes drastisch verbessern können, ohne dass wir tatsächlich viel tun müssen. Einzige Voraussetzung: Nichts blind machen; Stellen Sie sicher, dass Sie wissen, was Sie tun!!


Es gibt Hunderte solcher Compiler-Flags, und in dieser Geschichte geht es um eine Handvoll. Es lohnt sich also, sich das offizielle Compiler-Handbuch Ihres bevorzugten Compilers anzusehen (insbesondere die Dokumentation zur Optimierung).


Abgesehen von diesen Compiler-Flags gibt es eine ganze Reihe von Techniken wie Vektorisierung, SIMD-Intrinsics, Profile Guided Optimization und Guided Auto Parallelism , die die Leistung Ihres Codes erstaunlich verbessern können.


Ebenso unterstützen Intel C++-Compiler (und alle gängigen) auch Pragma-Direktiven, was sehr nette Funktionen sind. Es lohnt sich, einige der Pragmas wie ivdep, parallel, simd, vector usw. in der Intel-Specific Pragma Reference zu überprüfen.


Das sind alle Leute auf Giphy

Empfohlene Lektüre

[1] Optimierung und Programmierung (intel.com)

[2] Hochleistungsrechnen mit „Elwetritsch“ an der Universität Kaiserslautern-Landau (rptu.de)

[3] Interprozedurale Optimierung (intel.com)

[4] Börsengang, Qipo (intel.com)

[5] IP, Qip (intel.com)

[6] Intel Compiler, Optimierung und andere Flags zur Verwendung durch SPEChpc

[7] Aliasing – IBM-Dokumentation

[8] Intel® Compiler-Optimierungsberichte


Ausgewähltes Foto von Igor Omilaev auf Unsplash .


Auch hier veröffentlicht.