·
Gedanken

Zwanzig zu eins – Effizienz, dreimal nachgemessen

Ich sage gern, dass wir schlanke Technik bauen. Dann habe ich es nachgerechnet – an dieser Seite und an einem unserer Spiele. Einmal hatte ich recht, zweimal nicht, und einmal hat mich das Messwerkzeug selbst angelogen.

Zwanzig zu eins

Ich sage gern, dass wir auf Effizienz achten. Rechnen soll dort passieren, wo der Mensch sitzt. Svelte statt React. Schlanke Abhängigkeiten statt Boilerplate.

Das sind Sätze, die man gut sagen kann, weil sie niemand nachprüft. Also habe ich sie nachgeprüft, an dieser Seite hier und an einem unserer Spiele. Einmal stimmte es. Zweimal nicht. Und beim ersten Versuch hat mich das Messwerkzeug selbst belogen.

Erste Messung: Der Vergleich hält

Fangen wir mit dem an, was für mich ausgeht.

React ist die meistverbreitete Art, heute eine Oberfläche zu bauen. Was kostet das, bevor man eine einzige Zeile eigenen Code geschrieben hat? Die veröffentlichte Produktionsfassung von react-dom ist 536.016 Bytes gross, komprimiert 94.739 Bytes. Dazu react selbst mit 4.446. Zusammen also rund 99 Kilobyte, die man ausliefert, bevor die App irgendetwas tut.

Diese Seite hier läuft mit SvelteKit. Ihr gesamtes JavaScript — Router, Übergänge, Zeitleiste, Filterleiste, Umschalter für hell und dunkel, jede Zeile, die ich selbst geschrieben habe — sind 93.878 Bytes über die Leitung, verteilt auf 27 Dateien.

Das ganze Haus ist also etwas kleiner als bei den anderen die Grundmauer. Der Satz stimmt. Ich habe ihn nur nie belegt, bis heute.

Zweite Messung: Das Werkzeug lügt

Beim ersten Versuch habe ich die Seite mit curl abgeholt, die Skript-Tags im HTML zusammengezählt und ein hervorragendes Ergebnis bekommen: 27 Kilobyte für alles. Ich war kurz sehr zufrieden mit mir.

Das war falsch. SvelteKit lädt den grössten Teil seines Codes über Importe nach, die im HTML gar nicht als Adresse stehen. Wer nur das Dokument liest, sieht sie nicht. Im echten Browser nachgesehen, sind es einunddreissig Dateien statt fünf — und statt 27 Kilobyte kommen 117 zusammen, gut das Vierfache.

Das ist die Sorte Fehler, die nie auffliegt, weil das Ergebnis ja in die gewünschte Richtung zeigt. Wenn eine Messung schmeichelt, ist sie verdächtig — und meine hat geschmeichelt.

Dritte Messung: Zwanzig zu eins

Jetzt die Zahl, wegen der ich das aufschreibe.

Dieselbe Startseite lädt achtzehn Bilder: Filmstills, ein Wolkenfoto, ein Kartenstapel auf einem Tisch. Zusammen 2.300.912 Bytes. Das grösste einzelne Bild ist 464 Kilobyte gross — allein viermal so viel wie sämtlicher Code der Seite.

Code: 117 Kilobyte. Bilder: 2,25 Megabyte. Das Verhältnis ist knapp zwanzig zu eins.

Ich habe also monatelang eine Entscheidung verteidigt, die auf dieser Seite fünf Prozent der Übertragung betrifft, und die anderen fünfundneunzig Prozent nie angeschaut. Das Framework war nie das Problem. Achtzehn unbearbeitete JPEGs sind das Problem.

Und das Ärgerliche: Die Bilder sind der einfachere Teil. Ein Framework tauscht man nicht mehr aus, wenn eine Seite steht. Bilder in kleineren Fassungen ausliefern, richtig dimensioniert, in einem neueren Format — das ist Arbeit von einem Nachmittag, und niemand muss dafür eine Grundsatzdiskussion führen.

Und das Spiel?

Bei einem unserer Godot-Spiele im Browser hatte ich das Verhältnis schon einmal gemessen: 39.509.339 Bytes Engine gegen 322.836 Bytes Spiel. Über die Leitung 9,6 Megabyte gegen 310 Kilobyte. 97 Prozent Apparat, 3 Prozent Sache.

Diesmal wollte ich wissen, ob sich daran etwas machen lässt, statt es nur zu beklagen. Es gibt dafür ein anerkanntes Werkzeug, wasm-opt, und einen Aufruf, der in jeder Anleitung steht. Ich habe ihn auf unsere Datei losgelassen. Keine halbe Minute Rechenzeit.

Ergebnis: roh von 37,6 auf 34,6 Megabyte, also acht Prozent weniger. Über die Leitung, komprimiert, von 9,5 auf 9,3 — 2,6 Prozent. Der Anteil der Engine sinkt von 96,9 auf 96,8 Prozent.

Praktisch nichts. Der Grund ist einleuchtend, wenn man ihn einmal gesehen hat: Godots Web-Export baut ohnehin schon auf kleine Grösse hin, und was danach an Wiederholung übrig bleibt, holt die Komprimierung sowieso heraus. Der Nachbrenner findet nichts mehr, was nicht schon weg wäre.

Der grosse Hebel ist ein anderer, und er ist unbequem: die Engine selbst neu übersetzen, ohne die Teile, die man nicht braucht. Wer das 3D-Modul abschaltet, spart erheblich.

Nur: Ich kann es nicht abschalten. Ich habe im Projekt nachgesehen, statt es anzunehmen — dreiundzwanzig 3D-Knoten, sieben Meshes, drei Kameras, zwei Lichter. Die Würfel in diesem Spiel sind echte Körper, die echt fallen. Genau das ist an dem Spiel schön. Der naheliegende Sparweg führt durch das, wofür man es überhaupt spielt.

Was ich daraus mitnehme

Drei Sätze, und alle drei sind mir unangenehm.

Eine Zahl, die schmeichelt, misst man nochmal. Meine erste war um das Vierfache zu gut, und ich hätte sie ungeprüft veröffentlicht, wenn ich nicht zufällig in den Browser geschaut hätte.

Der Streitpunkt und der Kostenpunkt sind selten dasselbe. Über Frameworks wird gestritten, weil man dabei etwas über sich selbst aussagt. Über Bildgrössen wird nicht gestritten, weil es langweilig ist. Der Faktor zwanzig liegt auf der langweiligen Seite.

Manche Kosten sind das, wofür man das Ding gebaut hat. 97 Prozent Engine sind viel. Aber der Weg, sie loszuwerden, hätte die Würfel gekostet, und dann wäre nichts mehr da, dessen Grösse sich zu optimieren lohnt.

Effizienz ist ein Bekenntnis, solange man sie nicht nachrechnet. Ich habe dreimal nachgerechnet, einmal recht behalten, und die beiden Male, in denen ich danebenlag, waren die nützlichen.

endeitfres