14 Punkte von GN⁺ 2024-11-08 | 3 Kommentare | Auf WhatsApp teilen
  • recall ist ein Dienst, der Meeting-Bots für Hunderte von Unternehmen anbietet und auf AWS eine große Infrastruktur betreibt
  • Für einen kosteneffizienten Service wollte das Unternehmen die Hardwareleistung maximal ausnutzen
  • Da die GPU-Verfügbarkeit bei Cloud-Anbietern in den letzten Jahren instabil war, wurde die Videobearbeitung auf der CPU statt auf der GPU durchgeführt
  • Beim Profiling der Bots mit headlosem Chromium zeigte sich, dass der Großteil der CPU-Zeit nicht für die Videobearbeitung (Encoding/Decoding), sondern für die Speicher-Kopierfunktionen __memmove_avx_unaligned_erms und __memcpy_avx_unaligned_erms verbraucht wurde
    • memmove und memcpy sind Funktionen zum Kopieren von Speicherblöcken in der C-Standardbibliothek (glibc)
    • memmove behandelt einige Sonderfälle beim Kopieren überlappender Speicherbereiche, aber beide Funktionen lassen sich als „Speicher-Kopier“-Funktionen einordnen
    • Das Suffix avx_unaligned_erms bedeutet, dass sie für Systeme mit Unterstützung für Advanced Vector Extensions (AVX) optimiert sind und auch nicht ausgerichtete Speicherzugriffe effizient behandeln
    • erms steht für Enhanced REP MOVSB/STOSB, eine Optimierung für schnelle Speicherbewegungen auf modernen Intel-Prozessoren. Man kann es als „schnellere Implementierung für bestimmte Prozessoren“ verstehen
  • Das Profiling zeigte, dass diese Funktionen am häufigsten vom Python-WebSocket-Client aufgerufen wurden, der Daten empfängt
    • Danach folgte Chromiums WebSocket-Implementierung, die Daten sendet

Das Problem mit WebSockets

  • Es wurde ein lokaler WebSocket-Server verwendet, um rohe Videodaten aus der JS-Umgebung von Chromium an den Encoder zu übertragen
  • Ein roher 1080p-30fps-Videostream erfordert eine hohe Bandbreite von mehr als 93 MB pro Sekunde
  • Der Einsatz von WebSockets verursachte hohe Rechenkosten, wobei die Hauptursachen Fragmentierung und Maskierung waren
    • Fragmentierung: Chromiums WebSocket-Implementierung fragmentiert Nachrichten über 131 KB in mehrere Frames. Rohe Videoframes von mehr als 3 MB wurden aufgeteilt und in mehr als 24 separaten Frames übertragen
    • Maskierung: Aus Sicherheitsgründen maskiert WebSocket alle vom Client zum Server gesendeten Frames. Bei großen Datenmengen von über 100 MB pro Sekunde wird das zu einem spürbaren Overhead

Suche nach Alternativen

  • Mit Browser-APIs ließ sich nur schwer etwas realisieren, das deutlich leistungsfähiger als WebSocket ist, daher wurde beschlossen, Chromium zu forken und benutzerdefinierte Funktionen zu implementieren
  • Drei Alternativen wurden geprüft: raw TCP/IP, Unix Domain Socket und Shared Memory
    • TCP/IP: Die Probleme von WebSocket mit Fragmentierung/Maskierung lassen sich vermeiden, aber die maximale Paketgröße ist klein, sodass Fragmentierung weiterhin ein Problem bleibt. Zudem entsteht Kopier-Overhead in den Kernel-Space
    • Unix Domain Socket: Der Netzwerk-Stack lässt sich vollständig umgehen, aber es ist weiterhin eine Datenkopie zwischen User-Space und Kernel-Space erforderlich
    • Shared Memory: Speicher, auf den mehrere Prozesse gleichzeitig zugreifen können. Chromium kann ohne Zwischenkopie direkt in den Shared Memory schreiben, und der Encoder kann ihn sofort lesen

