Unpslash Photo
Photo by Edward Howell on Unsplash.com

5 Beispiele für nachhaltige Programmierung

Ja, Code trägt unter anderem zur Umweltbelastung bei! Dabei geht es vor allem um die Zeiten und Speichergröße, welche bei der Ausführung von Programmen anfallen.

Bei einer nachhaltigen Programmierung geht es dabei um die Code-Effizienz. Darunter fallen zum Beispiel die Minimierung von Dateigrößen, Ausführungs- und Berechnungszeiten. Ob dann zusätzlich der Code auf einem Server oder Endgerät ausgeführt wird, welches mit erneuerbarer Energie versorgt wird, ist dabei nur ein kleiner Teil. Das bedeutet nicht, dass es nichts bringt oder unwichtig ist. Vielmehr soll es sagen, dass dies allein nicht ausreichend ist.


Im folgenden sollen nun fünf programmatische Beispiele aufgezeigt werden, wie nachhaltiger Code aussehen kann, welcher auf die verschiedensten Programmiersprachen übertragbar ist.

1. Datenbankeinträge innerhalb von Schleifen

Tritt der Fall ein, dass viele Informationswerte in einer loop verarbeitet und gespeichert werden sollen, kann ein derartiges Konstrukt in PHP in etwa wie folgt aussehen:

foreach($array as $value) {

    $sql = "INSERT INTO table (...) VALUES (...)";
    $mysqli->query($sql);
}

Für jeden Datensatz der eingefügt werden soll wird ein SQL-Statement innerhalb der Schleife ausgeführt. Dieses abstrakte Beispiel mag bedingt funktionieren und ist relativ simpel aufgebaut, dennoch ist diese Variante äußerst ineffizient! Besitzt ein Array beispielsweise 1000 Einträge, werden somit 1000 SQL-Statements an den Server gesendet, welcher die Datenbank bereitstellt. Das Lösungswort an dieser Stelle lautet “BULK INSERT”, eine der effizientesten Art und Weise große Dateimengen auf einmal in einer Tabelle zu schreiben. Allerdings unterstützen nicht alle Datenbank-Systeme einen derartigen Befehl. Bei MySQL wäre dies zum Beispiel der Fall und es wäre effizienter, die einzufügenden Werte vorher zu definieren und mit einem einzigen SQL-Befehl zu schreiben:

$sql = "INSERT INTO table (...) VALUES ";

$values = array();

foreach($array as $value) {
    array_push($values, "(" . value . ")")
}

$sql += implode(',', $values);

$mysqli->query($sql);

Mit diesem Lösungsansatz wird nur ein “riesiges” SQL-Statement erzeugt und an die Datenbank gesendet. Das resultierende SQL Statement sieht in etwa wie folgt aus:

INSERT INTO table
  (...)
VALUES
  (...),  //    1. Wert (oder auch Tupel)
  (...),  //    2. Wert
   ...
  (...);  // 1000. Wert

Abhängig von der auszuführenden Programmierumgebung können ggf. andere Ansätze genauso schnell sein, erzeugen dabei allerdings ein höheres Datenaufkommen auf der Datenbank und im Netzwerk, was wiederum ineffizienter wäre.


Eine weitere Variante, welche in der Praxis unter PHP / MySQL häufig zum Einsatz kommt, ist die einzufügenden Werte über eine Transaktion in einer Datenbank zu schreiben und abschließend diese Transaktion zu bestätigen:

$sql= "INSERT INTO table (...) VALUES (?)";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("s", $value);

$mysqli->query("START TRANSACTION");
foreach ($array as $value) {
    $stmt->execute();
}
$stmt->close();
$mysqli->query("COMMIT");

Aufgrund der Abstraktion wurden etwaige sicherheitsrelevante Faktoren bei den PHP-Beispielen nicht berücksichtigt, da diese lediglich der Veranschaulichung dienen sollen. Für BULK INSERT Beispiele unter einer Oracle-Datenbank oder T-SQL-Datenbank können die entsprechenden Verlinkungen aufgerufen werden.

Natürlich sollte auch immer geprüft werden, ob diese Daten wirklich festgeschrieben werden müssen. Denn schließlich kann es sich dabei auch nur um ein nettes Feature handeln, welches keinen Mehrwert für den Anwender bringt. Dadurch werden Unmengen an Daten und somit Speicherplatz verbraucht, welche keinen wirklichen Nutzen bringen.

2. Verzweigungsbäume mit if-else/if-Konstrukten

if (...) {...}
elseif (...) {...}
elseif (...) {...}
...
else {...};

