Toter Code

aus Wikipedia, der freien Enzyklopädie
(Weitergeleitet von Dead code elimination)
Wechseln zu: Navigation, Suche

Toter Code (englisch dead code) ist in der Programmierung der Begriff für den Quelltext eines Computerprogramms bzw. Teile davon, „die an keiner Stelle im Programm verwendet werden“.[1] In erweitertem Sinn kann toter Code als Sonderform redundanten Codes (= „überflüssig“) betrachtet werden.

Toter Code ist ein Programmteil, der durch keinen möglichen Kontrollfluss erreicht und deshalb nie ausgeführt werden kann; er wird auch unerreichbarer Code (englisch unreachable code) genannt. Ebenfalls als Toter Code gelten Anweisungen, die zwar ausgeführt werden, deren Ergebnis aber nie verwendet wird,[2] Toter Code kann Anweisungen/Befehle enthalten oder sich auf nicht verwendete Datendeklarationen beziehen.

Oft werden die Bezeichnungen ‚unerreichbarer Code‘ und ‚toter Code‘ synonym verwendet.

Bedeutung[Bearbeiten]

Alle Formen toten Codes gelten aus verschiedenen Gründen als unerwünscht bzw. Mangel in der Softwarequalität. So vergeudet toter und redundanter Code CPU-Zeit, die anderen Threads fehlt.

Je nach Situation kann toter Code auch bewusst entstehen: Er soll beispielsweise einen vorläufigen oder ehemaligen Quelltextteil konservieren. Häufig liegt jedoch auch ein Programmfehler vor, dessen Entdeckung ein Ziel beim Softwaretest ist.

Obwohl die Ergebnisse von totem Code nie verwendet werden, kann er Ausnahmebehandlungen auslösen oder globale Status beeinflussen. So kann eine Codeänderung die Programmausgabe verändern und unbeabsichtigte Programmfehler verursachen. Ob es sich in diesen Fällen noch um toten Code handelt, ist umstritten.

Beispiel[Bearbeiten]

1 int foo (int x) {
2   int y = 100 / x;  // toter Code, da y nicht verwendet wird
3   int z = x * x;    // redundanter Code zu 2 Zeilen weiter unten
4   if (z >= 0) {
5     return x * x;   // redundanter Code zu 2 Zeilen weiter oben
6   }
7   return -1;        // unerreichbarer Code, da z immer >= 0 ist
8 }
  • Toter Code – Im Beispiel wird in Zeile 2 die Zahl 100 durch x dividiert, das Ergebnis aber nie verwendet. Es handelt sich somit um toten Code. Ist allerdings x Null, wird eine Ausnahme ausgelöst. Eine Entfernung dieses Codes führt somit zu einer Änderung der Funktionalität. Da Ausnahmen auszulösen aber niemals ein Teil der Funktionalität sein sollte, handelt es sich dabei um eine fehlerhafte Funktionalität, die zu entfernen ist.
  • Redundanter Code – Im Beispiel wird x in Zeile 3 und 5 quadriert, ohne dass x dazwischen geändert wird. Das Ergebnis ist somit immer dasselbe, damit ist der Code redundant. return z wäre somit in Zeile 5 angebracht.
  • Unerreichbarer Code – Im Beispiel wird die Zeile 7 niemals erreicht, da die Abfrage z >= 0 in Zeile 4 immer wahr ist und in Zeile 5 die Methode verlassen wird. Somit handelt es sich bei Zeile 7 um unerreichbaren Code.
  • Unnötiger Code – Nachdem die Abfrage z >= 0 in Zeile 4 immer wahr ist, ist sie sinnlos und kann ebenso entfernt werden. Unnötiger Code fällt zwar in keine der drei genannten Kategorien, wird aber landläufig oft auch als toter Code bezeichnet.

Gründe[Bearbeiten]

Toter Codekann unter anderem entstehen durch

  • Programmierfehler; bei (noch) unvollständigem Testen eines neuen oder veränderten Programms;
  • einen neuen Fehler, der während der Korrektur eines Fehlers von einem Programmierer unbeabsichtigt hinzugefügt wurde und den unerreichbaren Code umgeht und während des Tests unentdeckt blieb;
  • überflüssigen Code, den ein Programmierer nicht entfernen wollte, weil er mit funktionalem Code vermischt ist;
  • überflüssigen Code, den ein Programmierer vergaß zu löschen;
  • vorher sinnvollen Code, der nie mehr erreicht werden kann, weil die Eingabedaten sich verändert haben, so dass dieser Code nie wieder aufgerufen wird;
  • komplexen, überflüssigen Code, der absichtlich nicht entfernt, aber unerreichbar gemacht wurde, damit man ihn bei Bedarf „wiederbeleben“ kann;
  • defektlokalisierende Konstrukte (Eng. debugging constructs) und Reste vom Entwicklungscode, der noch vom Programm entfernt werden muss.

In den letzten fünf Fällen ist der derzeit unerreichbare Code eine Altlast, d. h. Code, der früher sinnvoll war, aber nicht mehr benötigt wird.

Beispiel

Oft wird absichtlich toter Code erzeugt, um Ausgaben während der Programmentwicklung später zu deaktivieren:

int main()
{
#define DEBUG  0
    int a = 3;
    // ...
    if (DEBUG)
        printf("%d\n", a);
    // ...
    return a;
}

