Kernel-Tuning für Linux

14.02.2002 von Jörg Luther
Der Linux-Kernel bietet umfangreiche Möglichkeiten zur einsatzspezifischen Optimierung. Neben der Neukompilierung zählt dazu auch die Option zur dynamischen Modifikation von Kernelparametern im laufenden Betrieb.

Für die meisten Windows-Konvertiten lässt sich der Augenblick ihrer Bekehrung zu Linux exakt definieren: Es ist in der Regel der Moment, in dem das noch ungewohnte Betriebssystem zum ersten Mal mit dem selbst optimierten Kernel bootet. Die Erkenntnis schlägt ein wie eine Bombe: So einfach ist es also, ein modernes, leistungsfähiges und stabiles Betriebssystem zielgenau auf Maschine und Einsatzzweck hin anzupassen. Und unweigerlich stellt sich die Frage, warum Microsoft dem Systemverwalter nicht dieselben Möglichkeiten einräumt, wenn sich deren Produkte schon derart schmerzhaft im Budget bemerkbar machen.

Bei etwas Anpassung hier und ein wenig Kompilierung da stellt sich jenseits aller Euphorie jedoch irgendwann die Frage, was die Handarbeit am Kernel denn eigentlich bringt. Rentiert es sich wirklich, die zig Megabyte an Sourcen Zeit raubend zu konfigurieren und kompilieren? Bringt der Feinschliff an den Quellen tatsächlich noch merklichen Performance-Schub, obwohl heute alle gängigen Distributionen ohnehin schon für Pentium-Maschinen voroptimiert sind? Dieser Frage wollen wir im vorliegenden Artikel einmal genauer nachgehen. Als Versuchsobjekt soll uns dabei ein SuSE Linux 7.3 Professional in der Standard-Installationsvariante dienen.

Werkzeugkasten

"You can't manage what you can't measure", lautet ein alter Grundsatz des Performance-Managements. Bevor wir uns also an die Optimierung des Systems begeben, müssen wir uns einige Messwerkzeuge bereitlegen. Die meisten davon hat jede gängige Linux-Distribution ohnehin an Bord. So lässt sich beispielsweise mit dem Befehl time die Zeit messen, die Programme oder Skripts zur Abarbeitung benötigen. Dabei stellt man time einfach dem jeweiligen Kommando voran. Für die Kompilation des Kernels mit

time make dep clean bzImage modules

liefert unser Testrechner beispielsweise die Ausgabe:

real 36m42.392s
user 33m25.870s
sys 1m48.700s

Dabei gibt der real-Anteil die gesamte verbrauchte Laufzeit wieder. Die Werte für user und sys signalisieren die vom getesteten Programm/Skript respektive vom System verbrauchte Zeit. Differenzen zwischen der Summe der user- sowie sys-Werte und der Gesamtlaufzeit ergeben sich aus dem Ressourcenverbrauch anderer Prozesse.

Die momentane Belegung des Arbeitsspeichers lässt sich jederzeit durch die Ausgabe der Datei /proc/meminfo feststellen. Der Befehl

cat /proc/meminfo

liefert eine recht umfangreiche Ausgabe, an der uns speziell vierWerte interessieren: used zeigt den gesamten belegten Speicher, MemFree die verbleibende Reserve. Buffers und Cached zeigen an, wie viel des belegten Speichers Linux für den jeweiligen Zweck verbraucht.

x11perf, bonnie und unixbench

Die XFree86-Suite beinhaltet mit x11perf auch ein Werkzeug zum Benchmarken des grafischen Subsystems. Falls Sie sehr viel Zeit mitbringen, können Sie über den Aufruf des Programms ohne Parameter sämtliche Tests vornehmen. Je nach Rechner und Grafikkarte beansprucht ein Durchlauf dann mehrere Stunden. Glücklicherweise lässt sich der Test aber auch per Parametrisierung auf einzelne Funktionsprüfungen einschränken. Erstellen Sie sich dazu am besten ein kleines Skript wie das unten abgebildete.

Mit bonnie stellen die meisten Distributionen ein Tool zur Verfügung, das die Performance des Filesystems misst. Dazu nutzt bonnie sequenziellen In- und Output sowie wahlfreien Zugriff auf ein Testfile, dessen Größe sich beim Aufruf angeben lässt. Als Vorgabe arbeitet der Benchmark mit einer 100 MByte großen Datei. Rechner mit 128 MByte oder mehr Arbeitsspeicher können jedoch einen Großteil der dabei entstehenden Zugriffe puffern. Daher sollten Sie über den Parameter -s die Größe der Testdatei auf solchen Rechnern erhöhen. Der Aufruf

bonnie -s 1000

