Wie eBay einen Barcode-Scanner in WebAssembly erstellt hat

Seit ihrer Ankündigung hat die WebAssembly- Technologie sofort die Aufmerksamkeit von Front-End-Entwicklern auf sich gezogen. Die Web-Community akzeptierte begeistert die Idee, Code in einem Browser auszuführen, der in anderen Sprachen als JavaScript geschrieben ist. Die Hauptsache ist, dass WebAssembly eine viel höhere Geschwindigkeit als JavaScript garantiert.

Unsere Ingenieure haben die Entwicklung des Standards genau verfolgt. Sobald die Unterstützung für WebAssembly 1.0 in allen gängigen Browsern implementiert war, wollten die Entwickler sie sofort ausprobieren.

Aber es gab ein Problem. Obwohl viele Anwendungen von WebAssembly profitieren, ist der Umfang der Technologie im E-Commerce immer noch primitiv. Wir konnten nicht sofort die richtige Version seiner Verwendung finden. Es gab einige Vorschläge, aber JavaScript war in allen Variationen besser. Bei der Bewertung neuer Technologien bei eBay lautet die erste Frage: „Was sind die potenziellen Vorteile für unsere Kunden?“ Wenn hier keine Klarheit besteht, werden wir nicht mit dem nächsten Schritt fortfahren. Es ist sehr einfach, sich von neuer modischer Technologie mitreißen zu lassen, auch wenn dies für die Kunden keine Rolle spielt und nur den bestehenden Workflow kompliziert. Die Benutzererfahrung ist immer wichtiger als die Entwicklererfahrung. Aber mit WebAssembly anders. Diese Technologie hat ein enormes Potenzial, wir konnten einfach nicht den richtigen Anwendungsfall finden. Am Ende fanden sie es jedoch immer noch.

Barcode-Scanner


In nativen eBay-Apps für iOS und Android gibt es eine UPC- Barcode-Scanfunktion, mit der das Formular automatisch eingegeben werden kann. Es funktioniert nur in Anwendungen und erfordert eine intensive Verarbeitung von Bildern auf dem Gerät, um die Barcode-Ziffern im Bildstrom von der Kamera zu erkennen. Der resultierende Code wird dann an den Serverdienst gesendet, der seinerseits das Formular ausfüllt. Dies bedeutet, dass die Bildverarbeitungslogik auf dem Gerät sehr effizient sein muss. Für native Anwendungen haben wir unsere eigene C ++ - Bibliothek in nativen Code für iOS und Android kompiliert. Es erkennt Barcodes außergewöhnlich gut. Wir wechseln schrittweise zu nativen APIs in iOS und Android, aber unsere C ++ - Bibliothek ist immer noch zuverlässig.

Der Barcode-Scanner ist eine intuitive Funktion für Verkäufer und vereinfacht das Ausfüllen des Formulars erheblich. Leider funktionierte diese Funktion auf der mobilen Version der Website nicht und Verkäufer mussten die UPC manuell eingeben, was unpraktisch ist.

Web-Barcode-Scanner


Früher haben wir nach einer Option zum Scannen von Barcodes im Web gesucht. Vor zwei Jahren veröffentlichten sie sogar einen Prototyp, der auf der Open-Source-JavaScript-Bibliothek BarcodeReader basiert. Das Problem war, dass es nur in 20% der Fälle gut funktionierte. Die restlichen 80% der Zeit arbeitete der Scanner extrem langsam oder überhaupt nicht. In den meisten Fällen war es eine Auszeit. Es wird durchaus erwartet: JavaScript kann nur dann in seiner Geschwindigkeit mit nativem Code verglichen werden, wenn es sich auf einem „heißen Weg“ befindet, dh von JIT- Compilern stark optimiert wird. Der Trick besteht darin, dass JavaScript-Engines zahlreiche Heuristiken verwenden, um festzustellen, ob ein Pfad "heiß" ist, ohne ein Ergebnis zu garantieren. Diese Diskrepanz führte offensichtlich zu Frustration der Benutzer, und wir mussten diese Funktion deaktivieren. Aber jetzt ist alles anders. Mit der rasanten Entwicklung der Webplattform stellte sich die Frage: "Ist es möglich, einen zuverlässigen Barcode-Scanner im Web zu implementieren?"

