Arbeitsspeicher Knappheit: Kostenreduzierung durch Performance Engineering
AI brachte uns neben neuen Werkzeugen und Arbeitsweisen leider auch eine verschärfte Hardware Knappheit, unter anderem in Bezug auf Arbeitsspeicher. Egal ob Industrie oder Forschung, die Verarbeitung großer Datenmengen erfordert typischerweise auch eine große Menge Arbeitsspeicher, welcher oft über einen Cloud Service Provider bezogen wird, um sich den Betrieb eines eigenen Rechenzentrums zu sparen. In den vergangenen Monaten haben viele Cloud Hoster ihre Preise dementsprechend erhöht und nicht selten ist die Rechnung um 25% teurer geworden. Ein Weg dem entgegenzusteuern, ist eine effizientere Nutzung des teuer gewordenen Arbeitsspeichers.
Die Ausgangslage
JavaScript und Python zählen zu den beliebtesten Programmiersprachen für Anwendungsentwicklung, sowohl in Industrie als auch in Forschung.
Leider sind diese Sprachen besonders ineffizient in der Resourcennutzung im Vergleich zu anderen Sprachen. Mindestens um den Faktor 3, oft um den Faktor 10, in Extremfällen sogar um den Faktor 1000. So kann es durchaus passieren, dass von bspw. einem 10 TB RAM Deployment effektiv nur 3 TB RAM genutzt werden, die restlichen 7 TB RAM werden vom Sprachoverhead verbraucht.
Lösungsansatz 1 - Reimplementierung in effizienterer Programmiersprache
Die besten Ergebnisse erzielt man durch Portierung des Programms auf eine Low-Level Programmiersprache. Sprachen wie C++ oder Rust haben fast gar keinen Overhead und erlauben somit fast perfekte Ausnutzung der verfügbaren Hardware.
Der größte Nachteil ist, dass diese Sprachen oft eine Menge Wissen voraussetzen. Ein geübter Entwickler kann zwar durchaus genauso schnell in diesen Sprachen arbeiten wie er in Python oder JavaScript arbeiten würde, allerdings ist die Einstiegshürde definitiv höher.
Einen Mittelweg stellen Sprachen wie Java, Kotlin, C# oder Go dar. Diese Sprachen haben zwar ebenfalls einen Overhead, dieser ist jedoch deutlich geringer als der von Python oder JavaScript und sie sind einfacher zu nutzen als C++ oder Rust.
Lösungsansatz 2 - Shared Libraries
So gut wie jede Programmiersprache erlaubt die Nutzung von sogenannten Shared Libraries (.dll Dateien auf Windows, .so Dateien auf Linux). Diese Dateien beinhalten Maschinen-Code, welcher zur Laufzeit von z.B. Python geladen und aufgerufen werden kann, allerdings ursprünglich in bspw. C++ implementiert ist.
So könnte man bspw. nur den Kernalgorithmus in C++ programmieren, den Rest der Applikation ist allerdings weiterhin in Python geschrieben. Das Maintainability Problem bezieht sich dementsprechend nicht mehr auf die ganze Applikation, sondern nur auf den performance kritischen Kernteil.
Dieser Ansatz wird von vielen Python Bibliotheken wie z.B. numpy genutzt.
Lösungsansatz 3 - Separater Prozess / Service
Ähnlich zu den Shared Libraries wird hier nur ein Teil ausgelagert, dieser wird allerdings nicht zur Laufzeit geladen, sondern läuft als separater Prozess (nicht zwingend auf gleicher Maschine). Die Kommunikation findet dann nicht direkt über einen Prozeduraufruf, sondern über Sockets, Pipes etc. statt, ähnlich wie mit einer Web-Api kommuniziert werden würde. Wichtig: Diese Art der Kommunikation fügt deutlich messbaren Overhead hinzu.
Indirekte Auswirkungen
Neben der besseren Ausnutzung der Hardware und dem damit kostengünstigeren Hosting reduziert sich generell die Komplexität des Deployments, was zusätzliche Arbeit einspart. So kann zum Beispiel ein optimierter, in Go geschriebener Server das 10-Fache eines node.js Servers handeln. Dadurch kann dann im Extremfall eine Go Instanz 10 node.js Instanzen ersetzen, aus einem Deployment von 10 Serven kann also ein 1 Server werden.
Fazit
Performance Engineering hat das Potential, massiv Kosten einzusparen und somit die Profitabilität eines Projektes im Alleingang merklich zu steigern. Gerade heutzutage, wo Hardware teurer wird aber gleichzeitg Workloads und Datenmengen immer größer, ist Performance Engineering für jedes Projekt unabdingbar.