Hallo johnnycc,
Zitat |
Der erste Aufruf dauert immer länger. Z.B.:
|
.NET basiert aktuell auf einem JIT (just in time compiler), der den vom C#-Compiler generierten IL-Code erst zu ausführbaren Maschinencode übersetzen muss. Dieses Übersetzung IL -> Maschinencode basiert beim ersten Aufruf*.
D.h. die Methode
foo muss vom JIT verarbeitet werden, genauso wie
Task.Run, etc. Eben alles das bisher noch nicht geJITet wurde.
Weiters ist in .NET vieles auf "lazy-initialisation" getrimmt. D.h. -- ähnlich wie beim JITen -- dass beim ersten Zugriff die Ressourcen, die benötigt werden, bereitgestellt werden. Dies hat den Vorteil gegenüber "eager-initialisation", bei dem alle Ressourcen unmittelbar beim Start zur Verfügung gestellt werden, dass nur jenes bereitgestellt wird, das auch tatsächlich benötigt wird -- das sogenannte "you pay for what you play".
Hier ist es bei
Task.Run, das dem
TaskScheduler anweist den übergebenen Delegaten auszuführen, nicht anders. Kurz: die benötigte TPL-Infrastruktur wird beim ersten Aufruf bereitgestellt. Für die folgenden Aufrufe sind diese Schritte nicht mehr nötig.
Aus diesen Gründen sollten bei Messungen eine Warmlaufphase eingebaut werden, um diesen "Start-Overhead" zu eliminieren. Tools wie das zitierte ttps://benchmarkdotnet.org/ machen das genau so.
* mit .NET Core 2.2 wird ein Tiered-Jitting der Standard werden, d.h. der IL-Code wird ohne Optimierungen nach Maschinencode übersetzt und falls die Runtime bemerkt dass eine Methode "heiß" ist, so wird diese erneut kompiliert, aber mit vielen Optimierungen, und anschließend diese optimierte Version verwendet. Unterm Strich soll das schnellere Startzeiten der Anwendung bringen und im Betrieb bessere Laufzeiten, da eben besser optmierter Code verwendet werden kann. Der JIT muss bis dato immer die Balance zwischen Schnelligkeit der Kompilierung und Optimierung (zeitaufwändig) treffen.
Hallo weismat,
Zitat |
StopWatch ist auch nicht sauber für das Messen von Microbenchmarks.
|
Warum?
https://benchmarkdotnet.org/ misst intern auch mit der
Stopwatch.
Der Vorteil von BDN ist dass es mehrere Messungen durchführt (inkl. Warmup, etc.) und dann statistische Aussagen liefert, so dass Messfehler (Standardabweichung, ...) ins Ergebnis einfließen können.
Das lässt sich selbst auch umsetzen...nur wenn es das mit BDN schon gibt kann auch das fertige getestete Tool verwendet werden, zumals die Verwendung recht einfach ist.
Hallo Abt,
Zitat |
aber natürlich auch an der Schleife.
So eine Schleife ist so ziemlich der größte potentieller Messfehler solch einer Angelegenheit.
Es ist überhaupt nicht garantiert, dass die Schleife immer die gleiche Zeit benötigt; der Code wird schließlich zur Laufzeit optimiert.
|
Mag sein dass ich dich hier falsch verstanden habe, aber eine
for-Schleife ist so ziemlich das exakteste in solch einer Angelegenheit ;-)
Tiered JITing außer Acht gelassen: ist der Code, der die Schleife beinhaltet, einmal durch den JIT gelaufen, so bleibt dieser Maschinencode bis ans Ende des Prozesses auch gleich. Somit sind es immer die exakt gleichen Anweisungen die in der Schleife ausgeführt werden.
Mögliche Unsicherheiten für die Messungen kommen hier nur den CPU-Instructioncache ins Spiel, aber bei so einem kleinen Schleifenrumpf ist mit ziemlicher Sicherheit davon auszugehen, dass hier kein Cachemiss auftreten wird beim Rücksprung an den Schleifenbeginn.
mfG Gü