Ein Schlafmittel namens dotNet

Meine “Beziehung” zum dotNet Framework ist eine sehr sehr schwierige. Die Kernaufbau ist mir so verhasst, dass ich stets versuche an dotNet Sprachen nicht anzustreifen.

Gleichzeitig halte ich den Klassenaufbau des Frameworks selbst für ausgezeichnet durchdacht und vorbildhaft. Nicht grundlos habe ich viele Klassen und Methodekonventionen in die C++ Schicht des GATE Frameworks aufgenommen.


Neulich saß ich wieder vor einem meiner virtuellen Windows Server, die auf meinen stromsparenden Atom Rechnern liefen.
Dann kam das monatliche dotNet Update und nach einer Stunde mühsamen Updates (zusammen mit anderen nativen Windows Updates), lief dann über 2 Stunden die dotNet Runtime Optimierung und fraß die Systemressourcen derart auf, dass das System unbenutzbar wurde.

TaskMgr dotNet Update

Neben der CPU Auslastung waren vor allem die Festplattenzugriffe ein stark bremsenden Faktor.

Die Performance-Illusion

Die Idee eines Just-In-Time Compilers, (JIT) der erst zur Laufzeit Pseudocode in CPU-Code übersetzt, hat schon Java nie performant umsetzen können. Ich habe daher nie verstanden, wie man die Fehlentscheidung treffen konnte, die bestehende COM Struktur in Windows durch langsame JIT-dotNet Komponenten zu ersetzen.

Das hat man uns seit 2000 immer wieder als “beste Lösung” verkauft und erst seit ein paar Jahren rudert man mit dotNet Native und WinRT kräftig zurück.

Es stimmt zwar, dass manche Operationen mit modernen CPU-Instruktionen wesentlich schneller laufen. Ein C oder C++ Programm aus dem Jahr 2003 hätte memcpy in einen Byte-zu-Byte Transfer übersetzt. Bereits 5 Jahre später war es aber üblich, dass memcpy auf das Alignment der Daten prüft und dann SSE oder neuere Vektor-Instruktionen einsetzt. Das geht tatsächlich dann 2 bis 4 mal schneller.

Man könnte also argumentieren, dass ein dotNet Programm von 2003 im Jahr 2008 ohne Zutun ob der neuen Instruktionen schneller ausgeführt wird, während das C/C++ Kompilat keinen Performancegewinn ermöglicht.
Ja, das ist auch so. Man kann das teils schon bei der ZLIB gut nachweisen. (Habe das mal bei MSVC 2012 vs MSVC 2005 nachgemessen.)

Aber mal ehrlich! Zeitkritische Software wird stets weiterentwickelt und spätestens alle paar Jahre neu ausgerollt. Im Normalfall benutzt also niemand X-Jahre veraltete Software und hofft darauf, dass die Runtime-Umgebung diese “schneller” macht.
Und zum Zeitpunkt der Veröffentlichung sind die nativen Routinen dank ihrer hohen Optimierung stets schneller (oder im schlechtesten Fall gleich schnell) wie “verwaltete” Programme.

Versionschaos

“Ein Framework” gab es seit v2.0 in dotNet leider nicht mehr. Mit jeder neuen Version kamen Features hinzu und weil die Entwicklungsumgebungen von Microsoft immer auf die neueste Inkarnation verweisen (und die Entwickler offenbar nichts hinterfragen), braucht fast jede Software ihr eigenes dotNet Paket.

Ganz besonders schlimm ist die Integration ins Betriebssystem. Ein dotNet 3.5 ist ein optionaler Teil von Windows 7 und kann nur per Windows Setup installiert werden. Ältere und neuere Versionen müssen jedoch per MSI Setup installiert werden, aber das macht die Administration und das Deployment schwierig.
Und Windows 8 bis Windows 10 stellen wieder weitere 4.X Profile zur Verfügung, die auf älteren Systemen separat und eben “anders” nachinstalliert werden müssen.
Dafür, dass dotNet letztlich nur ein “Wrapper” rund um das Betriebssystem ist, und kein eigenes Subsystem, sind diese Inkompatibilitäten und Installationshürden nicht gerade nachvollziehbar.

