Geschwindigkeit einiger Operationen
-
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.
-
Ok, wahrscheinlich hast Du recht und Du kennst Dich auch besser mit den Optimierungsfunktionalitäten der Compiler aus. Das mit C++ ist schon ne Weile her bei mir. Ich hab erst vor ein paar Wochen wieder angefangen.
-
Nexus schrieb:
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.
die ausreißer liegen aber absolut nicht am eigenen code. die kommen, weil andere prozesse die cpu bekommen, die kommen, weil code eingelagert werden muss, die kommen weil der prozessorcache noch leer war, man hat den eindruck, die kämen einfach so.
mit einfach einer langen schleife und dem mittelwert fällst du ganz super auf die effekte rein, daß der rechner schneller ist, wenn er vorher ein paar sekunden ruhen durfte, daß die zweite schleife im selben prog normalerweise gewinnt (vielleicht weil keine nachwirkungen des einlagerns mehr laufen und freispeicher schon im pool ist, oder weil die anderen programme (der explorer, die ide) endlich ausgezappelt haben).
kleine sachen wie x+=(i==0) versus x+=(i<=0) kann man zuverlässig auf den takt genau ausmessen, indem man kurzlaufende schleifen nimmt (so, daß es öfters mal vorkommt, daß ein ganzer durchlauf ohne prozesswechsel klappt) und von vielen versuchen den schnellsten nimmt.andere sachen müssen anders gemessen werden, für eine new-funktion lieber mittelwert.
-
Da hast Du recht. Mit dem Mittelwert stimme ich mit Dir überein. Aber über längere Zeiträume sollten die Interrupts des Systems doch eigentlich auch rausgemittelt werden oder?
-
Ich programmier mir jetzt mal was mit 1 000 000 Messungen, die über schleifen von einmal 100 und einmal 1 000 000 000 Durchläufen gehen. Sollte etwas länger dauern, aber dann sollten sich die Interrupts eigentlich statistisch raus mitteln, wenn ich den Mittelwert bilde (und auch nichts anderes an dem Rechner mache).
-
Senfer schrieb:
Da hast Du recht. Mit dem Mittelwert stimme ich mit Dir überein. Aber über längere Zeiträume sollten die Interrupts des Systems doch eigentlich auch rausgemittelt werden oder?
nicht wirklich. ich messe 10 minuten lang variante a und alles geht gut. dann messe ich 10 minuten lang variante b und dabei kommt
- ein automatisches update übers netzwerk rein
- eine dos-attacke auch übers netz
- der indizierungsdienst indiziert was
- der prozessor wird heiß und wird runtergetaktet
- idle-defrag geht an
- teile der ide werden ausgelagert
mist. das ist einfach zu viel mist, daß man da in einigermaßen überschaubaren zeiten etwas herausmitteln könnte. mit meiner methode haste bei kleinen tests über ca 20 sek vier oder fünf gleichbleibende stellen bei der zeitmessung, mit einfachem mitteln nur eine oder zwei.
-
Stimmt, vielleicht sollte ich es aufgeben. Ich frag am Montag noch mal den Guru, warum das mit dem Vergleich so ist, dann geb ich Dir bescheid, ok?
-
Senfer schrieb:
Stimmt, vielleicht sollte ich es aufgeben. Ich frag am Montag noch mal den Guru, warum das mit dem Vergleich so ist, dann geb ich Dir bescheid, ok?
gerne.
vielleicht weiß ich, was er meint.
erstmal machen wir eine zeitreise ins jahr damals und zum 8086.
um x>y (unsigned) zu prüfen, muß ich ans carray-flag nach einer subtraktion kommen. prinzipiell mit SUB.
besser ein CMP x,y und das ist intern ein SUB x,y, nur daß das ergebnis nicht geschrieben wird, es werden nur die flags gesetzt.
wenn ich gegen 0 vergleiche, dann eben CMP x,0.mal die zeiten nehmen von http://www.tu-chemnitz.de/informatik/RA/lehre/mop/praktika/AsmBefehle8086.pdf
sub AX,0 brauchte 4 takte
und alle flags werden berechnet, auch das gewünschte carry-flag.aber für (x=0) reicht es mir ja schon, ans zero-flag zu kommen. dazu muß ich den wert bloß einmal durch die ALU schicken. das geht zum beispielschon mit
AND AX,AX
braucht nur 3 takte. also kann (x=0) zu schnellerem code gemacht werden als (x>0).ich erwarte aber, daß halbwegs moderne compiler das auch selber wissen und für mich erledigen.
-
im endeffekt ist vollkommen egal was schneller ist

BTW: ein guter compiler wird bei unsigned typen und bool den gleichen code erstellen...
-
Senfer schrieb:
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
)Bitte mach den Debugmodus aus.

Da kannst du garnix messen, weil dir ein vernünftiger Compiler das mit x alles weg optimiert, weil du es garnicht verwendest und deine iGrenze konstant ist.Das ist wieder mal so ein premature microoptimierungs gescheiße hier.

-
Ihr seid so schlecht schrieb:
Das ist wieder mal so ein premature microoptimierungs gescheiße hier.

ach, das wort "premature" lese ich hier oft. ist das nicht seltsam?
die hardware kennenzulernen und zu lernen, was ein compiler so kann, das ist doch etwas im allerhöchsten maße positives.
-
volkard schrieb:
ach, das wort "premature" lese ich hier oft. ist das nicht seltsam?
Ne, nicht seltsam, ganz normal, dass das hier immer wieder gemacht wird, bei so vielen Anfängern
die hardware kennenzulernen und zu lernen, was ein compiler so kann, das ist doch etwas im allerhöchsten maße positives.
ja, aber wieso meinen dann immer noch so viele, dass sie es besser können als der Compiler? Wartbaren und leicht zu erweiternden Code zu schreiben ist so gut wie immer wichtiger, als solcher kleinscheiß, wie < vs. ==...