Solche Bauweisen von Verzweigungen sind meistens in der Praxis organisch gewachsen und leider nicht anhand der Wahrscheinlichkeit sortiert. Das bedeutet konkret, das die Option mit der höchsten eintretenden Wahrscheinlichkeit als erster Vergleich (ganz oben im Verzweigungsbaum) vorzufinden wäre.

Zur Veranschaulichung wird bei der Variable parameter folgende Verteilung der **Werte a,b,c **und **d **angenommen:

 Wert | Anzahl
--------------
  a   |   15
  b   |   25
  c   |   10
  d   |   50

Bei dieser Verteilung (sowohl in absoluter als auch in relativer Betrachtung) bedeutet das der Wert der Variable parameter zu 50% aller Fälle “d” ist.

Demzufolge würde sich ein effizienter Aufbau wie folgt realisieren lassen:

if (parameter == d) {...}
elseif (parameter == b) {...}
elseif (parameter == a) {...}
elseif (parameter == c) {...}
else {...};

Der Aufbau dieses Verzweigungsbaumes unter Berücksichtigung der Wahrscheinlichkeit hat zur Folge, dass “unnötige” Vergleiche minimiert werden und somit die Gesamtlaufzeit reduziert.


In manchen Situationen kann ein Verzweigungsbaum mit einem if-else/if Konstrukt unumgänglich sein. Deshalb sollte in diesem Falle unbedingt darauf geachtet werden, dass die Eintrittswahrscheinlichkeit in den Verzweigungen widergespiegelt werden, sodass zeitaufwändige und rechenintensive Vergleiche minimiert werden.

Sollte es allerdings der Fall sein — wie in dem oben genannten Beispiel, dass lediglich auf einen Wert der weitere Ablauf bestimmt werden soll, dann empfiehlt sich die Verwendung von einem sogenannten switch, da diese deutlich effizienter in der Verarbeitung von Verzweigungen aufgrund von Compiler-Optimierungen sind:

switch (parameter) {
    case "d":
        ...
        break;
    case "b":
        ...
        break;
    case "a":
        ...
        break;
    case "c":
        ...
        break;
    default:
        ...
}

3. Kommentare sind nur für Entwickler

Ein guter Code ist auch ohne Kommentare verständlich!

Dabei scheiden sich die Geister, ob Kommentare nun nötig sind oder nicht. Letzten Endes gilt aber eine Sache: In der veröffentlichten Version des Quellcodes haben Kommentare nichts zu suchen!

Bei manchen Programmiersprachen ist dies sogar standardmäßig der Fall. Zum Beispiel werden sämtliche Kommentare im Java-Code beim Kompilieren ignoriert, bzw. entfernt, da diese keine Vorteile für die effiziente Abarbeitung des Bytecodes zur Folge haben. Somit machen Kommentare die gespeicherte Datei lediglich in der Entwicklungsumgebung größer, allerdings trifft dies nicht für das kompilierte Endergebnis (z.B. ausführbare Programme) zu.

Wird allerdings eine Quellcode-Datei ohne jegliche Code-Reduzierung an den Empfänger gesendet, wird unnötiger Speicherplatz, Bandbreite und somit wertvolle Zeit verschwendet. Dies trifft bei JavaScript zu, da dieser Code oder besser gesagt das Skript erst auf dem Endgerät des Nutzers verarbeitet wird.

Als Beispiel dient ein Kommentar für einen JavaScript-Befehl:

/* Die folgenden Worte bilden einen mehrzeiligen Kommentar und beschreiben, dass der folgende Code die Überschrift auf der jeweiligen Webseite zu "Neue Überschrift" ändert: */
document.getElementById("h1-header").innerHTML = "Neue Überschrift";

So wie oben dargestellt, benötigt dieser Code-Schnipsel in etwa 251 Bytes. Lässt man das JavaScript in einen Minify-Prozess durchlaufen, welcher Kommentare oder unnötige Leerzeichen entfernt, bleiben lediglich noch 67 Bytes übrig.

Das entspricht einem Speicherplatzersparnis von 184 Bytes oder ~73%:

document.getElementById("h1-header").innerHTML="Neue Überschrift";

Dieses Vorgehen führt vor allem zu kürzeren Ladezeiten, welche in der heutigen Zeit sehr viel Geld kosten können. Somit sollte darauf geachtet werden, dass der Programmcode für die finale Version immer minimiert wird, sodass kein unnötiger Speicher beansprucht wird, was sich negativ auf die Bearbeitungs- und Ausführungszeiten auswirkt.

4. Bedingte Variablenzuweisung

