Performanceoptimierung oder was .Net wirklich macht

Es kursiert immer wieder das Gerücht, dass bestimmte Anweisungen im Code „schneller“ und besser sind als andere. Gerade Entwickler die in älteren Sprachen angefangen haben, trauen den „neuen“ Sprachen nicht über den Weg.

Auf der Suche nach Erklärungen habe ich mich nun selber auch mal mit der Performance von alltäglichen Code beschäftigt. Wie wäre es z.B. mit String Operationen ? Da gibt es doch immer das Gerücht, das die Verbindung von Strings über „+“ viel zu langsam ist. Damit fange ich doch gleich mal an….Also schnell mal den Code hier hingepinselt (Code1) und auch gleich die Verbesserung dazu (Code2).

//Code1
Stopwatch watch = Stopwatch.StartNew();
string res = "abc" + "abc123";
watch.Stop();
//Code2
Stopwatch watch = Stopwatch.StartNew();
string res = String.Concat("abc" + "abc123");
watch.Stop();

Die Klasse Stopwatch zeigt, wie der Name schon sagt, die vertrichene Zeit an. Mit etwas Code drum rum zum Aufwärmen und Mitteln der Ausführungszeiten komme ich zu dem Schluss, dass beide 0 Millisekunden brauchen. Ein unbefriedigendes Ergebnis. Also die Messung umgestellt auf Ticks. Das Ergebnis ist, dass mit jeder Wiederholung die Ergebnisse etwas variieren, zwischen 1 und 4 Ticks. Das ist kein Unterschied…  Was ist da also los ? Was macht .Net hier wirklich ?

Um das weiter zu analysieren gibt es Profiler. Es gibt diverse Kaufprodukte (z.B. Redgate Performance und Memory Profiler ), aber auch Microsoft bietet gut versteckt einen Memory Profiler an.  Unter dem Link gibt es den CLRProfiler für  .Net 4 direkt von Microsoft. Man startet über den Profiler sein eigenes Programm und kann auswerten was das eigene Programm so treibt.

32bit Profiler starten, „Start Application“, Pfad auswählen und Programm ausführen lassen. Nachdem es ausgeführt wurde, Klick auf View, Call Graph.

Das Erste was man sieht sind Unmengen von Aufrufen, die mit dem eigenen Programm nichts zu tun haben  – damit verbrät also .Net schon für ein einfaches Consolenprogramm mehrere MB…

Wenn man die Ansicht etwas einschränkt kommt folgendes Ergebnis für meine beiden Möglichkeiten heraus:

Zu sehen ist eine zusammengestellte, gefilterte Ansicht der beiden relevanten Methoden. Der übersichtlichkeit halber Methode2 vor Methode 1.

Was ist da zu sehen ? Die Methode StringConcat2 führt  –  wie auch im Code zu sehen  – String.Concat aus. Aber was ist da bei StringConcat1 passiert ? Da findet sich auch ein String.Concat Aufruf darin! Den Aufruf der Klasse Datetime habe ich selber noch eingefügt, um überhaupt einen Aufruf bei der zweiten Methode zu sehen. Anscheinend hat .Net meine sinnlose Verbindung von nichtssagenden Strings ebenfalls für sinnlos befunden und gleich mal wegoptimiert. Erst durch das Hinzufügen eines „+ Datetime.Now.ToString()“ habe ich quasi volle Überzeugungsarbeit geleistet, sodass der Profiler auch etwas anzuzeigen hatte …

Folgerungen:

  • Das .Net-Runtime-Environment überarbeite den Code in nicht unerheblichem Maße
  • Die Diskussion um die bessere Variante derart einfacher, einzeiliger Stringverkettungen ist denkbar unsinnig.

 

Dieser Beitrag wurde unter .Net, Programmierung veröffentlicht. Setze ein Lesezeichen auf den Permalink.