Während der Entwicklungsphase kann hier der Wert der Variablen a ausgegeben werden, falls das Makro DEBUG auf einen Wert ungleich Null (wahr) gesetzt wird. Wenn diese Kontrollausgabe nicht mehr benötigt wird, setzt man den Wert auf 0 und der Präprozessor erkennt und entfernt das nun tote Stück Code.

Analyse[Bearbeiten]

Toten Code zu entdecken ist eine Form von statischer Codeanalyse und benötigt eine genaue Analyse der Ablaufsteuerung, um den Code unabhängig von den Variablen und anderen Laufzeitbedingungen zu finden. Mit Hilfe geeigneter Analysewerkzeuge kann ein Großteil toter und unerreichbarer Codeteile gefunden werden. In einigen Sprachen (wie z. B. Java) sind einige Formen von unerreichbarem Code ausdrücklich verboten und führen zu Compilierungsfehlern.

In großen Softwareprojekten ist es manchmal schwierig, toten Code zu erkennen und zu entfernen, insbesondere wenn ganze Module davon betroffen sind. Der Testgerüstbau kann solchen Code als noch „lebendig“ zeigen, und es kann sogar sein, dass aus vertraglichen Gründen der irrelevante Code geliefert werden muss.[3]

In einigen Fällen ist ein praktischer, nicht allzu aufwändiger Ansatz eine Kombination von einfachen Unerreichbarkeitskriterien und die Verwendung von einem Profiler, um komplexe Fälle zu bearbeiten. Mit Profiling kann man nicht die Unerreichbarkeit von Code beweisen. Es ist aber eine gute heuristische Methode, um potenziell unerreichbaren Code zu entdecken. Wird einmal ein Codeteil als suspekt gesehen, können andere Methoden wie z. B. wirksamere Codeanalysewerkzeuge verwendet oder die Analyse von Hand durchgeführt werden. Dadurch kann man dann entscheiden, ob der gefundene Codeteil wirklich unerreichbar ist oder nicht.

Optimierung[Bearbeiten]

Aktuelle Compiler und Interpreter können ‚tote‘ Codeabschnitte erkennen und auch automatisch entfernen. Diese Optimierungstechnik bezeichnet man als dead code elimination.

Die korrekte, redundanzfreie Umsetzung des oberen Beispiels würde folgendermaßen aussehen:

1 int foo (int x) {
2     return x*x;
3 }

Die Optimierung toten Codes funktioniert bei allen Arten toten Codes ähnlich, meist durch einfaches Entfernen der betreffenden Codestellen. Bei der Entfernung von totem Code ist besondere Vorsicht geboten, da toter Code Seiteneffekte haben kann, die aus dem Code selbst nicht erkennbar sind.

Compileroptimierungen sind typischerweise konservative Ansätze, um toten oder unerreichbaren Code zu entfernen. Ist eine Mehrdeutigkeit bezüglich Programmverhalten vorhanden, wird der Code nicht entfernt. Die Optimierung während der Compilierung wird Dead code elimination genannt. Sie kann für toten Code durch Variablenanalyse, für unerreichbaren Kode mit Datenflusskontrolle gemacht werden.

Der Code kann auch durch Transformationen, die der Compiler durchführt, unerreichbar werden, wie z. B. die sogenannte common subexpression elimination (Entfernen gemeinsamer Teilausdrücke).

In der Praxis hat die Ausgereiftheit der Analyse einen entscheidenden Einfluss auf den Anteil des gefundenen unerreichbaren Codes. Z. B. kann durch constant folding und einfache Flussanalyse gezeigt werden, dass der Funktionsaufruf foo() im folgenden Beispiel unerreichbar ist:

int i = 2 + 1;

if (i == 4)
   foo();

Einer vollständigen Analyse zur Kompilierungszeit sind jedoch theoretische Grenzen gesetzt, die eng mit der Entscheidbarkeitsproblematik der theoretischen Informatik verbunden sind.

Öffentliche Wahrnehmung[Bearbeiten]

Im November 2010 veröffentlichte Microsoft eine neue Version des Internet Explorers, der scheinbar alle anderen Browser in puncto JavaScript-Geschwindigkeit weit hinter sich ließ. Es stellte sich jedoch schon bald heraus, dass Microsoft eine spezielle Implementation der dead code elimination nutzte, um sich an die Spitze eines bekannten JavaScript-Benchmarks zu katapultieren. In anderen Benchmarks waren die Resultate eher im Mittelfeld.[4]

Literatur[Bearbeiten]

  • S. S. Muchnick: Advanced Compiler Design and Implementation. Morgan Kaufmann, 1997.

Weblinks[Bearbeiten]

  • DCD Alternatives Vergleich unterschiedlicher Werkzeuge zur statischen Code Analyse mit Funktionalitäten zum Auffinden toten Codes

Tools zum Auffinden toten/unerreichbaren Codes

Einzelnachweise[Bearbeiten]

  1. Fernuni Hagen: Ein Eclipse Plugin zum Aufspüren toter Codefragmente (…) , PDF.
  2. A. W. Appel: Modern Compiler Implementation in Java. Cambridge University Press, 1998.
  3. Douglas W. Jones: Dead Code Maintenance. Risks 8.19, 1. Februar 1989
  4. Herbert Braun: Browser-Debatte: Hat Microsoft geschummelt? Heise Online, 18. November 2010.