veranlasst das Tool beispielsweise, mit einem 1000 MByte großen File zu arbeiten. So erhalten Sie durch Saturieren des Cache realistische Performance-Werte für die I/O auf Dateisystem-Ebene.

Zu den nützlichsten generellen Messwerkzeugen für Linux zählt unixbench. Dabei handelt es sich um eine Modifikation der Byte Unix Benchmarks, die für den Einsatz unter Linux angepasst wurde. Die aktuelle Variante trägt die Versionsnummer 4.0.1 und stellt neben Dateisystem-Benchmarks auch Tests für arithmetische Operationen und Kernelfunktionen bereit.

Kernel-Kompilierung als Benchmark

Die Kompilierung eines Kernels eignet sich hervorragend als Benchmark. Zu den Faktoren, die hier die Geschwindigkeit beeinflussen, zählen neben der schieren CPU-Leistung auch die Geschwindigkeit von Festplatte und Dateisystem sowie die Performance des RAM und der Speicherverwaltung.

Jedoch lassen sich die Ergebnisse nur dann vergleichen, wenn in allen getesten Konfigurationen derselbe Kernel mit identischen Optionen erstellt wird. Dazu kann man im Regelfall die Standardeinstellungen des Distributors einsetzen. Im Fall von SuSE finden sich diese etwa in der Datei /boot/vmlinuz.config. Bei entsprechender Einstellung des Kernels lagern zudem in der Datei /proc/config.gz die Settings für die momentan laufende Variante des Betriebssystemkerns. Vor Gebrauch gilt es, diese Datei in ein beliebiges Verzeichnis zu kopieren und zu entpacken.

Um die gewünschten Einstellungen zu aktivieren, starten Sie aus dem Verzeichnis /usr/src/linux mit make xconfig die Kernelkonfiguration. Dort laden Sie dann mit Hilfe des Buttons Load Configuration from File die fragliche Konfigurationsdatei. Bei SuSE Linux können Sie also /boot/vmlinuz oder ersatzweise die kopierte und entpackte /proc/config.gz angeben. Über den Button Save and Exit speichern Sie die Einstellungen anschließend als Vorgabe für den nächsten Kompilationslauf ab.

Nun genügt beispielsweise ein

time make dep clean bzImage modules

um ein Messergebnis zu generieren. Bei dem von uns verwendeten SuSE Linux 7.3 ist der Rechner damit eine gute halbe Stunde beschäftigt.

CPU-gerechte Kompilierung

Als ersten Schritt zum optimierten Kernel erstellen wir eine Variante in der vom Hersteller vorgegebenen Defaultkonfiguration, die wir aber für den tatsächlichen Prozessor optimieren. In unserem Fall handelt es sich dabei um einen Pentium-III, weswegen wir bei Processor Family die Option Pentium-III/Celeron/Coppermine anwählen. Zudem verzichten wir auf die Unterstützung für vier GByte Memory und begnügen uns mit maximal zwei GByte Speicherausbau.

Die Kompilierung des so angepassten Kernels und der Module nimmt auf unserem Testrechner knapp 35 Minuten in Anspruch. Nach der Installation des Kernels messen wir mit einer Stoppuhr sowie den bereits erwähnten Benchmarks einige Leistungsdaten. Dabei erweisen sich die erzielten Verbesserung als recht moderat.

So sinkt die Zeit für den Bootvorgang um drei Sekunden, die Durchlaufzeit für eine Kernelkompilierung verringert sich um 1,2 Prozent. Beim Test mit bonnie ergibt sich ein Performance-Plus von rund sechs Prozent für sequenzielles Lesen und Schreiben sowie wahlfreien Zugriff. Zudem bleiben rund drei Prozent mehr Arbeitsspeicher frei. x11perf und unixbench können dagegen keinen Geschwindigkeitsvorteil vermelden, mit einer Ausnahme: Die Pipe-basierten Tests des Unix-Benchmarks verzeichnen ein Plus von rund sieben Prozent.

Ergebnisse - Prozessoranpassung

SuSE 7.3 (i586, 4GByte)

Angepasst (i686, 2GByte)

Differenz (Prozent)

Distribution: SuSE 7.3 Professional, Standardinstallation

Bootdauer (min)

1:13

1:10

4,1

RAM frei (Byte)

75788288

78159872

3,1

Kernel erstellen (min)

36:46,1

36:19,9

1,2

bonnie -s 1000

Block write (KB/s)

19495

19497

0,0

Block read (KB/s)

17864

19466

9,0

Random seek (op./s)

96,8

102,6

6,0

Unixbench (loops/s)

Dhrystone2

1362894,5

1371010,6

0,6

Arithmetic (double)

154133,6

155140,2

0,7

Pipe throughput

329216,9

349317,6

6,1

Pipe-based context switching

166980,7

179567,3

