Premature Optimization - noch einmal

Kurz nachdem ich meinen Blog-Eintrag Performance und die Ausrede der verfrühten Optimierung hier gepostet habe, erschien unter dem Titel "The Second Law of Optimization" ein lesenswerter Beitrag von Ashod Nakashian zum gleichen Thema.

Im Wesentlichen sagt auch Ashod, dass man wissen muss, wo und wann es sich lohnt, von vornherein zu optimieren und wo man dies besser unterlässt:

As we’ve already iterated, designing and writing efficient code doesn’t necessarily mean premature optimization. It just means we’re responsible and we are balancing the cost by investing a little early and avoiding a high cost in the future.

Ashod Nakashian: The Second Law of Optimization, Juni 2011

Ashods Schwerpunkt liegt weniger auf den Auswirkungen der Architektur (obwohl er diese auch erwähnt), als vielmehr auf der vernünftigen Entscheidung für geeignete Datenstrukturen und/oder Algorithmen für ein Problem. Ein Thema, das ich in meinem Posting nicht behandelt habe.

Ich empfehle, den ganzen Beitrag (oder doch zumindest Ashods eigene Zusammenfassung für Eilige) zu lesen, will aber zumindest ein Beispiel wiedergeben:

It’s not premature optimization to use dictionary/map instead of a list or the most common container in your language of choice. Not when we have to read items most of the time. It’s not premature optimization if we use an O(n) algorithm instead of the O(n2) that isn’t much more complicated than what we’ll use (if not an O(log2 n) algorithm). It’s not premature optimization if we refactor a function so it wouldn’t handle multiple unrelated cases. Similarly, moving invariant data outside a loop isn’t premature optimization. Nor is caching very complex calculation results that we don’t need to redo.

Ashod Nakashian: The Second Law of Optimization, Juni 2011

Gute Beispiele für sinnvolle Optimierungen, deren späterer Einbau mit dem dazugehörigen Umbau des restlichen Codes deutlich aufwendiger wäre, als dies gleich zu bedenken. Das ist ein wichtiger Aspekt, der in meinem Posting eindeutig zu kurz kam. Von unterschiedlichen Ausgangspunkten kommend, sind wir aber beide zu dem gleichen Schluss gekommen: Man muss wissen, wann es sich lohnt, rechtzeitig nachzudenken und wo man dies übertriebenen Ehrgeiz bedeutet:

There are no excuses to writing inefficient code if the alternative is available at a small or no cost. There is no excuse to not thinking the algorithm ahead of typing. No excuse to leaving old experimental bits and pieces because we might need them later, or that we’ll cleanup later when we optimize. The cost of poor design, badly performing code is very high. It’s the least maintainable code and can have a very high cost to improve.

Ashod Nakashian: The Second Law of Optimization, Juni 2011

Also es gilt weiterhin:

Know when to play dumb!   :-)

Neuer Releaseplan für GlassFish 3.1.1

Für die nächste Version des GlassFish-OpenSource Servers wurde der Releaseplan überarbeitet. Ursprünglich sollte GlassFish 3.1.1 bereits veröffentlicht sein, doch es wurden noch ein paar neue Anforderungen mit aufgenommen - wodurch sich der Plan verzögert hat.

Vor allem einige integrierte Komponenten werden aktualisiert, wie bspw. Weld, Jersey und EclipseLink. Alexis Moussine-Pouchkine hat dazu Detailinformationen veröffentlicht.

Den überarbeiteten Releaseplan hat Alexis im Wiki veröffentlicht.

asadmin und Shell-Expansion

Nachdem ich eine neue GlassFish-Installation vorgenommen hatte, wollte ich mir wie üblich mit asadmin list alle konfigurierbaren Elemente ansehen. Da es sich um GlassFish 3.1 handelt, sollten deutlich mehr als bei GF v3 vorhanden sein. Die Ausgabe entsprach dann allerdings nicht ganz dem Gewünschten:

rittmey@ballblazer:~$ asadmin list *
Command list only accepts one operand
Usage: asadmin [asadmin-utility-options] list
	[-m|--monitor[=]]
	[-?|--help[=]] pattern
Command list failed.

Hups. Zunächst war ich irritiert, ob sich das list-Kommando möglicherweise in GlassFish 3.1 an der Stelle verändert hätte. Dabei war die Fehlermeldung nun wirklich mehr als deutlich: "Command list only accepts one operand."

Es hätte sich also gelohnt, sich die Ausgabe mal genauer anzuschauen. Aber wer macht das schon? Ich jedenfalls nicht.

Immerhin kam ich dann doch noch schnell drauf: Ich hatte einfach ignoriert, dass es ein so nützliches Feature wie die Shell-Expansion gibt. Dieses ersetzt bekanntlich den Asterisk durch alle Dateien des aktuellen Verzeichnisses. Normalerweise genau das, was man will, aber nicht in dem Fall.

Also einfach den Asterisk escapen ( asadmin list \* ) oder in Anführungsstrichen setzen ( asadmin list "*" ) und schon listet asadmin alle 256 Konfigurationsparameter auf.

Wieder zurück

