Geschwindigkeit einiger Operationen
-
ich gehe im zweifelsfall davon aus, daß die optimierungen eingeschaltet sind.
guden schrieb:
1. Vergleich
if(3!=4);und
if(3>4);Ist es Performancegünstiger auf Ungleichheit oder auf größer/kleiner zu testen?
völlig gleich schnell.
aaber, wenn if(i>j) und if(i!=j) kann, wenn i und j iteratoren sind, erheblich anders sein, generell sind == und != schnell und die anderen vergleiche können überraschend sein, nämlich wenn sie in verkettete listen reinzeigen.guden schrieb:
2. Inkrementieren
++i;und
i=i+1;Ist hier die (pre)Inkrementierung schneller? Denn immerhin gibt es ja nur eine mögliche Addition. Vielleicht lässt sich das ja effektiver implementieren
bei int wieder echt gleich. bei iteratoren ist ++i am schnellsten (weil eine referenz auf *this zurückgegeben werden kann), danach kommt i++ (weil eine kopie des alten werts zurückgegeben werden muß) und noch eine winzigkeit später ist i=i+1 (bei listen, weil erstmal die schleife angeworfen wird).
guden schrieb:
3. Vergleichen II
int x = 0;if(x)und
if(x==0)Im zweiten Fall muss erst der Vergleichoperator durchgeführt werden, der dann den Wert zurück gibt, der erst dann von if ausgewertet wird. Somit müsste doch erstere Variante schneller sein, oder?
bei int wieder exakt gleich schnell, bei klassen gewinnt if(x).
guden schrieb:
4. bool vs. int
bool x = 1; int y = 1;while(x)und
while(y)In dem Falle müsste doch das 1. schneller sein, da im 2. Fall int erst in bool convertiert werden muss, oder?
kann ich nicht sagen. ich vermute, sie sind gleich schnell. es muß nicht konvertiert werden, denke ich.
guden schrieb:
5. Anweisungsblock
if(x) dosomething;und
if(x) { dosomething; }Wirken sich hier die Klammern des Anweisungsblockes auf die Performance aus? Ich kanns mir zwar nicht vorstellen, aber die werden ja wohl irgendwie vom compiler berücksichtig werden, oder?
völlig gleich. es ist egal, ob der sprung über 10 oder über 50 bytes geht.
-
volkard schrieb:
guden schrieb:
3. Vergleichen II
int x = 0;if(x)und
if(x==0)Im zweiten Fall muss erst der Vergleichoperator durchgeführt werden, der dann den Wert zurück gibt, der erst dann von if ausgewertet wird. Somit müsste doch erstere Variante schneller sein, oder?
bei int wieder exakt gleich schnell, bei klassen gewinnt if(x).
Meinst du jetzt Pointer auf NULL prüfen, da wird doch der Compiler in beiden Fällen den gleichen Code erzeugen. Oder meinst du safe bool?
[/cpp]Wirken sich hier die Klammern des Anweisungsblockes auf die Performance aus? Ich kanns mir zwar nicht vorstellen, aber die werden ja wohl irgendwie vom compiler berücksichtig werden, oder?
völlig gleich. es ist egal, ob der sprung über 10 oder über 50 bytes geht.
Klammern erzeugen keine Bytes.
-
Meinst du jetzt Pointer auf NULL prüfen, da wird doch der Compiler in beiden Fällen den gleichen Code erzeugen. Oder meinst du safe bool?
ich meinte irgendwelche operatoren, die if(x) erlauben, zum beispiel safe bool. unterschiede nur, wenn x eine instanz einer klasse ist und die überladung des op== nicht inline ist und deswegen der optimierer nicht zuschlagen kann.
Klammern erzeugen keine Bytes.
klar. ich hatte irgendwie im kopf, daß di8e version mit klammern nur gemacht worden sein konnte, weil mehrere anweisungen da stehen. ist ja gar nicht so.
-
falls es dir so auf performance ankommt, dann nimm
for (;;) { //... }und nicht
while (true) { //... };bb
-
falls das jetzt so weiter geht, sag ich mal, dass das unwichtige premature microoptimierung ist.
-
unskilled schrieb:
falls es dir so auf performance ankommt, dann nimm
for (;;) { //... }und nicht
while (true) { //... };bb
Glaub ich übrigens nicht, dass das der compiler nicht selber merkt und das gleiche draus macht. Hast du nen Beweis dafür?
-
Ein guter Compiler sollte so gar ne Warnung ausgeben, dass die Abbruch-Bedingung konstant ist ^^
MSVC:
aufwhile(true) {};folgt
1>.\main.cpp(23) : warning C4127: conditional expression is constantAbgesehen von der Warnung ist das Ergebnis aber wirklich gleich...
Die Funktion, die die Schleife aufruft, damit die nicht komplett wegoptimiert wird - noinline damit man im disassembly auch durchsieht ^^
__declspec(noinline) void func() { std::cout << "a"; }for (;;) { func(); 01091010 call func (1091000h) } 01091015 jmp main (1091010h)while (true) { func(); 00D91010 call func (0D91000h) }; 00D91015 jmp main (0D91010h)bb
-
volkard schrieb:
aaber, wenn if(i>j) und if(i!=j) kann, wenn i und j iteratoren sind, erheblich anders sein, generell sind == und != schnell und die anderen vergleiche können überraschend sein, nämlich wenn sie in verkettete listen reinzeigen.
Aus diesem Grund implementieren die Iteratoren verketteter Listen Operatoren wie
<,<=,>,>=und auch+,+=,-,-=üblicherweise nicht. Da müsste man schon zustd::advance()undstd::distance()greifen.unskilled schrieb:
Abgesehen von der Warnung ist das Ergebnis aber wirklich gleich...
Die Funktion, die die Schleife aufruft, damit die nicht komplett wegoptimiert wird - noinline damit man im disassembly auch durchsieht ^^
In der Regel sollte man aber davon ausgehen können, dass Optimierungen an sind, und somit ist auch die
noinline-Nötigung für den Praxisfall irrelevant.
-
Nexus schrieb:
unskilled schrieb:
Die Funktion, die die Schleife aufruft, damit die nicht komplett wegoptimiert wird - noinline damit man im disassembly auch durchsieht ^^
In der Regel sollte man aber davon ausgehen können, dass Optimierungen an sind, und somit ist auch die
noinline-Nötigung für den Praxisfall irrelevant.Es ging ja auch nicht um den Praxisfall - habs extra noch mal fett für dich geschrieben
^^
Es ging einfach nur darum etwas in der Schleife zu machen und im Disassembly trotzdem noch ganz einfach durchzusehen - weil dort halt kein std::cout gedöns steht sondern viel eher nur ein simpler funktions-aufrufbb
PS: Trotzdem ist while (true) imho eine doofe endlosschleife und man sollte for ( ;; ) bevorzugen - es erzeugt einfach keine warnings - und das es normalerweise ne warning zur Folge hat zeigt imho ja nur, dass es nicht der richtige Weg ist!? außerdem hat man bei der for-schleife die gleiche möglichkeit, doch eine abbruch-bedingung einzubringen - und hat auch den vorteil, dass man variablen im schleifenkopf initlialisieren kann etc.
-
unskilled schrieb:
- es erzeugt einfach keine warnings - und das es normalerweise ne warning zur Folge hat zeigt imho ja nur, dass es nicht der richtige Weg ist!?
Nein. Je nach Kontext können Warnings entstehen. Es sind ja deshalb nur Warnungen und keine Fehler, weil sie nur darauf hinweisen, dass man möglicherweise etwas falsch macht. Schau beispielsweise mal in Standard- oder Boost-Header, da werden teilweise Warnungen systematisch deaktiviert.
unskilled schrieb:
außerdem hat man bei der for-schleife die gleiche möglichkeit, doch eine abbruch-bedingung einzubringen - und hat auch den vorteil, dass man variablen im schleifenkopf initlialisieren kann etc.
Es geht aber nicht um die Vorteile von For- gegenüber While-Schleifen. Wir betrachten hier nur den Fall einer Endlosschleife, da sind Schleifenkopf-Anweisungen nicht wichtig.
Ich finde, es ist eher Geschmackssache, was man verwendet. Auch wenn
for(;; )die nativere Variante ist, kann es gut sein, dass manwhile(true)besser findet, sei es, weil man die Endlosschleife schneller erkennt. Aber im Zweifelsfalle istforhier schon besser, da stimme ich dir zu. Und da solche Schleifen sowieso ziemlich selten vorkommen...
-
Nexus schrieb:
unskilled schrieb:
- es erzeugt einfach keine warnings - und das es normalerweise ne warning zur Folge hat zeigt imho ja nur, dass es nicht der richtige Weg ist!?
Nein. Je nach Kontext können Warnings entstehen. Es sind ja deshalb nur Warnungen und keine Fehler, weil sie nur darauf hinweisen, dass man möglicherweise etwas falsch macht. Schau beispielsweise mal in Standard- oder Boost-Header, da werden teilweise Warnungen systematisch deaktiviert.
Das weiß ich ^^
Aber man spart sich eben so eine weitere Warning auszublenden...
Erspart mir im besten Falle 2 Dateien - da meine Source-Codes immer so aussehen:#include "header.h" #include "warnings_off.h" //implementationen #include "warnings_on.h"und wenn es eine variante gibt, die mir keine warnings bringt und trotzdem keine nachteile hat... warum sollte ich mich nicht für diese entscheiden?
bb
-
Ja, du hast schon Recht, grundsätzlich ist
for(;; )schon besser. Ich finde es nur nicht tragisch, wenn jetzt jemandwhile(true)schreibt.Mit meiner Aussage bezog ich mich auch auf deinen eher allgemeinen Satz, dass Warnungen auf den falschen Weg hinweisen würden.
Aber ich denke, wir sind uns einig.

-
Du kannst bei Funktionen die Variablen als Referenzen übergeben:
z.B.
int add(int& a, int& b) { return (a + b); }ist besser als
int add(int a, int b) { return (a + b); }Wenn Du vergleiche mit 0 machst, ist das besser, wenn Du
bool b = (a != 0)
alse wenn Du
bool b = (a > 0) schreibst, weil da intern nur die Register verglichen werden.
Hoffe, das kann Dir weiter helfen.
Martin
-
Das mit den Referenzen lohnt sich bei skalaren Typen wahrscheinlich eher nicht, da diese sehr schnell kopiert werden und die Dereferenzierung auch Zeit braucht. Mal abgesehen von der Semantik, der eher eine Const-Referenz angemessen wäre.
Und wie meinst du das genau mit den Registern?
-
Das mit den Referenzen mach ich selber gerade. Hab ich aber von hier:
[url]
http://www.mathematik.uni-marburg.de/~cpp/referenzen/
[/url]
Kapitel Referenzübergabe. Der Funktionsaufruf kostet natürlich Zeit, da hast Du recht.Das mit den Registern ist so:
Ich hab das von nem Guru, der Steuergeräte (für Autos) programmiert und dafür alles mögliche irgendwelche Optimierungen kennt.
Mit Register meine ich die Speicherzellen. D.h. der Vergleich wird direkt im Speicher durchgeführt, was anscheinend schneller ist, aber genau kenn ich mich da auch nicht aus.
Hab aber gerade einen Performancetest gemacht:
Wenn Du Windows hast und Deine Entwicklungsumgebung das unterstützt, kannst Du das auch kurz machen:
#include <cstdlib> #include <iosteam> #include <windows.h> using namespace std; int main(int argc, char *argv[]) { int iGrenze = 100000000; DWORD dwStart1 = 0; DWORD dwStart2 = 0; DWORD dwStop1 = 0; DWORD dwStop2 = 0; bool x = false; dwStart1 = GetTickCount(); for(int i = 0; i < iGrenze; i++) x = (i > 0); dwStop1 = GetTickCount(); dwStart2 = GetTickCount(); for(int i = 0; i < iGrenze; i++) x = (i == 0); dwStop2 = GetTickCount(); cout << "Test 1: " << dwStop1 - dwStart1 << endl; cout << "Test 1: " << dwStop2 - dwStart2 << endl; }Das kannst Du ein paar mal machen, kommt aber immer raus, dass das untere um ca. ein fünftel schneller ist.
(hoffe, ich hab den Code richtig abgetippt, war nämlich auf dem anderen Rechner
)
-
Das geht auch mit den Referenzen:
Da ist auch die Referenz um ca. ein fünftel schneller;
int addVal(int a, int b) { return (a + b); } int addRef(int& a, int& b { return (a + b); }
-
dann vertausch mal oben und unten. dann ist schon wieder das untere schneller. oder?
zum messen solltest du alle optimierungen anmachen und von hunderten von messungen diejenige mit der kleinsten zeit nehmen.
-
hab ich gerade gemacht. Kein Unterschied.
Auch mit den Optimierungen ein (da sind natürlich jetzt wahrscheinlich keine MMX, SSE-Optimierungen drin).
Messungen hab ich so an die zehn gemacht, aber das Ergebnis ist immer ungefähr das gleiche.
-
volkard schrieb:
und von hunderten von messungen diejenige mit der kleinsten zeit nehmen.
Wäre es nicht sinnvoller, den Mittelwert zu nehmen? Ausreisser gibt es immer, und die sind nicht praxisrelevant.
Ich habe die Messung wegen < und == übrigens auch gemacht, mit anderen Zeitmessungs-Funktionen. Beides war ziemlich genau gleich schnell. Gleiches gilt für Referenz und Kopie. Wahrscheinlich kann das der Compiler gut optimieren.
Senfer schrieb:
Das mit den Referenzen mach ich selber gerade. Hab ich aber von hier:
http://www.mathematik.uni-marburg.de/~cpp/referenzen/
Kapitel Referenzübergabe. Der Funktionsaufruf kostet natürlich Zeit, da hast Du recht.Nicht der Aufruf, die Dereferenzierung. Und schau mal, was dort steht:
3. Da nichts kopiert und vor allem kein Konstruktor aufgerufen werden muß, entsteht ein Laufzeitvorteil.
Bei Klassentypen lohnt sich eine Const-Referenz-Übergabe schon von der Performance her. Da skalare Typen aber so klein sind und keine Konstruktoren haben, lohnt es sich nicht wirklich.
-
Die Messungen sollten richtig sein, der TickCount kann laut
MSDN:
[url]
http://msdn.microsoft.com/en-us/library/ms724408(VS.85).aspx
[/url]ca. 47 Tage lang laufen, ohne dass er auf Null zurück springt.
Aber mit dem an den Kern binden hast Du natürlich recht. Ich glaube jedoch wirklich, die Messungen einigermaßen ok sind, habe es jetzt noch mal auf 1 Mrd. erhöht und noch mal ca. 20 Messungen mit vertauschen gemacht.
Die Referenzen und der Vergleich auf 0 ist immer noch ca. 20% schneller.
Aber ich lass mich gern vom Gegenteil überzeugen.