Eine Möglichkeit besteht darin, darauf zu warten, dass die Formerkennungs-API mit ihren integrierten Bilderkennungsfunktionen, einschließlich Barcodes, beendet wird . Diese Schnittstellen befinden sich jedoch noch in einem sehr frühen Entwicklungsstadium und sind weit von einer browserübergreifenden Kompatibilität entfernt. Und selbst in diesem Fall ist die Arbeit auf allen Plattformen nicht garantiert . Daher müssen Sie andere Optionen in Betracht ziehen.

Hier kommt WebAssembly ins Spiel. Wenn ein Barcode-Scanner in WebAssembly implementiert ist, funktioniert er garantiert. Die starke Typisierungs- und Bytecode-Struktur von WebAssembly ermöglicht es Ihnen, immer den "Hot Path" der Ausführung beizubehalten. Darüber hinaus verfügen wir bereits über eine C ++ - Bibliothek für native Anwendungen. C ++ - Bibliotheken sind ideale Kandidaten für die Kompilierung in WebAssembly. Wir dachten, das Problem sei gelöst. Es stellte sich heraus, nicht wirklich.

Architektur


Die funktionierende Prototyparchitektur für den Barcode-Scanner in WebAssembly war ziemlich einfach.

  • Kompilieren Sie die C ++ - Bibliothek mit Emscripten . Es werden die Middleware und die WASM-Datei erstellt.
  • Wählen Sie einen Arbeitsthread aus dem Hauptthread aus. Der JavaScript-Code für den Worker importiert den generierten JavaScript-Verknüpfungscode, der wiederum die WASM-Datei erstellt.
  • Der Hauptstrom sendet einen Schnappschuss vom Strom von der Kamera an den Arbeitsstrom und ruft die entsprechende WASM-API über den Verbindungscode auf. Die API-Antwort wird an den Hauptthread übergeben. Die Antwort kann eine UPC-Zeichenfolge (die an das Backend übergeben wird) oder eine leere Zeichenfolge sein, wenn kein Barcode erkannt wird.
  • Für eine leere Antwort wird der obige Schritt wiederholt, bis ein Barcode erkannt wird. Dieser Zyklus läuft für das angegebene Zeitintervall in Sekunden. Sobald der Schwellenwert erreicht ist, wird eine Warnmeldung angezeigt: „Ungültiger Produktcode. Versuchen Sie es mit einer anderen Barcode- oder Textsuche . Entweder hat der Benutzer die Kamera nicht auf einen echten Barcode fokussiert, oder der Scanner ist nicht effektiv genug. Wir verfolgen Statistiken zu Zeitüberschreitungen als Indikator für die Qualität des Scanners.


WebAssembly-Workflow

Zusammenstellung


Der erste Schritt in einem WebAssembly-Projekt besteht darin, eine übersichtliche Kompilierungspipeline zu definieren. Emscripten ist zum De-facto-Standard für die Kompilierung von WebAssembly geworden. Es ist jedoch wichtig, eine konsistente Umgebung zu haben, die ein deterministisches Ergebnis liefert. Unser Frontend basiert auf Node.js, daher müssen wir eine Lösung finden, die mit dem npm-Workflow kompatibel ist. Glücklicherweise veröffentlichte Surma Das zu dieser Zeit einen Artikel mit dem Titel „Emscripten and npm“ . Der Docker- basierte Ansatz zum Kompilieren von WebAssembly ist sinnvoll, da hierdurch eine Menge Overhead vermieden wird . Wie im Artikel empfohlen, haben wir das Docker- Image von Emscripten von trzeci übernommen . Um die Kompilierung in WebAssembly zu ermöglichen, musste die native C ++ - Bibliothek etwas optimiert werden. Grundsätzlich haben wir zufällig, durch Versuch und Irrtum gehandelt. Am Ende gelang es mir, es zu kompilieren und einen ordentlichen WebAssembly-Workflow innerhalb der vorhandenen Assembly-Pipeline einzurichten.

Es funktioniert schnell, aber ...


Die Scannerleistung wird anhand der Anzahl der von der Wasm-API pro Sekunde verarbeiteten Frames gemessen. Die Wasm-API nimmt einen Frame aus dem Videostream der Kamera, führt Berechnungen durch und gibt eine Antwort zurück. Dies erfolgt fortlaufend, bis ein Barcode erkannt wird. Die Leistung wird in FPS gemessen.

