Geschwindigkeit einiger Operationen
-
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.
-
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).