Autor |
Beitrag |
Kha
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: Mo 04.02.08 16:35
Dass Delegates seit 2.0 alles andere als langsam sind, habe ich gehört. Trotzdem wollte ich lieber auf Nummer Sicher gehen, bevor ich meiner TestStopwatch-Klasse eine StressTest-Method verpasse, die einfach n-mal einen Delegate aufruft und die Zeit misst. Die Ergebnisse haben mich nicht bestätigt, sondern eher sprachlos gemacht...
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32:
| static void Main(string[] args) { int times = 1000000000; float x = 1;
using (new TestStopwatch("By Hand")) for (int i = 0; i < times; i++) x *= x;
TestStopwatch.StressTest("Stress", () => x *= x, times);
using (new TestStopwatch("By Hand")) for (int i = 0; i < times; i++) x *= x;
TestStopwatch.StressTest("Stress", () => x *= x, times);
using (new TestStopwatch("Enumerable")) foreach (var i in Enumerable.Range(1, times)) x *= x;
Console.WriteLine(x); Console.ReadLine(); } |
Quelltext 1: 2: 3: 4: 5:
| By Hand: 5074 ms Stress: 4954 ms By Hand: 5067 ms Stress: 4955 ms Enumerable: 8876 ms |
Im Anhang noch das gesamte Projekt, da ich nicht erwarte, dass ihr mir das Ergebnis abkauft .
PS: Delphi ist noch einmal 10% langsamer .
Einloggen, um Attachments anzusehen!
|
|
Christian S.
Beiträge: 20451
Erhaltene Danke: 2264
Win 10
C# (VS 2019)
|
Verfasst: Mo 04.02.08 16:47
Bei mir sind die Delegates zwar nicht schneller, aber auch nur unwesentlich (wahrscheinlich im Bereich der Messgenauigkeit) langsamer:
Quelltext 1: 2: 3: 4: 5:
| By Hand: 10786 ms Stress: 10818 ms By Hand: 10606 ms Stress: 10804 ms Enumerable: 26184 ms |
Auf jeden Fall mal ein interessanter Test! Insbesondere in Multithreaded-Anwendungen braucht man Delegates ja ständig, um sie an Invoke zu übergeben
_________________ Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
|
|
Kha
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: Mo 04.02.08 20:22
Oh lala, bin sogar mit angezogener Handbremse gefahren: Platform Target war zum Testen auf x86 gestellt.
C#-Quelltext 1: 2: 3: 4:
| By Hand: 4358 ms Stress: 4072 ms By Hand: 4295 ms Stress: 4057 ms |
Dynamischer Code ist demnach 5% schneller als statischer . Auf einem anderen Rechner komme ich auf etwa den gleichen Wert - außer natürlich unter Mono .
Der Asm-Code würde mich einmal interessieren, aber a) wüsste ich spontan nicht, wie man an ihn herankommt (über VS kann es ja nur mit angehängtem Debugger funktionieren) und b) würde ich mit dem Ergebnis sowieso nichts anfangen können .
|
|
Robert_G
Beiträge: 416
Delphi32 (D2005 PE); Chrome/C# (VS2003 E/A, VS2005)
|
Verfasst: Mo 04.02.08 21:11
Khabarakh hat folgendes geschrieben: | Dynamischer Code ist demnach 5% schneller als statischer |
Ist nicht dynamischer Code. Ist nur ein Zeiger auf den Code.
Delegates sind dann schneller, wenn sie auf virtuelle Methoden zeigen.
Denn beim zuweisen wird der virtual dispatch nur einmal ausgeführt und dann immer direkt die richtige Implementierung aufgerufen.
Das ist der gleiche Effekt wie bei Interfaces.
Aber: Delegates verhindern JIT-Optimierungen, und damit meine ich Inlining, da der JITter für andere Optimierungen zu dumm ist.
|
|
Kha
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: Mo 04.02.08 23:24
Aber auch das ist doch kein Grund, dass sie schneller als inline Code sind, oder ? Es kann doch eigentlich nur so sein, dass der assemblierte hunz normale Code auf irgendeine Art nicht optimal ist.
|
|
Robert_G
Beiträge: 416
Delphi32 (D2005 PE); Chrome/C# (VS2003 E/A, VS2005)
|
Verfasst: Di 05.02.08 01:32
Khabarakh hat folgendes geschrieben: | Aber auch das ist doch kein Grund, dass sie schneller als inline Code sind, oder ? Es kann doch eigentlich nur so sein, dass der assemblierte hunz normale Code auf irgendeine Art nicht optimal ist. |
Inlining auf Teufel komm raus kann die Anzahl der zu intialisierenden locals brachial erhöhen.
Es kann auch einfach sein, dass die statische Methode hinter dem delegate klein genug ist, so dass der JIT sie gut genug kapiert hat um den loop auszurollen, oder CPU Register nehmen konnte.
Der andere Code war ja innerhalb des Method bodies, die Delegates sind eigene Methoden.
|
|
Kha
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: Di 05.02.08 22:14
Ich muss mich wohl entschuldigen, mir ist gerade eben erst eingefallen, wie Closures eigentlich implementiert werden :duck: . So sieht das Ganze aus, nachdem jede Schleife ihre eigene x-Variable bekommt (= Die Inline-Loops wirklich lokale Variablen benutzen und nicht die autogenerierten Felder der Lambdas).
Quelltext 1: 2: 3: 4:
| Inline: 3541 ms Stress: 4263 ms Inline: 3527 ms Stress: 4153 ms |
Während der erste Code also ein Inline<=>Delegate-Vergleich war (beide Male ldfld), war das nun eher ein ldloc<=>ldfld-Vergleich. Da man bei anonymen Methoden aber gebundene Variablen kaum vermeiden kann (schon allein, damit sie nicht einfach wegoptimiert werden), wird es wohl meistens auf den zweiten Fall herauslaufen, womit man bei meiner StressTest-Methode immer mit einem Overhead rechnen muss. Schade .
|
|
|