7,5

Kompilierung ohne Module

Zusätzlich zu gezielten Optimierungen für den Prozessor lässt sich ein Kernel "out-of-the-box" auch deutlich verschlanken. Er muß ja Module für verschiedenste Zwecke bereitstellen, die jedoch für die meisten Installationen nicht gebraucht werden. Im Regelfall werden ganze Modulgruppen nicht benötigt. So können auf Desktops beispielsweise die IrDA-Komponenten entfallen, auf Netzwerk-Clients in der Regel das ISDN-Subsystem. Die Amateur-Radio-Unterstützung oder die Bluetooth-Komponenten zählen ebenfalls zu den typischen Entsorgungskandidaten.

Bei der gezielten Anpassung der Kernel-Konfiguration an die vorhandene Hardware hilft unter anderem der Befehl

cat /proc/modules

der eine Liste der tatsächlich geladenen Module liefert. Daneben empfiehlt sich auch ein Blick in die Datei /var/log/boot.msg. Dort finden Sie ein Protokoll aller vom Kernel beim Systemstart ausgegebenen Meldungen, das ebenfalls Informationen zur Kernelkonfiguration liefern kann.

In der schlanken Konfiguration ohne überfüssigen Ballast sinkt der Zeitaufwand für eine komplette Kernel-Kompilierung von 35 auf knapp sieben Minuten. Der Speicherbedarf des Systems reduziert sich erneut, auch bei sequenziellen Lesevorgängen erreichen wir nochmals einen Performance-Vorteil. Ansonsten weisen unsere Benchmarks jedoch keine wesentliche Beschleunigung gegenüber einem modularen Kernel aus.

Ergebnisse - Statischer Kernel

SuSE 7.3 (i586, 4GByte)

Angepasst (i686, 2 GByte statisch)

Differenz (Prozent)

Distribution: SuSE 7.3 Professional, Standardinstallation

Bootdauer (min)

1:13

1:10

4,1

RAM frei (Byte)

75788288

82812928

9,3

Kernel erstellen (min)

36:46,1

36:15,2

1,4

bonnie -s 1000

Block write (KB/s)

19495

19544

0,3

Block read (KB/s)

17864

20134

12,7

Random seek (op./s)

96,8

102,6

6,0

Unixbench (loops/s)

Dhrystone2

1362894,5

1371702,1

0,6

Arithmetic (double)

154133,6

155144,2

0,7

Pipe throughput

329216,9

349319,8

6,1

Pipe-based context switching

166980,7

179571,1

7,5

Powertweak für Linux

Einige Performance-relevante Kernelvariablen lassen sich auch während des laufenden Betriebs modifizieren. Dies betrifft vornehmlich Parameter rund um Caching und Swapping, also Virtual-Memory-Komponenten. Linux hält diese Variaben im proc-Pseudo-Filesystem unter /proc/sys/vm in mehreren Dateien vor. Der klassische Weg, dort Einstellungen vorzunehmen, führt über einen sysctl()-Aufruf, etwa per Skript.

Es geht aber auch deutlich bequemer: powertweak-linux von Dave Jones bietet sowohl ein bequemes User-Interface für die Einstellung als auch einen Daemon (powertweakd), der die veränderten Parameter bei jedem Systemstart automatisch wieder setzt. Neben Variablen, die für die Arbeitsweise des Virtual Memory (VM) zuständig zeichnen, kann powertweak-linux auch zahlreiche andere Einstellungen beeinflussen. Dazu zählen unter anderem auch Settings rund um die Hardware, das Netzwerk-Subsystem oder das Kernel-Logging. Wir gehen im Folgenden jedoch nur auf die VM-spezifischen Parameter ein.

Diese können für die Leistung des Rechners in bestimmten Einsatzumgebungen deutliche Folgen haben. Generelle Vergleiche lassen sich jedoch nicht treffen, da die Auswirkungen je nach konkretem Verwendungszweck und abzuarbeitenden Anwendungen völlig unterschiedlich ausfallen können.

Auch die Hardware des Rechners ist hier ausschlaggebend. Ältere PCs mit langsamen Festplatten gewinnen etwa bei einer Optimierung des Festplatten-Cache deutlich mehr als moderne Systeme. Auf Grund der starken Abhängigkeit von Anwendung und Hardware verzichten wir im Folgenden auf die Angabe von konkreten Einstellungen und Benchmarks.

Powertweak: bdflush-Parameter

Der Kernel-Daemon bdflush kontolliert, wie und wann der Inhalt der Buffer nach Modifikationen auf die Platte zurückgeschrieben wird. Zur Kontrolle dieser Vorgänge stellt powertweak-linux im Abschnitt Virtual Memory/bdflush sechs Parameter zur Verfügung. Über drei davon lässt sich das System gezielt auf bestimmte Anwendungen hin optimieren.

