# Schnelleinführung in die Optimierung von GNOME-Programmen

Dies ist eine kurze Einführung in die Optimierung und behandelt sowohl das Wie als auch das Warum. Details zu individuellen Werkzeugen verschieben wir auf spätere Artikel, eine Sammlung aus Hinweisen und Tricks ist jedoch enthalten.

## Was optimieren wir?

Sie sollten bei der Optimierung für GNOME zunächst eines bedenken: Wir versuchen nicht, das Programm zu verbessern, sondern wir versuchen, die Benutzer glücklicher zu machen.

Bessere Programme machen die Benutzer glücklicher, aber es gibt Verbesserungsmöglichkeiten, die sie viel glücklicher machen werden als Andere: Ansprechzeit, Startzeit, leichter Zugriff auf Befehle. Der Rechner sollte auch nicht sofort den Auslagerungsspeicher benutzen müssen, wenn mehr als eine Anwendung geöffnet ist.

Die traditionelle Optimierung umfasst Konzepte wie Prozessorlast, Umfang des Codes, die Anzahl der Mausklicks sowie die Speichernutzung des Programms. Die folgende Auflistung sollte die erste Liste ergänzen, wobei ein grundlegender Unterschied besteht: Ein GNOME-Benutzer schenkt der zweiten Liste keine Beachtung, während der ersten Liste sehr wohl Aufmerksamkeit gewidmet wird. Beim Optimieren von GNOME-Programmen sollen die Prozessorlast, der Speicherverbrauch und all diese Dinge optimiert werden, aber das sind die Bedeutungen, nicht das Endziel. Wir optimieren für Benutzer.

## Ausführung der Optimierung

Aus dem vorigen Abschnitt wurde deutlich: Um etwas optimieren zu können, muss es messbar sein. Sie können Zufriedenheit nicht messen, aber Sie können sehr wohl Startzeiten messen, um sagen zu können, dass Sie sie verbessert haben. Die Zufriedenheit wird dann - hoffentlich - folgen.

Optimization is the process of measurement, refinement and re-measurement. So the first thing you must do is find a way to measure what you are optimizing. Ideally this measurement is a single number, for example: the time taken to perform a task. This is your benchmark, it is the only way to tell if you are winning or losing. There is a big difference between a program that should be fast and a program that is fast.

Once you have a basic benchmark you need to find out why your code is not doing as well as it should. It is tempting to do this by inspection: just looking at the code and trying to spot something that looks like it needs improvement. You will invariably be wrong. Using a profiler to get a detailed break-down of what your program really does is the only way to be sure.

Usually the problem is isolated to small sections of code. Pick the worst place and concentrate on that first. Once that is done, rerun the profiler and repeat. As you proceed the gains made at each step will get less and less, at some point you will have to decide that the results are good enough. If your efforts are only extracting 10% improvements then you are well past the point where you should have stopped.

Don't forget the big picture. For example, rather than just trying to speed up a piece of code, ask yourself if it needs to be run at all. Could it be combined with another piece of code? Can the results of previous calculations be saved and reused? It won't even need to be optimized if it is in a place where the user is never going to notice it. Worse still, the code may already be optimized and is doing the heavy calculations now to avoid doing them later. Code does not run in isolation and neither does the optimization process.

## Hinweise

### Die Grundlagen

- Re-run your benchmark after every change you make to the code and keep a log of everything you change and how it affects the benchmark. This lets you undo mistakes and also helps you not to repeat mistakes.

- Stellen Sie sicher, dass Ihr Code korrekt und fehlerfrei ist, bevor Sie mit der Optimierung beginnen. Überprüfen Sie nach der Optimierung, ob dies dann auch noch zutrifft.

- Optimize at the high level before optimizing the details.

- Use the right algorithm. The classic text-book example is using quick-sort instead of bubble-sort. There are many others, some save memory, some save CPU. Also, see what shortcuts you can make: you can do quicker than quick-sort if you are prepared to make some compromises.

- Optimization is a trade-off. Caching results speeds up calculations, but increases memory use. Saving data to disk saves memory, but costs time when it is loaded back from disk.

- Make sure you choose a wide variety of inputs to optimize against. If you don't it is easy to end up with a piece of code carefully optimized for one file and no others.

- Avoid expensive operations: Multiple small disk reads. Using up lots of memory so disk swapping becomes necessary. Avoid anything that writes or reads from the hard disk unnecessarily. The network is slow too. Also avoid graphics operations that need a response from the X server.

### Fallen für die Unvorsichtigen

- Beware of side effects. There can often be strange interactions between different sections of code, a speed-up in one part can slow another part down.