Implementierung einer Shared-Memory-basierten Übertragung

  • Um Daten im Shared Memory fortlaufend zu lesen und zu schreiben, wurde eine Ringpuffer-Struktur implementiert
  • Anforderungen: lock-free, mehrere Produzenten/ein Konsument, variable Frame-Größen, Zero-Copy-Lesen, Sandbox-Freundlichkeit, Signalisierung mit geringer Latenz
  • Vorhandene Ringpuffer-Implementierungen wurden evaluiert, aber keine erfüllte alle Anforderungen, daher wurde eine eigene Implementierung entwickelt
  • Um Zero-Copy-Lesen zu unterstützen, wurden die Pointer in drei Zustände aufgeteilt: write, peek und read
  • Für Thread-Sicherheit wurden atomare Operationen verwendet, und um neue Daten bzw. frei gewordenen Speicherplatz zu signalisieren, kamen named semaphores zum Einsatz
  • Durch die Shared-Memory-basierte Ringpuffer-Implementierung und weitere Optimierungen konnte die CPU-Auslastung der Bots um bis zu 50 % gesenkt werden. Dadurch wurden letztlich jährlich mehr als eine Million US-Dollar an AWS-Kosten eingespart.

3 Kommentare

 
GN⁺ 2024-11-08
Hacker-News-Kommentare
  • Das ist die typische Geschichte eines Startups, das einen „gut genug“-Shortcut wählt und erst später optimiert.

    • In einem Unternehmen gab es einen VM-Cluster mit hoher CPU-Auslastung, und zur Optimierung wurde ein Profiler eingesetzt.
    • Durch das Löschen alter Daten und das Hinzufügen von Filtern zu Abfragen konnte die CPU-Auslastung gesenkt werden.
  • Es gibt die Meinung, dass die hohe Bandbreite der Rohvideodaten überraschend ist.

    • Die Designentscheidungen von WebSockets werden kritisiert, weil dabei Probleme bei der CPU-Auslastung nicht vorhergesehen wurden.
  • Es gibt die Meinung, dass es kein AWS-Problem ist, sondern ein Problem verschwendeter CPU-Zyklen.

    • WebSockets stehen im Zusammenhang mit Kosten für Datenübertragung oder API-Gateways.
  • Es wird darauf hingewiesen, dass MTU und MSS in TCP/IP-Netzwerken im Vergleich zur Größe von Videoframes klein sind.

    • Es wird mangelndes technisches Wissen kritisiert und behauptet, dass man Entwickler einstellen müsse.
  • Es gibt die Meinung, dass man mit Chromiums Mojo keinen plattformspezifischen Code berücksichtigen muss.

    • Auch eine benutzerdefinierte Ringpuffer-Implementierung wird für okay gehalten.
  • Es gibt die Meinung, dass nicht das Netzwerk das Problem sei, sondern mangelndes Verständnis für Video-Codecs.

    • Es sei unverständlich, warum keine Video-Streaming-Protokolle wie RDP verwendet wurden.
  • Die Transparenz wird gelobt, und es wird gesagt, man wünsche sich auch Transparenz bei den Produktpreisen.

  • Es wird erklärt, dass das Masking im WebSocket-Protokoll ein Versuch ist, Probleme mit Man-in-the-Middle-Angreifern zu lösen.

    • Es wird gesagt, dass es sich lohnt, die zugehörige RFC zu lesen.
  • Es wird darauf hingewiesen, dass die Übertragung von Videodaten ohne Komprimierung seltsam ist.

    • Es sei unverständlich, warum kein komprimierter Stream übertragen wird.
  • Es wird gesagt, dass der anfängliche Ansatz, Rohvideo über WebSocket zu übertragen, überraschend ist.

    • Die Ineffizienz habe die Produktentwicklung nicht behindert.
    • Es sei unverständlich, wie man bei einem solchen Ansatz die Datenmenge nicht berücksichtigen könne.
 
ahwjdekf 2024-11-09
  • Ich denke, bei der Produktentwicklung wurden Performance-Aspekte überhaupt nicht berücksichtigt.
  • Letztlich läuft dieses Problem darauf hinaus, wie große Datenmengen per IPC übertragen werden sollen.
  • Der Unterschied ist, dass es sich nicht um gewöhnliche IPC handelt, sondern um IPC mit dem Chrome-Browser, und
  • die internen Mechanismen des Chrome-Browsers sind zwar nicht gerade einfach, aber offen, daher wären Änderungen möglich.
  • Am Ende ist es also eine Frage der Wahl von IPC.

Sie haben also von Anfang an falsch entwickelt..

 
ahwjdekf 2024-11-09

„Dass der anfängliche Ansatz, Rohvideo über WebSocket zu übertragen, erstaunlich sei.“ Dem stimme ich zu.