Unsere Testimplementierung von WebAssembly zeigte eine erstaunliche Geschwindigkeit von 50 FPS. Es funktionierte jedoch nur in 60% der Fälle und im Rest stürzte es durch Timeout ab. Selbst mit solch einer hohen FPS konnten sie den Barcode für die verbleibenden 40% der Scans nicht schnell erkennen und gaben am Ende eine Warnmeldung aus. Im Vergleich dazu lief die vorherige JavaScript-Implementierung normalerweise mit 1 FPS. Ja, WebAssembly ist viel schneller (50-mal), aber aus irgendeinem Grund funktioniert es in fast der Hälfte der Fälle nicht. Es sollte auch beachtet werden, dass JavaScript in einigen Situationen sehr gut funktionierte und den Barcode sofort fand. Eine der offensichtlichen Optionen bestand darin, das Zeitlimit zu erhöhen. Dies erhöht jedoch nur die Frustration der Benutzer, sodass wir das eigentliche Problem nicht lösen. Deshalb haben wir diese Idee aufgegeben.

Zuerst konnten wir nicht verstehen, warum die native C ++ - Bibliothek, die in nativen Anwendungen perfekt funktionierte, im Web nicht das gleiche Ergebnis zeigte. Nach langem Testen und Debuggen haben wir festgestellt, dass die Erkennungsgeschwindigkeit vom Fokuswinkel des Objekts und dem Hintergrundschatten abhängt. Aber wie funktioniert dann alles in nativen Anwendungen? Tatsache ist, dass wir in nativen Anwendungen die integrierten APIs für den Autofokus verwenden und dem Benutzer die Möglichkeit bieten, manuell zu fokussieren, indem er mit dem Finger auf den Barcode zeigt. Daher bieten native Anwendungen der Bibliothek immer klare Bilder in hoher Qualität.

Wir erkannten die Essenz des Geschehens und beschlossen, eine andere native Bibliothek auszuprobieren: einen recht beliebten und stabilen Open-Source- ZBar- Barcode-Scanner. Noch wichtiger ist, dass es gut mit verschwommenen und körnigen Bildern funktioniert. Warum probieren Sie es nicht aus? Da wir bereits über den WebAssembly-Workflow verfügten, verlief die Kompilierung und Bereitstellung von ZBar in WebAssembly reibungslos. Die Leistung erwies sich mit 15 FPS als anständig, wenn auch nicht so gut wie die unserer eigenen C ++ - Bibliothek. Bei gleichem Timeout lag die Erfolgsquote jedoch nahe bei 80%. Eine deutliche Verbesserung gegenüber unserer C ++ - Bibliothek, aber immer noch nicht 100%.

Das Ergebnis hat uns noch nicht zufrieden gestellt, aber wir haben etwas Unerwartetes bemerkt. Wo Zbar ausfiel, erledigte unsere eigene C ++ - Bibliothek die Arbeit sehr schnell. Es war eine angenehme Überraschung. Es scheint, dass Bibliotheken Bilder unterschiedlicher Qualität auf unterschiedliche Weise verarbeiteten. Dies führte uns zu der Idee.

Multithreading und Speed ​​Racing


Du hast es wahrscheinlich schon verstanden. Erstellen Sie zwei Arbeitsthreads: einen für Zbar und einen für unsere C ++ - Bibliothek und führen Sie sie nicht parallel aus. Wer gewonnen hat (wer zuerst einen gültigen Barcode sendet), sendet das Ergebnis an den Hauptstrom, und beide Mitarbeiter halten an. Wir haben ein solches Szenario implementiert und uns selbst getestet, um so viele Szenarien wie möglich zu simulieren. Diese Einstellung zeigte 95% der erfolgreichen Scans. Viel besser als die vorherigen Ergebnisse, aber immer noch nicht 100%.

Einer der seltsamen Vorschläge war, die ursprüngliche JavaScipt-Bibliothek zum Wettbewerb hinzuzufügen. Es werden drei Streams sein. Wir haben ehrlich gesagt nicht gedacht, dass dies etwas ändern würde. Ein solcher Test erforderte jedoch keinen Aufwand, da wir die Arbeitsoberfläche standardisiert haben. Zu unserer Überraschung lag die Erfolgsquote mit drei Streams tatsächlich nahe bei 100%. Dies war wiederum völlig unerwartet. Wie bereits erwähnt, hat JavaScript in einigen Situationen sehr gut funktioniert. Anscheinend schloss er die Lücke. Die populäre Weisheit des Gesetzes lautet also: "JavaScript gewinnt immer . " Wenn ohne Witze, bietet die folgende Abbildung einen Überblick über die endgültige Architektur, die wir implementiert haben.


