Epitaph auf die bayerische Wirtshauskultur

Nun ist er also besiegelt, der Untergang der bayerischen Wirtshauskultur: Gestern hat der bayerische Landtag mit 140 von 166 Stimmen ein weitgehendes Rauchverbot u.a. in bayerischen Wirtshäuser beschlossen (für Nicht-Bayern: Eine derartig große Mehrheit schafft auch hier die CSU nicht alleine). Der eigens von Gastwirten gegründete „Verein zur Erhaltung der bayerischen Wirtshauskultur“ [sic!] hatte zuvor in einer Unterschriftenaktion der CSU schon mit der Höchststrafe gedroht, sollte das Gesetz verabschiedet werden: Der Entzug von Stimmen bei der nächsten Wahl. Die Abstimmung im Landtag lässt also doppelt Hoffnung aufkommen für den Freistaat.

Was die Wirtshauskultur angeht: Die wird offensichtlich nur von Rauchern getragen, welche sich nun scheinbar schmollend zurückziehen, so die Wirte, nie wieder unter die Menschen gehen und einsam auf ihrer Couch dem Ende Ihres Raucherlebens entgegenqualmen. Zwar bin ich als genetischer Nur-Achtel-Bayer nicht berufen, mich in diese Angelegenheiten einzumischen. Als Nichtraucher werde ich jedoch mit Freude in Zukunft auch in solche Lokale gehen, die ich bisher wegen der schlechten Luft gemieden habe; gerne auch in Begleitung meiner Freunde, die (noch) unter Nikotinsucht leiden.

Übrigens pflegen auch nicht-rauchende Achtel-Bayern, ihre Rechnungen zu bezahlen. Ich mag zwar keinen wesentlichen Beitrag zur bayerischen Wirtshauskultur leisten, aber wie sagten schon die alten Römer: Pecuniam non olet, nun auch nicht mal mehr nach Rauch.

Performance Tuning

Regelmäßig führe ich die folgende Diskussion mit Entwicklern:

Ich: „Wenn man hier noch eine polymorphe Indirektion/lokale Variable/Methodenaufruf einzieht, wird das ganz deutlich besser testbar/lesbar/änderbar.“
Entwickler: „Oh nein, dadurch wird die Peformance zu schlecht, den Code können wir nicht ändern.“

Diese Entgegnung gründet meistens in einem sehr begrenzten Verständnis von dem, was moderne Compiler, Laufzeitumgebungen und Prozessoren leisten. Aber dazu komme ich gleich.

Aus meiner Erfahrung gibt es drei Gründe für schlechte Performance mit absteigender Bedeutung:

  1. Schlechtes Design, aus dem inperformante Datenbankzugriffe entstehen. Datenbanken sind sehr gut, wenn es darum geht, aus einer große Grundmenge schnell nach bestimmten Kriterien zu filtern. Sie sind aber langsam, wenn es darum geht, durch ein Geflecht von Objekten zu navigieren. Verliert ein Team durch schlechtes Design den Überblick über den Code, kommt es häufig dazu, dass Funktionalität, die in der Programmiersprache sehr schnell wäre, durch komplexe und langsame Datenbankoperationen abgebildet wird und umgekehrt langsame Suchoperationen im Speicher gemacht werden, für die die Datenbank besser geeignet ist. Um das abzustellen, muss man allerdings den Code oft ganz wesentlich umbauen. Andererseits habe ich durch solche Umbauten Systeme schon um einen Faktor 1000 schneller machen können. Ein Faktor 10 bis 100 ist fast immer machbar.
  2. Schlechtes Design, bei dem das Team die Übersicht verloren hat. Dies führt dazu, dass aufwändige Operationen immer wieder durchgeführt werden. Je nach Fachlichkeit kommt es bei diesen Problemen auch vor, dass entsprechende Umbauten das System um den Faktor 10 beschleunigt.
  3. Schlechtes Design, bei dem ungeeignete oder zu einfache Algorithmen und Datenstrukturen verwendet werden. Häufig tritt dieses Problem auf, wenn anstatt geeigneter Objektstrukturen und Bibliotheksklassen Arrays und Indexschleifen verwendet werden. Dies führt oft dazu, dass selbst einfache Algorithmen so komplex zu programmieren sind, dass an den Einsatz leistungsfähigerer Varianten gar nicht erst gedacht wurde. Ironischer weise erhalte ich auf die Frage, warum man hier keinen Vector oder eine Map eingesetzt habe oft die Antwort „Die sind ja viel langsamer, als ein Array“. Nicht selten führt der Umbau dann zunächst zu einer deutlichen Beschleunigung und eröffnet durch den übersichtlicheren Code auf einmal deutlich bessere und auch schnellere Alternativen.

Nun ist es nicht immer sinnvoll, die schnellste Variante zu wählen. Code, der nur einmal durchlaufen wird, ist weniger kritisch, als der Kern des Systems. Grundsätzlich gilt: Erst zum laufen bringen, dann messen und dann erst optimieren. Zum Messen empfehlen sich professionelle Profiler wie z.B. Rational Quantify, die sehr gute Möglichkeiten bieten, kritische Stellen im Code zu identifizieren und seine Energien dort einzusetzen, wo man echte Hebelwirkung hat.

Und was ist mit den Low-Level Optimierungen in der Programmiersprache? Anders als alte Assemblermakros führen moderne Compiler hochgradige Optimierungen durch, die weit über das hinausgehen, was man manuell noch beherrschen könnte; Prozessoren parallelisieren verschiedenste Operationen, so dass Indirektionen und Variablenzugriffe zum Teil höchstens noch einen zusätzlichen Zyklus (also 0,2 Milliardenstel Sekunden) brauchen, oft sogar kostenneutral sind; Virtuelle Maschinen wie die Java VM gehen sogar noch weiter und überwachen die Abläufe zur Laufzeit, um dann den Code bei Bedarf nach neuen Kriterien zu optimieren. Sie wählen unter verschiedenen Optimierungsvarianten die aus, die am besten zum aktuellen Laufzeitverhalten passt. Der Versuch, diese Mechanismen durch „geschickte Programmierung“ zu „unterstützen“ geht in mehr als 90% der Fälle nach hinten los: Die zusätzliche Komplexität hebelt den Optimierer aus, der Code wird langsamer, als vorher. Von der Gefahr, den Überblick über das Design zu verlieren und sich ernsthafte Performanceprobleme einzuziehen ganz zu schweigen.

Fast alle Teams, denen ich bisher bei der Optimierung dieses Codes geholfen habe, waren zuvor in diese Falle geraten. Zumindest bei betrieblichen Informationssystemen ist eine Optimierung auf unterster Ebene praktisch immer sinnlos und kontraproduktiv.

Ruth Leuzinger zu Kapselung

„Wenn ich etwas von Ihnen will, rede ich ja auch mit Ihnen und fasse Ihnen nicht einfach in’s Hirn!“

Mit diesem einprägsamen Vergleich hat Ruth Leuzinger, ehemalige Chef-Architektin der Zürich Versicherung, vor über zehn Jahren das Prinzip der Kapselung von Daten beschrieben. Ich habe in all den Jahren weder eine einzige Schwäche dieses Vergleichs gefunden, noch eine bessere Beschreibung des Prinzips.