Das Setting Activate bdflush when % dirty gibt an, wie viele modifizierte Puffer im Cache gehalten werden sollen. Je höher diese Anzahl, desto seltener muss der Kernel auf die Platte zugreifen. Andererseits dauert ein Schreibvorgang dann im Einzelfall länger.

Die Einstellung Dirty blocks to write per wake cycle begrenzt die Anzahl der modifizierten Puffer, die der Kernel in einem Rutsch auf die Platte schreibt. Hohe Parameter verursachen seltenere, dafür aber längere Zugriffe. Niedrige Werte sorgen für ein verteiltes und unauffälligeres Schreiben.

Mit Try to reclaim n buffers on refill geben Sie die Anzahl der Puffer an, die der Kernel jeweils neben der aktuellen Pufferbelegung in Reserve halten soll. Je höher diese Zahl ausfällt, desto mehr Arbeitsspeicher beansprucht bdflush. Dafür lassen sich aktuelle Pufferanforderungen sehr viel schneller befriedigen.

Powertweak: Swapping-Parameter

Über den Abschnitt Virtual Memory/Swapping lässt sich das Verhalten des Kernel-Swapdaemon kswapd beeinflussen. Als interessantester Parameter findet sich hier Swap cluster. Er gibt an, wie viele Pages kswapd in einem Zug auf die Platte schreiben soll. Je höher dieser Wert, desto effektiver kann der Daemon die Auslagerung abwickeln. Zu hohe Angaben bremsen das System jedoch wieder aus, da dann die Request-Warteschlange zuläuft. Über einen höheren Wert für Page cluster lässt sich zudem der Read-ahead auf Kosten der Auslagerungslatenz steigern.

Eine ebenso nützliche wie gefährliche Einstellungsmöglichkeit bietet der Punkt Overcommit Memory. Manche Programme versuchen sich per malloc() "vorsorglich" große Speichermengen zu sichern, die sie dann doch nur teilweise in Anspruch nehmen. Dadurch scheitert die Zuweisung, obwohl das Programm eigentlich ausgeführt werden könnte. Schalten Sie Overcommit Memory ein, dann signalisiert der Kernel in jedem Fall ausreichend vorhandenen Speicher. Andererseits besteht damit das Risiko eines Speicherüberlaufs.

Mit dem Wert für Tries base definieren Sie die maximale Anzahl von Pages, die kswapd in einem Zug freimachen soll. Tries min gibt an, wie oft kswapd eine Page freizumachen versucht. Änderungen an diesen Werten führen im Regelfall selten zu mehr Performance, sondern im Regelfall nur zu intensiverem Swapping.

Fazit

Soll Linux auf dem Desktop zum Einsatz kommen, drängt wohl mehr der Optimierungswille des Anwenders als die tatsächlich erzielbare Leistungssteigerung zum Tuning des Kernels. Mehr als zehn Prozent Performance-Plus lassen sich bei gängigen Distributionen nicht erreichen - was übrigens für die Qualität und das durchdachte Design des Betriebssystemkerns spricht. Eine exakt auf die Hardware zugeschnittene Konfiguration erleichtert allerdings ein laufendes Update des Kernels: Der Verzicht auf den modularen Aufbau drückt die Kompilierungszeiten um über 80 Prozent.

Ganz anders stellt sich die Situation beim Servereinsatz dar. Hier kommt jede Leistungssteigerung direkt den Clients zu Gute. Allerdings muss die optimale Konfiguration jeweils gezielt für den Einzelfall erarbeitet werden. Dabei leisten Werkzeuge zur dynamischen Konfiguration - wie etwa powertweak-linux - wertvolle Dienste. (jlu)

Testkonfiguration

Als Testsystem dient ein Pentium-III/650 mit 128 MByte Hauptspeicher und 20,5-GByte-EIDE-Festplatte. SuSE Linux 7.3 Professional in der Standardinstallation kommt als Betriebssystem zum Einsatz.

Für jede Konfiguration ermitteln wir:

Testkonfiguration

Komponente

Daten

Mainboard

Asus P3B-F Rev. 1004

Slot 1, BX-Chipsatz

Prozessor

Pentium III/650

100 MHz FSB

RAM

128 MByte PC100 SDRAM

Festplatte

IBM DPTA-372050

20,5 GByte UltraDMA/66

CD-ROM

Toshiba DVD-ROM SD-M1402

40x, UltraDMA/33

Grafikkarte

Elsa Erazor III Pro

AGP, Riva TNT2, 32 MByte

Netzwerkkarte

3Com 3C905C-TX

PCI, 10/100 MBit/s

Soundkarte

Ensoniq ES-1371 Rev.8, PCI