Wrapper und Abstraktionsschichten

Wieviele Layer und Abstraktionsschichten “über” einer konkreten Implementierung sind gut und ab wann ist die Grenze des guten Geschmacks erreicht?

Diese Frage stelle ich mir bei jedem Projekt aufs Neue und komme stets zu anderen Ergebnissen.

Konkret geht es diesmal um die Frage, ob es Sinn macht um malloc() einen Wrapper herum zu legen.
Jeder vernünftige Entwickler würde sagen:

Nein! Es handelt sich um DIE wichtigste C Funktion, die Basis vieler anderen Funktionen. Man braucht sie nicht umzuleiten.

Das stimmt grundsätzlich auch, denn eine Umwickelung dieser Funktion mit einer anderen führt nur zu einem zusätzlichen Herumspringen im Codespeicher bei der Ausführung, und wirkt sich negativ auf die Cache-Auslastung und Performance aus.

Doch im GATE Projekt findet dennoch eine solche Umleitung statt, und zwar zu den Funktion gate_mem_alloc() und gate_mem_dealloc().

Und der Grund dafür ist, dass es unter fast jeder Plattform einen “Spezial-Modus” gibt, in dem malloc() nicht zur Verfügung steht. … und das ist die Treiber und Kernel-Modul Entwicklung.

Nun könnte man mutmaßen, dass die Treiberentwicklung kein Primärziel ist (zumindest aktuell noch nicht) und weiter nur die negativen Aspekte der Umleitung bleiben.
Doch auch das ist (großteils) nicht zutreffend, da das GATE Framework für statische Kompilierung und Link-Time Codegeneration ausgelegt ist.

Der Optimizer erkennt in der Regel, dass ein Aufruf von gate_mem_alloc() direkt auf malloc() (im Usermode) umleitet und kann diesen Aufruf daher “inlinen”, also direkt setzen.

Damit bleibt der Code also weiter bei höchster (bzw. hoher) Effizienz.

Ich bin daher dazu über gegangen bei Framework-Projekten mehr Abstraktion einzubauen, als man es durchschnittlich erwarten würde, im Anbindungen von weiteren mir aktuell noch nicht bekannten Plattformen zu ermöglichen.

Fazit

Es bleibt natürlich immer im Ermessen des Entwicklers bzws. des Teams, wir oft man Wrapper oder höherer Abstraktionsschichten über konkrete Plattform oder Funktions-APIs darüber legt.

Der größte Nachteil von zu viel Abstraktion ist leider, dass man damit sehr individuelle und proprietäre Schnittstellen schafft, die niemand kennt und erst viel Dokumentation gelesen werden muss.
Beispiel malloc() kennt jeder, gate_mem_alloc() kenne aktuell nur ich selbst.

Und so geht es vielen Bibliotheksprojekten.
Immer herrscht der Kampf zwischen Abstraktion und einfacher bzw. direkter Native-API-Nutzung.


Wenn sich eine triviale Erkenntnis mit Dummheit in der Interpretation paart, dann gibt es in der Regel Kollateralschäden in der Anwendung.
frei zitiert nach A. Van der Bellen
... also dann paaren wir mal eine komplexe Erkenntnis mit Klugheit in der Interpretation!