Autor Beitrag
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: 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...
ausblenden volle Höhe C#-Quelltext
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);

  // und gleich nochmal, um den Jitter und Ähnliches
  // auszuschließen
  using (new TestStopwatch("By Hand"))
    for (int i = 0; i < times; i++)
      x *= x;

  TestStopwatch.StressTest("Stress",
    () => x *= x,
    times);

  // zum Spaß noch hinzugefügt, die Ergebnisse
  // hab ich nicht anders erwartet
  using (new TestStopwatch("Enumerable"))
    foreach (var i in Enumerable.Range(1, times))
      x *= x;

  Console.WriteLine(x);
  Console.ReadLine();
}

ausblenden 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 :nut: .

PS: Delphi ist noch einmal 10% langsamer :mrgreen: .
Einloggen, um Attachments anzusehen!
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 20451
Erhaltene Danke: 2264

Win 10
C# (VS 2019)
BeitragVerfasst: Mo 04.02.08 16:47 
Bei mir sind die Delegates zwar nicht schneller, aber auch nur unwesentlich (wahrscheinlich im Bereich der Messgenauigkeit) langsamer:
ausblenden 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! :zustimm: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Mo 04.02.08 20:22 
Oh lala, bin sogar mit angezogener Handbremse gefahren: Platform Target war zum Testen auf x86 gestellt.
ausblenden 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 :lol: . 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 :mrgreen: .
Robert_G
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 416


Delphi32 (D2005 PE); Chrome/C# (VS2003 E/A, VS2005)
BeitragVerfasst: Mo 04.02.08 21:11 
user profile iconKhabarakh hat folgendes geschrieben:
Dynamischer Code ist demnach 5% schneller als statischer :lol:
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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Mo 04.02.08 23:24 
Aber auch das ist doch kein Grund, dass sie schneller als inline Code sind, oder :gruebel: ? Es kann doch eigentlich nur so sein, dass der assemblierte hunz normale Code auf irgendeine Art nicht optimal ist.
Robert_G
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 416


Delphi32 (D2005 PE); Chrome/C# (VS2003 E/A, VS2005)
BeitragVerfasst: Di 05.02.08 01:32 
user profile iconKhabarakh hat folgendes geschrieben:
Aber auch das ist doch kein Grund, dass sie schneller als inline Code sind, oder :gruebel: ? 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: 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).
ausblenden 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 :( .