Barcode-Scanner für die Webarchitektur

Die folgende Abbildung zeigt ein Funktionsdiagramm auf hoher Ebene:


Funktionsdiagramm eines Barcode-Scanners

Hinweis zum Laden von Ressourcen


Die für das Funktionieren des Scanners erforderlichen Ressourcen werden nach dem Rendern der Hauptseite vorinstalliert. Auf diese Weise wird die Zielseite schnell geladen und ist für die Interaktion bereit. WebAssembly-Ressourcen (WASM-Dateien und Middleware-Skripte) und die JavaScript-Scannerbibliothek werden nach dem Laden der Hauptseite mithilfe von XMLHttpRequest vorinstalliert und zwischengespeichert. Hierbei ist es wichtig, dass sie nicht sofort ausgeführt werden, damit der Hauptthread für die Benutzerinteraktion mit der Seite frei bleibt. Die Ausführung erfolgt nur, wenn der Benutzer auf das Barcodesymbol klickt. Wenn der Benutzer vor dem Laden der Ressourcen auf das Symbol geklickt hat, werden diese bei Bedarf geladen und sofort ausgeführt. Der Barcode-Scanner-Ereignishandler und der Worker-Controller werden mit der Seite geladen, sind jedoch sehr klein.

Ergebnisse


Nach strengen Tests und internem Gebrauch durch die Mitarbeiter haben wir A / B-Tests für Benutzer gestartet. Das Scannersymbol (Abbildung unten) wurde der Testgruppe angezeigt, nicht jedoch der Kontrollgruppe.


Endprodukt

Um den Erfolg zu messen, haben wir die Metrik Draft Completion Rate eingeführt. Dies ist die Zeit zwischen dem Beginn der Bearbeitung eines Entwurfs und dem Absenden eines Formulars. Die Metrik sollte zeigen, wie ein Barcode-Scanner beim Ausfüllen von Formularen hilft. Der Test dauerte mehrere Wochen und die Ergebnisse waren sehr angenehm. Sie stimmen voll und ganz mit unserer ursprünglichen Hypothese überein. Die Entwurfsabschlusszeit wurde für einen Stream mit einem Barcode-Scanner um 30% verringert.


A / B-Testergebnisse

Wir haben auch Profilerstellung hinzugefügt, um die Wirksamkeit aller Scannertypen zu bewerten. Den größten Beitrag leisteten erwartungsgemäß Zbar (53% der erfolgreichen Scans), dann unsere C ++ - Bibliothek (34%) und schließlich die JavaScript-Bibliothek mit 13%.



Fazit


Die Erfahrung bei der Implementierung von WebAssembly ist für uns sehr informativ geworden. Ingenieure freuen sich sehr über die Entstehung neuer Technologien und möchten diese sofort ausprobieren. Wenn die Technologie auch für Kunden nützlich ist, ist dies eine doppelte Freude. Lassen Sie uns den am Anfang des Artikels geäußerten Gedanken wiederholen. Die Technologie entwickelt sich sehr schnell. Jeden Tag erscheint etwas Neues. Für Kunden sind jedoch nur wenige Technologien von Bedeutung, und WebAssembly ist eine davon. Unsere größte Schlussfolgerung aus dieser Übung ist, in 99 Situationen „Nein“ und in dem einzigen Fall „Ja“ zu sagen, wenn dies für Kunden wirklich wichtig ist.

In Zukunft planen wir, die Verwendung eines Barcode-Scanners zu erweitern und auf Käuferseite einzuführen, damit diese Produktcodes offline für die Suche und den Kauf bei eBay scannen können. Wir werden auch erwägen, die Funktion mithilfe der Formerkennungs-API und anderer Funktionen im Browser zu erweitern. Wir freuen uns jedoch, den richtigen Anwendungsfall für WebAssembly bei eBay gefunden und die Technologie erfolgreich im E-Commerce eingesetzt zu haben.

Besonderer Dank geht an Surma Das und Lin Clark für zahlreiche Artikel zu WebAssembly. Sie haben uns wirklich geholfen, die Sackgasse mehrmals zu überwinden.

Source: https://habr.com/ru/post/de453712/


All Articles