WebAssembly Experiment

von Christoph

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.

WebGraph.NET Open-Source

von Christoph

Schon etwas älter ist das Programm WebGraph, ein Dataminer der skriptgesteuert Links aus verschiedenen Webseiten extrahiert und daraus einen Verknüpfungsgraphen generiert. Das Originalprogramm basiert auf OpenGL und benutzt Googles v8-Javascript Engine.

Da man ja mit der Zeit geht und sich auf fortbilden will, habe ich die Applikation mal auf .NET in der Version 4.0 und WPF portiert. Das war erstaunlich einfach. Der Code ist im wesentlichen um ein Drittel kürzer geworden. Die grobe Architektur konnte beibehalten werden. Anstelle der v8-Engine ist Jint getreten. Sqlite wird immernoch für das Caching verwendet, allerdings in einer .NET Version. Schließlich konnte die CURL-Bibliothek für den Webzugriff komplett entsorgt werden, da das .NET Framework Standardklassen hierfür bereits mitbringt.

Klassendiagramm

Der Quelltext von WebGraph.NET ist im Github unter https://github.com/Chrisso/WebGraph.NET einzusehen. Am besten einfach mal lossurfen und durch die Source klicken.

Besonders interessant ist an dieser Stelle vielleicht, wie das Grabbing funktioniert. Der Nutzer gibt in der WPF-GUI ein Schlagwort ein. Diese fragt im Cache nach, ob diese Abfrage (evtl. auch als Sub-Abfrage) schonmal ausgeführt wurde. Gibt der Cache einen Fehler zurück (Cache-Miss) wird beim Skript ein Download-URL angefordert. Der Webloader kümnert sich dann um das herunterladen, die GUI gibt das Resultat erneut an das Skript, das dort Schlagwörter (Links, Kindknoten im Graphen) extrahiert. Das Ergebnis landet im Cache und je nach eingestellter Rekursionstiefe beginnt der Prozess mit jedem neuen Schlagwort von vorn.

Oder als Sequenzdiagramm ausgedrückt:

Sequenzdiagramm

Nach einem Port auf C# habe ich übrigens noch einen in JavaScript erstellt. Zur Anzeige des folgenden Beispiels benötigen sie einen HTML5-kompatiblen Browser, der das Canvas-Element unterstützt. Moderne Software wie Mozilla Firefox und Google Chrome können das von Haus, im Internet Explorer wird das Canvas mittels VML emuliert.

HTML5-Canvas nicht unterstützt!

Geogen v3.1 ist online

von Christoph

Geogen ist in der Version 3.1 online verfügbar. Die interne Datenbank-Zugriffsschicht ist nun unabhängig von einem bestimmten Hersteller. Vorher war die Verwaltung von Ranglisten, Nutzerkommentaren und Geograpphie nur mit dem MS SQL-Server via System.Data.SqlClient möglich. An dessen Stelle treten konsequent die Factory-Methoden aus System.Data.Common. Im derzeit stattfindenden Testlauf liegen alle Daten in einem SQL-Server Compact Edition 4.0 - also im selben Prozess wie ASP.NET. Dadurch sollte die Mehrzahl der Nur-Lese-Zugriffe etwas performanter sein.

Im Rahmen dieses Refactorings habe ich auch gleich die MS Ajax Bibliothek samt Ajax Control Toolkit entsorgt. Diese wird von Microsoft zwar noch ausgeliefert aber nicht mehr weiterentwickelt. Das Pferd ist also tot. Man muss sich da auch nichts vormachen: MS Ajax war zu aufgebläht, zu langsam und (was das Control Toolkit betrifft) zu fehleranfällig. Ich habe die Webseite daher komplett auf jQuery umgestellt. Es ist enorm was da grade in der Entwicklung abgeht.

Die erste Änderung, die dadurch möglich wurde, ist ein Autocompleter auf dem Eingabetextfeld. Einfach ein paar Buchstaben eingeben und im Hintergrund werden per Ajax einige Vorschläge herausgesucht, welchen Namen man meinen könnte. Das Control-Toolkit enthielt auch einen Autocompleter, allerdings vertrug er sich nicht nicht mit den per Javascript abgerundeten Ecken. Mein Bug-Report dazu wurde damit abgetan, dass man sich nicht um die Z-Order kümmern kann und das Problem nicht lösbar sei. Die jetzigen runden Ecken kommen per CSS auf alle modernen Browser. Im Internet Explorer bleibt es kantig.

Die Kartenübersicht wurde verschlankt. Die Steuerelemente für Zusatzparameter, die sowieso niemand benutzt hat, habe ich entfernt. Eine derartig fortgeschrittene Konfiguration ist nur noch über die Kaufsoftware möglich. In diesem Zuge wurden die Karten auch gleich in die Ergebnisseite integriert. Mit Hilfe der Fancybox werden sie in Form eines Overlays vor leicht ausgegrautem Hintergrund angezeigt.

Weitere Änderungen sind eher Kleinigkeiten: zu selteneren Namen wird ein maschinenlesbarer Balkencode berechnet mit dem man irgendwelche Gegenstände bekleben (neudeutsch taggen) kann, auf der Startseite läuft ein Ticker zuletzt abgerufener Namen aller Besucher, der phonetische Namensgraph hat den Beta-Status verlassen und ist nun auch in der englischen Version verfügbar und ganz neu gibt es auch eine Fassung für Mobilgeräte. Das nächste Update soll dann ein größeres werden (Version 4.0 oder zumindest 3.5). Ich experimentiere dazu etwas mit HTML5 und Canvas...

360° Panorama

von Christoph

Nun mal wieder ein Panorama. Die älteren bestanden aus 5 Einstellungen pro Panorama, diesmal sollte es etwas mehr sein. Das Maximum von 360°. Ich habe 67 Aufnahmen in je drei Belichtungen (HDR) gemacht. Das ist soviel, weil ich sogar den Boden unter meinen Füßen und den Himmel über meinem Kopf geknipst habe. Leider hat das beim Ausbelichten dann Probleme bereitet: der wirklich interessante Bereich um den Horizont wirkte dunkel und flau. Boden und Himmel habe ich deswegen leider weggeschnitten, das Panorama lässt sich nur um 12° nach oben und nach unten neigen.

Die Darstellung läuft in einem Flash-Plugin ab, das ich überlicherweise nicht so mag. Aber hier war es ziemlich einfach für das Web einen Viewer zu schreiben, der Skyboxen, genauer Skyspheres beherrscht. Wobei schreiben ist übertrieben. Mit PaperVision3D gibt es eine ziemlich ausgereifte 3D-Engine in Flash, die man nur noch integrieren braucht. Achja, der Aufnahmeort.

Nachtrag 27.04.2017: nun sind so viele Jahre vergangen, dass kein Mensch mehr Flash benutzt und ich keinen Browser mehr habe, der das Panorama anzeigen kann. Ersteres ist gut, letzteres nicht so ganz. Daher das ganze schnell als Javascript nachimprovisiert. Allerdings mit einem Sky-Cylinder, die Darstellung ist also nicht korrekt projeziert. Man schiebt nur noch die Tapete hin und her, jedoch wäre mir eine texturierte Kugel in WebGL jetzt zu viel Aufwand.