Nach langer Pause habe ich letztes Wochenende erstmalig wieder einen Eintrag gepostet. Anfang letzten Jahres wurde unser Sohn Linus geboren und danach war meine Zeit logischerweise etwas knapp :-) Infolge dessen habe ich meine Mitarbeit bei GlassFish als auch meinem Blog erst mal ruhen lassen.

Linus beansprucht nach wie vor viel Zeit - und das auch völlig zu recht. Und es macht auch viel Spaß mit ihm. Doch inzwischen ist unser Tagesablauf wieder deutlich geordneter und somit will und kann ich die Postings wieder aufnehmen.

Dabei werde ich weiterhin schwerpunktmäßig über GlassFish posten und zudem den ein oder anderen Kommentar über den Stand der Software-Entwicklung, sinnlose Hypes und Management-Erwartungen einstreuen.

Und weil Technorati es so will, hier mein Claim Code: GS7UY9JSYM2W :-)

Performance und die Ausrede der verfrühten Optimierung

Ein bekannter Spruch der Software-Entwicklung ist die Aussage "Premature optimization is the root of all evil." Dieses Zitat von Donald Knuth wird dabei meist in dieser Form aus dem Kontext gerissen benutzt und als Ausrede verwendet, um Performance-Gesichtspunkte bei der Entwicklung (erstmal) nicht beachten zu müssen. Womöglich wird gar gehofft, am Ende eines Projektes etwaige Performance-Schwachstellen mittels eines Profilers oder sonstiger magischer Tools herausfinden zu können und durch die daran anschließende Behebung der Engpässe, die Anwendung auf Trab zu bringen.

Dies ist eine irrige Hoffnung, die nur selten gelingen wird. Der Punkt an dem Zitat von Knuth ist zum einen zu entscheiden, was denn eine "premature optimization" ist und wann eine Optimization notwendig und im Zeitablauf eines Projekts angemessen ist. Zum zweiten lohnt es sich gerade bei diesem Zitat, den Kontext zu beachten, in dem es steht:

"There is no doubt that the grail of efficiency leads to abuse. Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97 % of the time: premature optimization is the root of all evil.

Yet we should not pass up our opportunities in that critical 3 %. A good programmer will not be lulled into complacency by such reasoning, he will be wise to look carefully at the critical code, but only after that code has been identified."

Donald E. Knuth in: Computing Surveys, Vol. 6, No. 4, 1974; Hervorhebungen im Original

Auch Brian Goetz Aussage, dass man am besten dummen Code schreibe, wird diesbezüglich gerne falsch verstanden:

"Often, the way to write fast code in Java applications is to write dumb code -- code that is straightforward, clean, and follows the most obvious object-oriented principles."

Interview mit Brian Goetz, März 2007

Goetz und Knuth haben natürlich recht. Das blöde ist nur, dass man wissen muss, wann man am besten dummen Code schreibt, wann es richtig ist, nicht zu optimieren. Randall Hyde weist in einem lesenswerten Essay darauf hin, dass die 97 Prozent von Knuth bedeuten, dass im Schnitt bei jeder 33. Codezeile Optimierung angebracht ist. Die konkreten Zahlen sind sicher eher willkürlich gewählt, sie dienen mehr dazu, das Problem deutlich zu machen.

Zunächst einmal sind zwei Dinge zu unterscheiden:

  1. Beim Design der Anwendung wird Performance berücksichtigt
  2. Beim Codieren wird auf performanten Code geachtet

Das Erstere ist immer ein Muß. Ein fehlerhaftes Design lässt sich im schlimmsten Fall nur durch ein Redesign mit entsprechendem Umschreiben des Codes korrigieren. So auch Goetz:

"Most performance problems these days are consequences of architecture, not coding -- making too many database calls or serializing everything to XML back and forth a million times."

Interview mit Brian Goetz, März 2007

De facto werden ganz wesentliche Entscheidungen, die die Performance einer Anwendung betreffen, zu einem frühen Zeitpunkt eines Projekts getroffen. Nehmen wir als Beispiel die Verteilung der Anwendung auf mehrere Knoten.

Ganz entscheidend in Knuths Zitat ist der Hinweis auf "noncritical parts". Diese zu optimieren ist, so Knuth, des Entwicklers liebstes Ding. Allerdings impliziert der Ausdruck gerade, dass es auch "critical parts" eines Programms geben müsste. Und diese zu optimieren ist sehr wohl wichtig.

Abschließend möchte ich allerdings betonen, dass das wichtigste an einem Programm ist, dass es das tut, was es tun soll - und zwar mit möglichst wenigen Fehlern und auf eine vorhersehbare Art und Weise. Ein Programm mag rennen wie Schmidts Katze und dazu die wunderbarste Benutzerführung aller Zeiten haben. Wenn es nicht das macht, was der Anwender braucht, wird der Anwender es dennoch nicht nutzen (umgekehrt wird der Anwender freilich auch nicht gerade mit Freuden altbackene und träge Anwendungen aufrufen). Gerade bei nebenläufigen Programmen kann man ein Programm wunderbar kaputt optimieren und kaum nachvollziehbare Fehlersituationen erzeugen. Dies ist für Anwender wie Entwickler in der Wartung gleichermaßen ein Albtraum! Insofern im Zweifel dann doch lieber dummen Code schreiben, bevor man sich selber austrickst!

In dem Sinne: Know when to play dumb!