- When timing code, even on a quiet system, events outside the program add noise to the timing results. Average over multiple runs. If the code is very short, timer resolution is also a problem. In this case measure the time the computer takes to run the code 100 or 1000 times. If the times you are recording are longer than a few seconds, you should be OK.

- Es ist sehr leicht, vom Profiler in die Irre geführt zu werden. Es gibt Geschichten über Leute, die die Untätigkeitsphase perfekt optimiert haben, weil das Programm ja die meiste Zeit in Untätigleit verbringt! Optimieren Sie keinen Code, von dessen Wirkung der Benutzer ohnehin keine Notiz nimmt.

- Vergessen Sie nicht den Ressourcenverbrauch des X-Servers. Der Speicherverbrauch Ihres Programms enthält nicht die Pixmaps, die vom X-Server verarbeitet werden, aber sie verbrauchen dennoch Speicher. Verwenden Sie `xrestop`, um zu sehen, welche Ressourcen Ihr Programm beansprucht.

### Low Level Hints

- When optimizing memory use, be wary of the difference between peak usage and average memory usage. Some memory is almost always allocated, this is usually bad. Some is only briefly allocated, this may be quite acceptable. Tools like massif use the concept of space-time, the product of memory used and the duration it was allocated for, instead.

- Time simplified bits of code that do only the things you know are essential, this gives an absolute lower limit on the time your code will take. For example, when optimizing a loop time the empty loop. If that is still too long no amount of micro-optimization will help and you will have to change your design. Make sure the compiler doesn't optimize away your empty loop.

- Gliedern Sie Code aus Schleifen aus. Ein etwas umfangreicheres Stück Code, das nur einmal ausgeführt wird, ist um Einiges schneller als ein simples Stück Code, was dafür tausend Schleifen durchläuft. Vermeiden Sie es, langsamen Code allzu oft aufzurufen.

- Geben Sie dem Compiler so viele Hinweise wie möglich. Verwenden Sie das const-Schlüsselwort. Verwenden Sie `G_INLINE_FUNC` für kurze, häufig aufgerufene Funktionen. Schauen Sie nach `G_GNUC_PURE`, `G_LIKELY` und weiteren diversen glib-Makros. Verwenden Sie diese Makros anstelle der gcc-spezifischen Schlüsselwörter, um die Portierbarkeit zu erhalten.

- Verwenden Sie keine Assemblersprachen. Sie sind nicht portierbar. Während sie auf dem einen Prozessor unglaublich schnell sind, ist nicht unbedingt garantiert, dass dies auf jedem von der Architektur unterstützten Prozessor genauso ist (beispielsweise Athlon vs Pentium 4).

- Schreiben Sie eine vorhandene Bibliotheksroutine nicht neu, es sei denn, Sie sind sich sicher, dass sie unakzeptabel langsam ist. Viele prozessorintensive Bibliotheksroutinen wurden bereits optimiert. Andererseits sind einige Routinen tatsächlich langsam, insbesondere diejenigen, die Systemaufrufe an das Betriebssystem absetzen.

- Halten Sie die Zahl der verlinkten Bibliotheken gering. Je weniger Bibliotheken gelinkt werden müssen, umso schneller startet das Programm. Dies ist in GNOME allerdings schwierig.

### High Level Tricks

- Take advantage of concurrency. This doesn't just mean using multiple processors, it also means taking advantage of the time the user spends thinking about what they are going to do next to perform some calculations in anticipation. Do calculations while waiting for data to be loaded off disk. Take advantage of multiple resources, use them all at once.

- Bluffen Sie. Der Benutzer sollte denken, dass sein Rechner schnell ist, aber es ist gleich, ob er es wirklich ist oder nicht. Die Zeit zwischen dem Befehl und der darauf folgenden Antwort ist maßgebend. Es ist gleichgültig, ob die Antwort vorausberechnet wird, zwischengespeichert oder in irgendeiner anderen Weise später zu einem günstigeren Zeitpunkt ausgewertet wird, solange der Benutzer genau das bekommt, was er erwartet.

- Do things in the idle loop. It is easier to program than using full multi-threading but still gets things done out of the users eye. Be careful though, if you spend too long in the idle loop your program will become sluggish. So regularly give control back to the main loop.

- Falls alles Andere scheitern sollte, informieren Sie den Benutzer mit einem Fortschrittsbalken drüber, dass es derzeit nur langsam voran geht. Das dürfte ihm lieber sein, als wenn Sie nur die Ergebnisse präsentieren würden. Er möchte zumindest wissen, dass das Programm nicht abgestürzt ist und er dann erst einmal eine Tasse Kaffee trinken gehen kann.