Ich möchte also sicherstellen, dass wenn ich profiliere, ich keine verzerrten Ergebnisse bekomme. Daher möchte ich sicherstellen, dass der Compiler keine output-Anweisungen
optimiert
Sie sind per Definition schief die Ergebnisse.
So beheben Sie das Problem, dass Sie versuchen, "Dummy" -Code zu schreiben, den Sie nur zum Testen geschrieben haben: Speichern Sie Ihre Ergebnisse für die Profilerstellung in einem globalen / statischen Array und drucken Sie ein Mitglied des Arrays an die Ausgabe am Ende des Programms. Der Compiler ist nicht in der Lage, out irgendwelche der Berechnungen zu optimieren, die Werte in das Array eingetragen haben, aber Sie erhalten immer noch andere Optimierungen, die er einsetzen kann, um den Code schnell zu machen.
Die Zuweisung zu einer float
-Variable sollte niemals wegoptimiert werden, damit Sie das gewünschte Ergebnis erhalten:
Was bei allen bisher verwendeten Compilern funktioniert hat:
%Vor% Beachten Sie, dass dies die Ergebnisse verfälscht, boith-Methoden sollten in int
schreiben.
fabs()
sagt dem Compiler "der Wert darf ohne Ihre Nachricht aufgerufen werden", daher kann der Compiler die Berechnung nicht überspringen und das Ergebnis löschen. Um die Propagierung von Eingabekonstanten zu blockieren, müssen Sie sie möglicherweise auch über ein externes volatiles ausführen:
Dennoch können alle Optimierungen zwischen dem Lesen und dem Schreiben "mit voller Kraft" angewendet werden. Das Lesen und Schreiben in die globale Variable sind oft gute "FencePosts" beim Überprüfen der generierten Assembly.
Ohne den i += 10
kann der Compiler bemerken, dass ein Verweis auf die Variable niemals genommen wird, und somit bestimmen, dass er nicht flüchtig sein kann. Technisch gesehen, mit Link Time Code Generation, ist es vielleicht nicht genug, aber ich habe keinen Compiler gefunden, der aggressiv ist. (Für einen Compiler, der tatsächlich den Zugriff entfernt, müsste der Verweis an eine Funktion in einer zur Laufzeit geladenen DLL übergeben werden)
Sie müssen nur zu dem Teil springen, in dem Sie etwas lernen und das veröffentlichte Intel CPU Optimierungshandbuch
Diese stellen ganz klar fest, dass das Gießen zwischen float und int eine sehr schlechte Idee ist, da es einen Speicher vom int-Register zum Speicher benötigt, gefolgt von einem Laden in ein float-Register. Diese Vorgänge verursachen eine Blase in der Pipeline und verschwenden viele wertvolle Zyklen.
Ein Funktionsaufruf verursacht ziemlich viel Overhead, also würde ich das sowieso entfernen.
Hinzufügen eines Dummy + = i; Das ist kein Problem, solange Sie dasselbe Bit im alternativen Profil behalten. (Also der Code, gegen den Sie es vergleichen).
Last but not least: asm-Code generieren. Auch wenn Sie in asm nicht codieren können, ist der generierte Code in der Regel verständlich, da er hinter Codenamen steht und C-Code kommentiert. So wissen Sie, was passiert und welche Bits behalten werden.
R
ps. habe das auch gefunden:
%Vor%angeblich auch sehr schnell. Vielleicht möchten Sie dies auch profilieren. (obwohl es kaum portabler Code ist)
In diesem Fall schlage ich vor, dass Sie die Funktion den ganzzahligen Wert zurückgeben:
%Vor%Ihr Anrufcode kann sie dann mit einem Printf auswerten, um sicherzustellen, dass sie nicht optimiert wird. Stellen Sie außerdem sicher, dass sich float_to_int in einer separaten Kompilierungseinheit befindet, damit der Compiler keine Tricks spielen kann.
%Vor%Vergleichen Sie dies jetzt mit einer leeren Funktion wie:
%Vor%Was auch extern sein sollte.
Der Unterschied in den Zeiten sollte Ihnen eine Vorstellung von den Kosten geben, die Sie zu messen versuchen.
Geben Sie den Wert zurück?
%Vor%und dann können Sie auf der Aufrufseite alle Rückgabewerte zusammenfassen und das Ergebnis nach Abschluss des Benchmarks ausdrucken. Der übliche Weg, dies zu tun, ist irgendwie sicherzustellen, dass Sie vom Ergebnis abhängig sind.
Sie könnten stattdessen eine globale Variable verwenden, aber es scheint, als würde das mehr Cache-Misses erzeugen. In der Regel reicht es aus, den Wert einfach an den Aufrufer zurückzugeben (und sicherzustellen, dass der Aufrufer tatsächlich etwas damit macht).
Ein Mikro-Benchmark um diese Aussage herum ist nicht repräsentativ für die Verwendung dieses Ansatzes in einem echten Szenario; Die umgebenden Anweisungen und ihre Auswirkung auf die Pipeline und den Cache sind im Allgemeinen genauso wichtig wie jede gegebene Anweisung in sich selbst.
GCC 4 macht jetzt viele Mikrooptimierungen, die GCC 3.4 noch nie gemacht hat. GCC4 enthält einen Tree Vectorizer, der sich als sehr nützlich erweist, um SSE und MMX zu nutzen. Es verwendet auch die Bibliotheken GMP und MPFR, um bei der Optimierung von Aufrufen wie int dummy;
, dummy += i
usw. zu helfen, und optimiert solche Aufrufe für ihre FPU, SSE oder 3D Now! Äquivalente.
Ich weiß, dass der Intel-Compiler auch bei diesen Optimierungen sehr gut ist.
Mein Vorschlag ist, sich keine Gedanken über Mikrooptimierungen wie diese zu machen - auf relativ neuer Hardware (alles, was in den letzten 5 oder 6 Jahren gebaut wurde), sind sie fast völlig irrelevant.
Edit: Bei neueren CPUs ist der Befehl i
der FPU weit schneller als ein Cast in %code% und Bitmaske, und der Befehl %code% wird im Allgemeinen schneller sein als die Vorberechnung einer Tabelle oder die Extrapolation einer Taylor-Reihe . Viele der Optimierungen, die Sie zum Beispiel in "Tricks of the Game Programming Gurus" finden würden, sind völlig unbegründet und könnten, wie in einer anderen Antwort ausgeführt, möglicherweise langsamer sein als Anweisungen auf der FPU und in SSE.
All dies ist auf die Tatsache zurückzuführen, dass neuere CPUs pipelined sind - Befehle werden dekodiert und an schnelle Recheneinheiten gesendet. Befehle laufen nicht mehr in Taktzyklen und sind empfindlicher für Cache-Misses und Inter-Instruction-Abhängigkeiten.
Lesen Sie die AMD- und Intel-Prozessor-Programmierhandbücher für alle wichtigen Details.
Ich möchte also sicherstellen, dass wenn ich profiliere, ich keine verzerrten Ergebnisse bekomme. Daher möchte ich sicherstellen, dass der Compiler keine output-Anweisungen
optimiert
Sie sind per Definition schief die Ergebnisse.
So beheben Sie das Problem, dass Sie versuchen, "Dummy" -Code zu schreiben, den Sie nur zum Testen geschrieben haben: Speichern Sie Ihre Ergebnisse für die Profilerstellung in einem globalen / statischen Array und drucken Sie ein Mitglied des Arrays an die Ausgabe am Ende des Programms. Der Compiler ist nicht in der Lage, out irgendwelche der Berechnungen zu optimieren, die Werte in das Array eingetragen haben, aber Sie erhalten immer noch andere Optimierungen, die er einsetzen kann, um den Code schnell zu machen.
Die Zuweisung zu einer %code% -Variable sollte niemals wegoptimiert werden, damit Sie das gewünschte Ergebnis erhalten:
%Vor%Was bei allen bisher verwendeten Compilern funktioniert hat:
%Vor%Beachten Sie, dass dies die Ergebnisse verfälscht, boith-Methoden sollten in %code% schreiben.
%code% sagt dem Compiler "der Wert darf ohne Ihre Nachricht aufgerufen werden", daher kann der Compiler die Berechnung nicht überspringen und das Ergebnis löschen. Um die Propagierung von Eingabekonstanten zu blockieren, müssen Sie sie möglicherweise auch über ein externes volatiles ausführen:
%Vor%Dennoch können alle Optimierungen zwischen dem Lesen und dem Schreiben "mit voller Kraft" angewendet werden. Das Lesen und Schreiben in die globale Variable sind oft gute "FencePosts" beim Überprüfen der generierten Assembly.
Ohne den %code% kann der Compiler bemerken, dass ein Verweis auf die Variable niemals genommen wird, und somit bestimmen, dass er nicht flüchtig sein kann. Technisch gesehen, mit Link Time Code Generation, ist es vielleicht nicht genug, aber ich habe keinen Compiler gefunden, der aggressiv ist. (Für einen Compiler, der tatsächlich den Zugriff entfernt, müsste der Verweis an eine Funktion in einer zur Laufzeit geladenen DLL übergeben werden)
Sie müssen nur zu dem Teil springen, in dem Sie etwas lernen und das veröffentlichte Intel CPU Optimierungshandbuch
Diese stellen ganz klar fest, dass das Gießen zwischen float und int eine sehr schlechte Idee ist, da es einen Speicher vom int-Register zum Speicher benötigt, gefolgt von einem Laden in ein float-Register. Diese Vorgänge verursachen eine Blase in der Pipeline und verschwenden viele wertvolle Zyklen.
Ein Funktionsaufruf verursacht ziemlich viel Overhead, also würde ich das sowieso entfernen.
Hinzufügen eines Dummy + = i; Das ist kein Problem, solange Sie dasselbe Bit im alternativen Profil behalten. (Also der Code, gegen den Sie es vergleichen).
Last but not least: asm-Code generieren. Auch wenn Sie in asm nicht codieren können, ist der generierte Code in der Regel verständlich, da er hinter Codenamen steht und C-Code kommentiert. So wissen Sie, was passiert und welche Bits behalten werden.
R
ps. habe das auch gefunden:
%Vor%angeblich auch sehr schnell. Vielleicht möchten Sie dies auch profilieren. (obwohl es kaum portabler Code ist)
In diesem Fall schlage ich vor, dass Sie die Funktion den ganzzahligen Wert zurückgeben:
%Vor%Ihr Anrufcode kann sie dann mit einem Printf auswerten, um sicherzustellen, dass sie nicht optimiert wird. Stellen Sie außerdem sicher, dass sich float_to_int in einer separaten Kompilierungseinheit befindet, damit der Compiler keine Tricks spielen kann.
%Vor%Vergleichen Sie dies jetzt mit einer leeren Funktion wie:
%Vor%Was auch extern sein sollte.
Der Unterschied in den Zeiten sollte Ihnen eine Vorstellung von den Kosten geben, die Sie zu messen versuchen.
Geben Sie den Wert zurück?
%Vor%und dann können Sie auf der Aufrufseite alle Rückgabewerte zusammenfassen und das Ergebnis nach Abschluss des Benchmarks ausdrucken. Der übliche Weg, dies zu tun, ist irgendwie sicherzustellen, dass Sie vom Ergebnis abhängig sind.
Sie könnten stattdessen eine globale Variable verwenden, aber es scheint, als würde das mehr Cache-Misses erzeugen. In der Regel reicht es aus, den Wert einfach an den Aufrufer zurückzugeben (und sicherzustellen, dass der Aufrufer tatsächlich etwas damit macht).
Ein Mikro-Benchmark um diese Aussage herum ist nicht repräsentativ für die Verwendung dieses Ansatzes in einem echten Szenario; Die umgebenden Anweisungen und ihre Auswirkung auf die Pipeline und den Cache sind im Allgemeinen genauso wichtig wie jede gegebene Anweisung in sich selbst.
GCC 4 macht jetzt viele Mikrooptimierungen, die GCC 3.4 noch nie gemacht hat. GCC4 enthält einen Tree Vectorizer, der sich als sehr nützlich erweist, um SSE und MMX zu nutzen. Es verwendet auch die Bibliotheken GMP und MPFR, um bei der Optimierung von Aufrufen wie %code% , %code% usw. zu helfen, und optimiert solche Aufrufe für ihre FPU, SSE oder 3D Now! Äquivalente.
Ich weiß, dass der Intel-Compiler auch bei diesen Optimierungen sehr gut ist.
Mein Vorschlag ist, sich keine Gedanken über Mikrooptimierungen wie diese zu machen - auf relativ neuer Hardware (alles, was in den letzten 5 oder 6 Jahren gebaut wurde), sind sie fast völlig irrelevant.
Edit: Bei neueren CPUs ist der Befehl %code% der FPU weit schneller als ein Cast in %code% und Bitmaske, und der Befehl %code% wird im Allgemeinen schneller sein als die Vorberechnung einer Tabelle oder die Extrapolation einer Taylor-Reihe . Viele der Optimierungen, die Sie zum Beispiel in "Tricks of the Game Programming Gurus" finden würden, sind völlig unbegründet und könnten, wie in einer anderen Antwort ausgeführt, möglicherweise langsamer sein als Anweisungen auf der FPU und in SSE.
>All dies ist auf die Tatsache zurückzuführen, dass neuere CPUs pipelined sind - Befehle werden dekodiert und an schnelle Recheneinheiten gesendet. Befehle laufen nicht mehr in Taktzyklen und sind empfindlicher für Cache-Misses und Inter-Instruction-Abhängigkeiten.
Lesen Sie die AMD- und Intel-Prozessor-Programmierhandbücher für alle wichtigen Details.
In diesem Fall schlage ich vor, dass Sie die Funktion den ganzzahligen Wert zurückgeben:
%Vor%Ihr Anrufcode kann sie dann mit einem Printf auswerten, um sicherzustellen, dass sie nicht optimiert wird. Stellen Sie außerdem sicher, dass sich float_to_int in einer separaten Kompilierungseinheit befindet, damit der Compiler keine Tricks spielen kann.
%Vor%Vergleichen Sie dies jetzt mit einer leeren Funktion wie:
%Vor%Was auch extern sein sollte.
Der Unterschied in den Zeiten sollte Ihnen eine Vorstellung von den Kosten geben, die Sie zu messen versuchen.
Ich möchte also sicherstellen, dass wenn ich profiliere, ich keine verzerrten Ergebnisse bekomme. Daher möchte ich sicherstellen, dass der Compiler keine output-Anweisungen
optimiert
Sie sind per Definition schief die Ergebnisse.
So beheben Sie das Problem, dass Sie versuchen, "Dummy" -Code zu schreiben, den Sie nur zum Testen geschrieben haben: Speichern Sie Ihre Ergebnisse für die Profilerstellung in einem globalen / statischen Array und drucken Sie ein Mitglied des Arrays an die Ausgabe am Ende des Programms. Der Compiler ist nicht in der Lage, out irgendwelche der Berechnungen zu optimieren, die Werte in das Array eingetragen haben, aber Sie erhalten immer noch andere Optimierungen, die er einsetzen kann, um den Code schnell zu machen.
Was bei allen bisher verwendeten Compilern funktioniert hat:
%Vor% Beachten Sie, dass dies die Ergebnisse verfälscht, boith-Methoden sollten in writeMe
schreiben.
volatile
sagt dem Compiler "der Wert darf ohne Ihre Nachricht aufgerufen werden", daher kann der Compiler die Berechnung nicht überspringen und das Ergebnis löschen. Um die Propagierung von Eingabekonstanten zu blockieren, müssen Sie sie möglicherweise auch über ein externes volatiles ausführen:
Dennoch können alle Optimierungen zwischen dem Lesen und dem Schreiben "mit voller Kraft" angewendet werden. Das Lesen und Schreiben in die globale Variable sind oft gute "FencePosts" beim Überprüfen der generierten Assembly.
Ohne den extern
kann der Compiler bemerken, dass ein Verweis auf die Variable niemals genommen wird, und somit bestimmen, dass er nicht flüchtig sein kann. Technisch gesehen, mit Link Time Code Generation, ist es vielleicht nicht genug, aber ich habe keinen Compiler gefunden, der aggressiv ist. (Für einen Compiler, der tatsächlich den Zugriff entfernt, müsste der Verweis an eine Funktion in einer zur Laufzeit geladenen DLL übergeben werden)
Sie müssen nur zu dem Teil springen, in dem Sie etwas lernen und das veröffentlichte Intel CPU Optimierungshandbuch
Diese stellen ganz klar fest, dass das Gießen zwischen float und int eine sehr schlechte Idee ist, da es einen Speicher vom int-Register zum Speicher benötigt, gefolgt von einem Laden in ein float-Register. Diese Vorgänge verursachen eine Blase in der Pipeline und verschwenden viele wertvolle Zyklen.
Ein Funktionsaufruf verursacht ziemlich viel Overhead, also würde ich das sowieso entfernen.
Hinzufügen eines Dummy + = i; Das ist kein Problem, solange Sie dasselbe Bit im alternativen Profil behalten. (Also der Code, gegen den Sie es vergleichen).
Last but not least: asm-Code generieren. Auch wenn Sie in asm nicht codieren können, ist der generierte Code in der Regel verständlich, da er hinter Codenamen steht und C-Code kommentiert. So wissen Sie, was passiert und welche Bits behalten werden.
R
ps. habe das auch gefunden:
%Vor%angeblich auch sehr schnell. Vielleicht möchten Sie dies auch profilieren. (obwohl es kaum portabler Code ist)
Geben Sie den Wert zurück?
%Vor%und dann können Sie auf der Aufrufseite alle Rückgabewerte zusammenfassen und das Ergebnis nach Abschluss des Benchmarks ausdrucken. Der übliche Weg, dies zu tun, ist irgendwie sicherzustellen, dass Sie vom Ergebnis abhängig sind.
Sie könnten stattdessen eine globale Variable verwenden, aber es scheint, als würde das mehr Cache-Misses erzeugen. In der Regel reicht es aus, den Wert einfach an den Aufrufer zurückzugeben (und sicherzustellen, dass der Aufrufer tatsächlich etwas damit macht).
Ein Mikro-Benchmark um diese Aussage herum ist nicht repräsentativ für die Verwendung dieses Ansatzes in einem echten Szenario; Die umgebenden Anweisungen und ihre Auswirkung auf die Pipeline und den Cache sind im Allgemeinen genauso wichtig wie jede gegebene Anweisung in sich selbst.
GCC 4 macht jetzt viele Mikrooptimierungen, die GCC 3.4 noch nie gemacht hat. GCC4 enthält einen Tree Vectorizer, der sich als sehr nützlich erweist, um SSE und MMX zu nutzen. Es verwendet auch die Bibliotheken GMP und MPFR, um bei der Optimierung von Aufrufen wie sin()
, fabs()
usw. zu helfen, und optimiert solche Aufrufe für ihre FPU, SSE oder 3D Now! Äquivalente.
Ich weiß, dass der Intel-Compiler auch bei diesen Optimierungen sehr gut ist.
Mein Vorschlag ist, sich keine Gedanken über Mikrooptimierungen wie diese zu machen - auf relativ neuer Hardware (alles, was in den letzten 5 oder 6 Jahren gebaut wurde), sind sie fast völlig irrelevant.
Edit: Bei neueren CPUs ist der Befehl fabs
der FPU weit schneller als ein Cast in int
und Bitmaske, und der Befehl fsin
wird im Allgemeinen schneller sein als die Vorberechnung einer Tabelle oder die Extrapolation einer Taylor-Reihe . Viele der Optimierungen, die Sie zum Beispiel in "Tricks of the Game Programming Gurus" finden würden, sind völlig unbegründet und könnten, wie in einer anderen Antwort ausgeführt, möglicherweise langsamer sein als Anweisungen auf der FPU und in SSE.
All dies ist auf die Tatsache zurückzuführen, dass neuere CPUs pipelined sind - Befehle werden dekodiert und an schnelle Recheneinheiten gesendet. Befehle laufen nicht mehr in Taktzyklen und sind empfindlicher für Cache-Misses und Inter-Instruction-Abhängigkeiten.
Lesen Sie die AMD- und Intel-Prozessor-Programmierhandbücher für alle wichtigen Details.
Tags und Links optimization c++