Und eben weil sich niemand mehr auskennt, welches Feature in welcher Version mit welchem anderen funktioniert, entsteht der “Druck” zur neuesten Version und einem ständigen Update mit immer wieder neuen Bugs.
DotNet Patches haben inzwischen die Größe der “üblichen” OS Patches erreicht bzw. diese überschritten.
Und dass nach jedem Update sich das ganze System selbst neu durchkompiliert, produziert unnötig Rechenlast, die die Last der eigentlichen Apps übersteigt. Letztendlich erzeugen Millionen von Maschinen wegen dotNet jeden Tag den gleichen Code.
… den Strom könnte man sich durchaus sparen!

Falschangaben beim Marketing

Anfangs hieß es:

C# und VB.NET sind rein statische Sprachen.

Und dann kam die Dynamic Language Runtime und das Statement, dass man nun die dynamische Typisierung, die es “intern immer schon gab”, auch in allen Sprachen verfügbar gemacht hätte.

C# und VB.NET sind nach JIT Kompilierung “fast” gleich schnell wie C oder C++ Programme.

Das heißt dann quasi übersetzt: “Man kann es gar nicht schneller machen.”
… zumindest bis mit der nachfolgenden Initiative behauptet wurde:

dotNet Native ist um bis zu 60% schneller als die Implementierung des klassischen Frameworks.

Ein neuer Ahead-of-Time-Compiler erlaubt nun das Kompilieren ähnlich wie C oder C++ und deshalb ist dotNet jetzt fast oder genau so schnell.

Hmm … na, ich warte dann mal auf den nächsten Future-Prediction dotNet-Compiler, der wieder 50% schneller sein wird und damit WIEDER EINMAL gleichwertig mit C und C++ sein wird.

Fazit

Beim dotNet Marketing wird so viel Bullshit geplappert, dass es weh tut.
Die Beispiele, die die hohe Performance zeigen sollen, sind immer genau auf die neueste Optimierung abgestimmt, stellen aber selten die allgemeine Praxis nach. Das passiert leider auch bei quasi allen Garbage-Collector-Vorführungen.

Wer Programme (egal mit welcher Technologie) testen will, sollte fertige Applikationen auf Systemen mit geringen CPU und RAM Kapazitäten laufen lassen.
Denn dort zeigt sich unmittelbar, dass kein Garbage-Collector “intelligent” genug arbeitet und kein “verwaltetes” Framework performant arbeitet. Und das wird noch deutlicher, wenn große Datenbanksysteme zeitkritisch arbeiten sollen.

Aber eines muss ich leider eingestehen:

Schuld sind wir! Wir C und C++ Programmierer.

Denn obwohl native Sprachen die Chance bieten würden, wirklich effizient zu arbeiten, sind eine Vielzahl der Entwickler, die mit diesen Sprachen arbeiten, nicht fähig ordentliche Programme damit zu entwerfen.
Die Anzahl von Memory-Leaks, Logikfehlern und falsch verstandenem Multithreading ist unerhört groß. In allen Firmen, in denen ich bisher gearbeitet habe, zeigte sich das Problem, dass niemand ausreichend auf Codequalität achtet, am wenigsten leider das Management.

Und eben deshalb sucht “die Industrie” krampfhaft nach jemandem, der zumindest behauptet “Out-of-the-box” alles perfekt machen zu können.

dotNet, Java und jede Menge Python Scripts versuchen diese Lücken mit ebenso schlechten Implementierungen zu füllen und sind der Grund, warum wir in der IT seit langem keinen wirklichen Fortschritt mehr produzieren, sondern nur Wiederholungen der immer wieder gleichen Bugs und Workarounds.

… und dabei fing alles gut an, mit einem durchdachten Klassengerüst und einer formal konsistenten Infrastruktur.
Schade, dass unter dieser schönen Hülle und fauliger Kern steckt.

Wie auch immer … ich versuche nach bestem Wissen und Gewissen aus diesen Fehlern zu lernen.


Wenn sich eine triviale Erkenntnis mit Dummheit in der Interpretation paart, dann gibt es in der Regel Kollateralschäden in der Anwendung.
frei zitiert nach A. Van der Bellen
... also dann paaren wir mal eine komplexe Erkenntnis mit Klugheit in der Interpretation!