Schon länger wollte ich mal etwas mit WebAssembly machen. Number crunching und performance-kritische Anwendung im Browser? Da bin ich dabei! Leider ist mir dazu nichts Realitätsnahes eingefallen (die klassische Schwierigkeit, wenn man zu einer Lösung das Problem sucht). Ich habe überlegt, ob man finanzmathematisch etwas implementieren könnte, aber über gleitende Durchschnitte oder Bezier-Splines mit 1000 Stützpunkten lacht sich ein Ryzen-Prozessor doch tot. Die üblichen Verdächtigen funktionieren locker in JavaScript.

Also habe ich das Game of Life von John H. Conway nochmal rausgekramt. Wie man das eben so macht, wenn man einfache Algorithmen auf größeren Datenmengen ausprobieren will. WebAssemblies (wasm) kann man in vielen Sprachen erzeugen. Ich habe es mit AssemblyScript probiert. Der Trick daran ist, dass man das auch nach JavaScript übersetzen kann - wichtig für Vergleiche. Aber hier erstmal das Ergebnis: der Evolutionsschritt findet in WebAssembly statt, das Rendering in JavaScript, denn WebAssembly hat keinen Zugriff auf das HTML DOM. Also bekommt es das Spielfeld als typisiertes Array und gibt die nächste Generation zurück. JavaScript packt das dann in ein Canvas.

Game of Life Screenshot

Nerd-Hinweise: Ein Klick mit der linken Maustaste platziert einen sogenannten Gleiter, ein Klick mit der rechten Maustaste eine Gleiterkanone. Die Simulation wird gedrosselt auf rund 10 Generationen pro Sekunde. Wenn die Evolution zum Erliegen kommt, einfach die Seite neu laden, um das Spielfeld zu resetten.

Und hat sich der ganze Spaß nun gelohnt? Als Lernerfahrung schon, aus Performance-Sicht leider nicht. Für die Benchmark habe ich 1000 Generationen ungedrosselt laufen lassen, jeweils ohne Rendering, damit hier nicht die Grafikkarte oder die Rendering-Engine vom Browser dazwischen funkt. Es sollte die meiste Rechenzeit im Game-of-Life-Algorithmus verbracht werden und nicht in der Browser-Infrastruktur. Ich war vom Ergebnis so verblüfft, dass ich in zwei weiteren Browsern gegenprüfen musste. WebAssembly ist signifikant langsamer als JavaScript.

Firefox 110 Edge 110 Chrome 110
WebAssembly 4030 ms 10386 ms 15083 ms
JavaScript 1019 ms 1096 ms 1077 ms

Ich vermute, das Problem ist zu sehr datengebunden. Die Übergabe der Rohdaten (nach WebAssembly und wieder raus, marshalling) ist teurer als wirklich mit den Daten etwas zu tun (der Algorithmus). Das Game of Life mit mehr als 100000 Feldern ist immer noch zu anspruchslos, um damit irgendwelche Optimierungen in verschiedenen Programmiersprachen und Runtimes zu machen.

Also Fazit: WebAssembly lohnt sich aus Performance-Sicht nicht pauschal. Vermutlich eher sehr selten. Ich denke, das ist etwas, wenn man existierenden Code in C++ oder Rust hat (Codecs, Krypto, Runtimes). Dann kann man den über Ecken auch im Browser ausführen. Wenn man eh neu entwickelt, kann man das auch direkt in JavaScript tun. Damit bin ich mir nicht mehr sicher, ob WebAssembly nicht nur ein modernes Flash oder Silverlight ist. Na mal sehen, ob da im Jahr 2030 noch ein Hahn nach kräht.