Manchmal ist der Wert von einer Variablen von einem vorherigen Ereignis abhängig und soll dementsprechend definiert werden. Angenommen eine bestimmte Funktion, die bei erfolgreicher Durchführung true zurück gibt und demzufolge die Variable status mit “Erfolgreich!” beschreibt. In allen anderen Fällen wird der Wert “Gescheitert!” in die Variable geschrieben.

if (function_XY() == true) {
    var status = "Erfolgreich!";
}
else {
    var status = "Gescheitert!";
}

Der obenstehende Codeschnipsel sollte ziemlich selbsterklärend sein, allerdings bietet sich hier schon ein enormes Einsparpotential für jegliche Programmiersprachen an. Man könnte die Variable status standardmäßig mit “Gescheitert!” belegen und auf die standardmäßige Vorgehensweise bei if Abfragen zurückgreifen. Und zwar wird eine Variable in einem Vergleich in den meisten Programmiersprachen immer auf vorhanden sein, bzw. true geprüft, sodass man sich dadurch bereits einige Zeilen Code, bzw. Zeichen sparen kann:

var status = "Gescheitert!";
if (function_XY()) {
    status = "Erfolgreich!";
}

Bei nicht minimierter Version wäre dies bereits ein Ersparnis von 25 Bytes. Natürlich nicht viel — bei einem Vergleich der jeweils minifizierten Variante mit Entfernung von unnötigen Leerzeichen wären es gerade einmal 15 mickrige Bytes.

Doch wie heißt es so schön? Kleinvieh macht auch Mist!

Allerdings geht da noch mehr! Und zwar mit Hilfe von “ternären Operatoren”, welche eine if-else Verzweigung ersetzen kann und sollte. Ternäre Operatoren arbeiten deutlich effizienter und sind somit deutlich performanter im Vergleich zu if-else Konstrukten:

var status = (function_XY()) ? "Erfolgreich!" : "Gescheitert!";

Bei einer Minimierung fallen bei diesem Befehl gerade einmal 55 Bytes an.

Hier noch einmal alle drei Varianten in minimiertem Format im Vergleich:

if-else-block | if-block | ternär-block
---------------------------------------
   86 Bytes   | 71 Bytes |   55 Bytes

Die gute Nachricht ist, dass bei vielen Code-Minimizer bereits solche bedingten Variablenzuweisungen mit if-else bereits automatisch zu ternären Operatoren umgeschrieben werden. Setzt allerdings voraus, dass dies auch wirklich der Fall ist und das Code-Minifier zum Einsatz kommen, um einen effizienten Code zu generieren.

Darauf zu Vertrauen ist gut, es zu Wissen allerdings besser!

Schließlich schadet es dem menschlichen Gehirn nicht, wenn es ein wenig mehr arbeiten muss und somit die Energie des Körpers beansprucht, anstelle aus den Ressourcen der Natur zu zehren aufgrund der höherer Umweltbelastung.

5. Unnötige Features löschen

Zu oft wird bei Software-Projekten fleißig hinzugefügt und Features eingebaut, welche letzten Endes keinen spürbaren Vorteil für den Endanwender bringen. Dafür sehen diese bei Aktivierung gut aus oder haben einen netten Effekt. Diese vermeintlichen kleinen Features können allerdings sehr schnell beachtliche Speicherfresser werden. Deshalb gilt es unnötige Features zu identifizieren und diese nicht einfach auszukommentieren, da dies ja “nichts kostet” — es geht auf die Kosten der Nachhaltigkeit! Nicht notwendige Feature und somit unbrauchbarer Programmcode sollte gelöscht werden und nicht auskommentiert, um bei der Kompilierung oder Minifizierung wieder mühsam entfernt zu werden. An dieser Stelle kann man sich die verschwendete Rechenkapazität wirklich schenken.


Nur weil Speicher und Rechenleistung heutzutage immer weniger Geld kosten, bedeutet dies nicht, dass man den Speicher mit Müll belagern sollte.

Gerade bei der Programmierung gibt es dutzende Wege um ein Ziel zu erreichen, doch mit dem richtigen Wissen kann der Programmcode nicht nur effektiv sondern auch gleichzeitig effizient und somit nachhaltig geschrieben werden. Gerade Speichergröße wirkt sich auf CPU-Last, Ausführungsgeschwindigkeit und Ladezeiten aus, welche einen negativen Einfluss auf die Gesamtqualität und somit Einfluss auf die CO²-Emission hat.

Autor
  • Manuel Steinberg
Veröffentlichung
  • 19.10.2020
zuletzt aktualisiert
  • 3.5.2021