Stromverbrauch der Seashell unter Karmic Koala

Wenn man Ubuntu 9.10 auf einem Netbook installiert, ist die Ausbeute des Akkus nicht optimal. Standardmäßig ist zwar der gnome-powersave-daemon aktiviert, dieser nutzt jedoch nicht das volle Potential. Ein Verbrauch von ca. 9 Watt und 4 Stunden Akkulaufzeit (ohne WLAN) ist nicht berauschend. Mit ein paar Handgriffen bekommt man aber den Verbrauch unter 6 Watt gedrückt und erreicht so die 6 Stunden Akkulaufzeit. Mein Rekord liegt bei knapp 7 Stunden, dabei war der Laptop jedoch nicht wirklich in Benutzung…

Erster Einstiegspunkt zum Stromsparen ist der „laptop_mode“-Tools. Diese sind standardmäßig installiert, jedoch deaktiviert. Dabei bieten sie wesentlich granularere Möglichkeiten das System auf „Sparsamkeit“ zu trimmen. Die wichtigen/benötigten Möglichkeiten kurz zusammen gefasst:

  • Starten und Stoppen von Diensten: Im Batteriebetrieb ist der betrieb einiger Diensten unter Umständen nicht nötig oder nicht so wichtig, dass sie kritisch für das System sind. zb: der NTP dienst.
  • das Abschalten von ungenutzten Video-Outs
  • das Einschränken der verfügbaren Bandbreite bei Ethernet interfaces. 1000MBit sind selten wirklich nötig und kostet das ein oder andere Watt

Um den Dienst scharf zu schalten, muss man die Datei /etc/default/acpi-support bearbeiten.

# Switch to laptop-mode on battery power - off by default as it causes odd
# hangs on some machines
ENABLE_LAPTOP_MODE=true

Kleinere Änderungen müssen auch unter /etc/laptop-mode/conf.d/lcd-brightness.conf gemacht werden, da sich unter dem 1008HA das Interface für das LCD Display wo anders befindet.

BRIGHTNESS_OUTPUT="/proc/acpi/video/VGA/LCDD/brightness"

Die Werte für die Helligkeit können dabei zwischen 0 und 15 skalieren.

Weitere Änderungen können nach Wunsch in den Dateien unter /etc/laptop-mode/conf.d/ gamacht werden. Hier kann man nach belieben sämtliche Veränderungen vornehmen. Das meiste ist selbsterklärend. Dabei gilt immer, alles was man im Normalbetrieb nicht braucht, sollte man abschalten und bei bedarf zuschalten.

Neben diesen „banalen“ Änderungen gibt es noch andere Optimierungen.
Einen „großen“ Vorteil bringt es RamDisk einzusetzten. Solange man mechanische Festplatten einsetzt, ist einer der größten „Stromfresser“ eben diese Festplatte. Entweder leistet man sich bei Gelegenheit eine (sündhaft) teure SSD oder man sieht zu, dass man Mechanische Zugriffe auf die Festplatte so lange unterdrückt wie irgend möglich.

Eine RamDisk ist dabei die UltimaRatio. Mittels tempfs kann man im RAM einen Bereich reservieren, der anschließend in das FileSystem gemountet wird. So kann man zb. den Cache des Browsers oder „/var/log“ direkt in den RAM legen. Auf der haben Seite verbucht man damit, dass die Fesplatte über „lange“ Zeiträume nicht angetastet werden muss. Negativ ist halt, dass alle abgelegten Daten den Kaltstart nicht überleben werden.

Alle bisherigen Optimierungen nutzten nur vorhandene Techniken besser aus, ohne das System in die Instabilität zu treiben. Die weiteren Eingriffe gehen Tiefer und können das System komplett zerschießen. Anwendung auf eigene Gefahr.

Unter Jaunty gab es eine reihe von Traytools die die „Super Hybrid Engine“. Leider fühlten sich einige dieser Entwickler von der Ubuntu-Entwicklungs-Politik so angepisst, dass sie die Entwicklung eingestellt haben. So muss man sich inoffizieller Pakete bedienen die zu alle dem keiner Paketverwaltung unterliegen. Im Internet kann man eine brauchbare Tray-Control finden. Leider startet die auf Anhieb nicht. Da der Pyhton-Script das falsche interface für die Helligkeitssteuerung voraussetzt. Als erstes braucht man die korrekte Schnittstelle. Mittels folgendem befehl das Interface identifizieren:

find /sys/ | grep brightness

Anschließend in der Datei /usr/bin/eee-control-daemon diesen Wert in Zeile 85 und 86 einsetzten. Nun kann man den Dienst starten. Während die Steuerung der RF-KillSwitches nur leidlich funktioniert, kann man über dieses Tool die FSB-Tacktung reduzieren, Was nochmals ordentlich Stromersparnis bringt.

Etwas heickler ist das Abschalten des des AppArmor-Systems. Hier sollte man jedoch kurz inne halten! AppArmor dient zur Sicherung des Systems und stellt eine aktive Sicherheitsebene dar. Jedoch lässt sich für die Praxis eines Netbook-Einsatzes dessen Sinn vernachlässigen bzw in Frage stellen. Die Abschaltung von AppArmor brachte bei meinem System ca 0.5 Watt.

Hat man diesen Schritt einmal getan, ist es mit dem Selberbauen des Kernel auch nicht mehr weit. Der Karmic-Koala setzt auf den Standard-Kernel, dieser ist „leider“ ein generic-Kernel. Also ein Kernel der das größtmögliche Spektrum an Hardware und Spezialanwendungen unterstützt was sich der Jungs bei Chanonical vorstellen können.

Beim konfigurieren des Kernels kann man extrem viel ein und verstellen. Meiner Meinung nach gibt es kann man sich aber auf folgende Punkte beschränken:

  • Zielarchitektur: Der Kernel sollte auf Core2Duo/Xeon optimiert werden, laut mehrere Internetquellen wird damit auch der Atom abgedeckt. Alle weiteren Optionen die andere X86 Architekturen unterstützen kann man abschalten.
  • LargeMemory: Standardmäßig ist der Ubuntu-Kernel auf 4GB eingestellt. In der Seashell sind fest 1GB verbaut. Es braucht den Overhead also nicht.
  • Virtualisierung: Wer will auf einem Atom mehrere virtuelle Gast-Systeme einrichten? Wer auch immer das macht, macht braucht sich um Stromverbrauch keine Gedanken machen 😉
  • AppArmor: will man die Fehlermeldung beim Booten wegbekommen, schaltet man hier auch das Kernel-Interface für AppArmor ab.

Man kann auch Treiber „entfernen“ von denen man weiß das man sie nie brauchen wird. Jedoch bringt das außer einer kleineren InitRamDisk kaum Performance. Mehr bringt da schon das feste einkompilieren von Treibern. Wenn man viel Zeit hat, kann man einen Kernel bauen, der ohne InitRamDisk auskommt. Leider funktioniert dann dann UReadAhead nicht. Dieser dienst speichert alle Dateien die für einen Systemstart gebraucht werden sequenziell optimiert ab. So können sie besser von der Festplatte geladen werden. Leider scheint dieser ohne InitRD nicht klar zu kommen. Der Kernel bootet dann zwar schneller, da er keine RamDisk laden muss, das restliche System braucht dafür merklich länger.

Eine Beispiel Konfiguration hab ich hier bereitgestellt: EEE-Kernel.config

Alles in allem haben mir diese eingriffe rund 3 Watt Leistungsersparniss gebracht. Mit Durchschnittlich 35 WakeUps pro Sekunde ist das Ende aber noch nicht erreicht.

Wie man mit TOP den SQL Server lahmlegt

Eine „häufig“ genutzte Funktionalität des TSQL Befehlssatzes ist das TOP Statement. Es schränkt das Ergebnis Resultset auf eine vorgegebene Anzahl an Zeilen ein. Sinnvoll ist das ganze wenn man mehre gleichwertige Treffer in einer Ergebnissmenge hat, und nur einen Teil davon braucht.

Beispiel: von 1000 Läufern werden nur die selektiert, die die Strecke in einer vorgegebene Zeit zurück gelegt haben, sortiert nach der benötigten zeit aufsteigend. Aufs Treppchen kommen jedoch nur drei Läufer.

Der TSQL – Select dazu:

SELECT TOP 3 t.nachname,t.vorname,t.zeit
  FROM teilnehmer t
  JOIN rennen r on (t.rennen = r.rennen)
  JOIN strecke s on (r.strecke = s.strecke)
 WHERE t.Zeit < s.ZeitMax
   AND r.Veranstalltung = @veranstaltung
 ORDER BY t.ZEIT asc

Die landläufige "Halbwahrheit" dazu ist, dass TOP X nicht in den Execution Plan eingreift. Folglich der Select normal ausgeführt wird und nur die Ergebnis-Menge auf die ersten X Reihen beschränkt wird. Das ist gefährliches Halbwissen! Hier sind zwei Bilder die "eindrucksvoll" belegen, dass man sich mit einem TOP den ganzen Server lahmlegen kann.

Select ohne TOP
Select ohne TOP
Select Top 100
Select Top 100

Dem geübten SQL-Auge dürfte sofort (wenn man die Bilder in groß betrachtet) auffallen das ohne TOP Hash-Match-Joins genutzt werden, wohingegen bei dem TOP 100 Nested-Loop-Joins genutzt werden. Was man im Bild nur erahnen kann, ist der Umstand, das mit dieser Konstellation jeder MS-SQL Server in die Knie zu bekommen ist.

Nested-Loops skalieren ganz beschissen, nämlich linear. Was kein Problem ist, wenn die zu bearbeitende Treffermenge klein ist. Hier ist der Problem begraben. Dem TOP entnimmt der QueryOptimizer eine Information, eine Vorhersage wie viele Treffer zu erwarten sind. Der Select wird nicht ausgeführt und dann die Treffermenge beschnitten, sondern der Select wird solange ausgeführt bis die gewünschte Treffermenge erreicht ist oder der Select keine Ergebnisse mehr liefert.

Normalerweise ist dieses "Denken" auch richtig. Wenn die Treffermenge normalerweise 10k Trefferr enthält und darauf ein TOP 1 angewendet wird ist es Schwachsinnig erst die 10K Treffer zu ermitteln und dann 9999 davon weg zu schmeißen.

Umgekehrt kann man sagen, das für einen Treffer selten ein kompletter Full-Table-Scann bei allen beteiligten Tabellen benötigt wird. So wird je nach "Where"-Bedingung aus der Lead-Tabelle der erste Datensatz ermittelt und so lange an die Folgeoperationen durch gereicht, bis er in der Treffermenge landet oder Aufgrund einer Bedingung ausscheidet. Das wird solange wiederholt bis de gewünschte Trefferzahl erreicht wurde. Im Beispiel benötigte es im "Gutfall" für einen Treffer 70 "Reads" aus der Lead-Tabelle (die Tabelle ganz rechts).

Was passiert aber, wenn Aufgrund einer Sortierung die komplette Lead-Tabelle gelesen werden muss? Das bekommt der Optimizer mit und wechselt sofort auf Hash-Match-Joins. Leider erkennt der Optimizer nicht, dass es auch andere Gründe geben kann, warum die Treffermenge rapide hochgehen kann. Der ungünstigste Fall ist folgender:
Die letzte Operation vor dem "TOP"-Operator führt einen Filter aus, der alle gelieferten Datensätze ausschließt. So müssen alle vorherigen Tabellen komplett gescannt werden und alle ermittelten Datensätze durch die Nested-Loops Operatoren bearbeitet werden. Bei Tabellen mit 10k und mehr Datensätzen sprengt das jeglichen verfügbaren Speicher. Der Server geht augenblicklich in die Knie. Wenn man Glück hat, ist die Instanz in CPU und Speicherverbrauch eingeschränkt, so dass die anderen Instanzen unbeeinflusst bleiben. Wenn nicht, dürfte man seinem Kunden gegenüber in Erklärungsnot geraten...

Die Gegenmaßnahmen sind recht einfach:

  • Ausschließen das es zu solchen Extrem-Situationen kommt, wo ein Datensatz erst den ganzen ExecutionPlan durchlaufen muss um dann ausgeschlossen zu werden.
  • Keep it Simple - Selects mit Top sollten ohne große Where-Bedingung auskommen.
  • Dem QueryOptimizer Hilfestellung über query-Hints geben.