Hallo allerseits,
ich habe da ein Problem:
Also ganz kurz: Ich habe mehrere Versionen einer Klasse geschrieben,
deren Performance ich vergleichen möchte. Dazu werden sie jeweils in
einer Schleife "genutzt". Aber alle in einem gemeinsamen Programm.
Also quasi
1
zeitmessung_Version_1(); // mal bin ich schneller
2
zeitmessung_Version_2(); // mal ich
3
zeitmessung_Version_3(); // und mal ich
4
...
Lasse ich das Programm laufen, gibt es, pro run, aber immer mal einen
anderen "Gewinner", da die gemessenen Zeiten schwanken und zwar nicht
etwa gemeinsam, sondern so, dass mal die eine Version, mal die andere
Version, schneller ist.
(Eine mehrfache Messung pro Version ergibt allerdings untereinander
konstante Zeiten)
Etwa bei:
1
zeitmessung_Version_1(); // == t1
2
zeitmessung_Version_2(); // == t2
3
zeitmessung_Version_3(); // == t3
4
...
5
6
zeitmessung_Version_1(); // == t1
7
zeitmessung_Version_2(); // == t2
8
zeitmessung_Version_3(); // == t3
9
...
Kann mir das bitte jemand erklären?! Danke schon mal - ich dreh solange
durch.:)
MfG
Ohne deine Klasse zu sehen kann man das schwer beantworten. Performance
Unterschiede kann alle möglichen und unmöglichen Gründe haben. Das kann
an an Memory Alignment oder bei Multithreaded Sachen sowas wie False
Sharing liegen. Je Programmaufruf liegen die Daten dann mal mehr oder
weniger günstig im Speicher sodass sich verschiedene Ergebnisse ergeben.
PerformanceHeini schrieb:> Kann mir das bitte jemand erklären?! Danke schon mal - ich dreh solange> durch.:)
Die Funktion liefert keine beliebig genau Zeit. Je nachdem wo du in
deren Zeitraster anfängst und aufhörst bekommst du total
unterschiedliche Differenzwerte. Versuche mal heraus zu bekommen wie die
Auflösung bei deinem System überhaupt ist.
Versuche die Zeit von eine Handvoll Befehle zu ermitteln gehen meist
Gründlich schief. Daher sollte man solche Zeitmessungen immer so in eine
Schleife Packen das jeder Test mindestens einige Sekunden dauert damit
eben Messungenauigkeiten und auch schwankungen durch externe Einflüsse
minimiert werden.
Auch wichtig wäre, welche Uhr verwendet wird und in welchem Bereich die
gemessenen Zeiten liegen. Extrem kurze Zeitmessungen können sehr ungenau
werden.
Ist jetzt etwas umständlich, eine Testklasse zusammenzubasteln. Aber die
Versionen unterscheiden sich teilweise nur darin, dass
1
class V1{
2
A* a;
3
B* b;
4
... a == reinterpret_cast<A*>(b) ... // Version 1
5
}
6
class V2{
7
B* b;
8
A* a;
9
... b == reinterpret_cast<B*>(a) ... // Version 2
10
}
11
//etc
Also Versionen mit eig. identischem Speicherlayout.
..Was eigentlich gar keinen Unterschied machen sollte. Und selbst da
ergeben sich manchmal(!) deutliche Unterschiede. Manchmal ist es aber
auch identisch.
Wird mit -O3 etc kompiliert.
"alignas(16)" an die Klassen macht auch keinen Unterschied.
Wird alles jeweils für ca ne halbe Sekunde iteriert und mit
"chrono::high_resolution_clock", oder "chrono::system_clock" gemessen.
Gibts einen anderen, zuverlässigen Weg, verschiedene Versionen zu
vergleichen?
PerformanceHeini schrieb:> Ist jetzt etwas umständlich, eine Testklasse zusammenzubasteln. Aber die> Versionen unterscheiden sich teilweise nur darin, dass
Es ist etwas umständlich dir zu helfen, wenn wir nichtmal wissen um
welche Zeitskalen es geht oder was und wie gemessen wird. Unterscheiden
sich die zeiten um Nanosekunden oder Minuten? Versuchst du ein memcpy
oder das Parsen einer csv-Datei zu timen? Hat deine unbekannte CPU 1
oder 128 CPU-Kerne? Welches OS? Läuft nebenbei ein Browser oder ein
Fileindexer? ...
PerformanceHeini schrieb:> Hallo allerseits,> ich habe da ein Problem:> Also ganz kurz: Ich habe mehrere Versionen einer Klasse geschrieben,> deren Performance ich vergleichen möchte. Dazu werden sie jeweils in> einer Schleife "genutzt". Aber alle in einem gemeinsamen Programm.> Also quasi> zeitmessung_Version_1(); // mal bin ich schneller> zeitmessung_Version_2(); // mal ich> zeitmessung_Version_3(); // und mal ich> ...>> Lasse ich das Programm laufen, gibt es, pro run, aber immer mal einen> anderen "Gewinner", da die gemessenen Zeiten schwanken und zwar nicht> etwa gemeinsam, sondern so, dass mal die eine Version, mal die andere> Version, schneller ist.> (Eine mehrfache Messung pro Version ergibt allerdings untereinander> konstante Zeiten)> Etwa bei:> zeitmessung_Version_1(); // == t1> zeitmessung_Version_2(); // == t2> zeitmessung_Version_3(); // == t3> ...> zeitmessung_Version_1(); // == t1> zeitmessung_Version_2(); // == t2> zeitmessung_Version_3(); // == t3> ...>> Kann mir das bitte jemand erklären?! Danke schon mal - ich dreh solange> durch.:)> MfG
1. Dein Testcode darf nicht wegoptimierbar sein
2. Dein Test darf nicht zu kurz sein, lieber 30sek bis 1Min, sonst ist
das alles ungenau mit der Zeitmessung
3. Releasebuild sonst kommt noch Debug Heap, Pruefungen dazu
4. Keinen trivialkram testen der im Zeitverhalten im Rauscheb untergeht
5. Profiler wie z.B. VTune verwenden wenn es um Details geht
Danke schon mal für die Tipps.
Mit VTune, oder Profilern überhaupt, habe ich mich noch gar nicht
beschäftigt. Muss ich mal reinschauen.
Läuft auf Linux. Ich hab nur gehört, dass der Zeitegeber von Linux
genauer sein soll!?
Genügt eine volatile gegen das Wegoptimieren?
Es ist jetzt so, dass zwei Versionen jeweils die gleiche binary erzeugen
und die dritte nicht. Also wenn man sie nicht zusammen in einem
Test-Programm verwurstet, sonder einzeln.
Aber auch wenn man sie alle drei in ein Programm packt, verhalten sie
sich so, wie als würde man die "Einzelprogramme" aufrufen.
Ich würde erwarten, dass unterschiedlicher Code, der aber das gleiche
Einzelprogramm ergibt, sich wenigstens als Teilprogramm identisch
verhält (wenn schon nicht pro Programmaufruf).
Wenn ich mir also das Programm aus "Teil 1/3", "Teil 2/3" und "Teil 3/3"
vorstelle, und "Teil 1/3" == "Teil 2/3" (als Einzelprogramm kompiliert
zumindest) gilt, warum wird dann identischer Code nicht identisch
ausgeführt und ergibt identische Ergebnisse??
Sowas kommt z.B. raus (bei ein paar 10^6 Iterationen):
PerformanceHeini schrieb:> warum wird dann identischer Code nicht identisch> ausgeführt und ergibt identische Ergebnisse??
Weil die Umgebungsbedingungen nicht gleich sind. Da können anderen
Prozesse dazwischen laufen, der Cache ist anders, die Speicherzuordnung
(Verteilung auf DRAM-Bänke) ist anders, etc. Gegen andere Prozesse hilft
das Verpacken in Realtime (sched_setscheduler), das dürfte das viel von
deinem Rauschen schon entfernen. Der Rest ist schwieriger bis unmöglich
gleichzuhalten.
PerformanceHeini schrieb:> Sowas kommt z.B. raus (bei ein paar 10^6 Iterationen):> ...
Das sehe ich Unterschiede in der Größenordnung von ca. 5%. Das sind imho
(auch in Bezug auf die Messmethode, siehe unten) keine signifikanten
Unterschiede. Wenn du den gleichen Testfalle mehrfach laufen läßt,
liegst du doch schon im Bereich dieser Unsicherheit, oder?!
PerformanceHeini schrieb:> Wird alles jeweils für ca ne halbe Sekunde iteriert und mit> "chrono::high_resolution_clock", oder "chrono::system_clock" gemessen.
Ich sehe gerade nicht, ob die STL auch per Process/Thread Zeiten
unterstüzt. Anyway, die Funktionen basieren idR auf clock_gettime().
Dort gibt es dann auch CLOCK_PROCESS_CPUTIME_ID /
CLOCK_THREAD_CPUTIME_ID die möglicherweise für deine Messung besser
geeignet sind.
PerformanceHeini schrieb:> Wenn ich mir also das Programm aus "Teil 1/3", "Teil 2/3" und "Teil 3/3"> vorstelle, und "Teil 1/3" == "Teil 2/3" (als Einzelprogramm kompiliert> zumindest) gilt, warum wird dann identischer Code nicht identisch> ausgeführt und ergibt identische Ergebnisse??
Bei allem, was etwas komplexer als ein AVR-µC ist, gibt es neben den
reinen Taktzyklen der Instruktionen noch viele weitere Effekte, die die
Laufzeit beeinflussen, vor allem auf einem Multitasking-OS. Beim PC gibt
es z.B. Caches, Branch Prediction, eine superskalare Architektur,
dynamische Taktung, Multicore-CPU u.s.w.
Beim Multitasking-Betriebssystem kommt noch das Scheduling und die
Speichervirtualisierung hinzu. Und die Uhr, mit der du misst, kann auch
einen sehr großen Unterschied machen. Da wirst du nie identische Werte
bekommen.