1 Punkte von GN⁺ 2025-09-28 | 1 Kommentare | Auf WhatsApp teilen
  • Etwa 20 % des HTTP-Traffics von Firefox nutzen HTTP/3, das auf QUIC und UDP läuft
  • Durch den Ersatz der bestehenden Netzwerk-I/O-Schicht NSPR durch das Rust-basierte quinn-udp werden Leistung und Speichersicherheit verbessert
  • Zur Leistungsoptimierung werden moderne System-Calls pro Betriebssystem (Multi-Message, Segmentation Offloading usw.) aktiv genutzt
  • Unter Windows und macOS waren einige Funktionen wegen Kompatibilitäts- und Treiberproblemen eingeschränkt, unter Linux wurde jedoch die beste Leistung bestätigt
  • Die vielfältigen plattformspezifischen Erfahrungen mit QUIC-ECN-Support und UDP-I/O, einschließlich Fehlversuchen und Bugfixes, dürften auch künftigen Projekten und dem Open-Source-Ökosystem zugutekommen

Motivation

  • Etwa 20 % des HTTP-Traffics von Firefox nutzen HTTP/3, das über QUIC arbeitet und wiederum auf UDP aufsetzt
  • Firefox nutzte historisch die Bibliothek NSPR für Netzwerk-I/O, doch deren Funktionen für UDP-I/O sind veraltet und eingeschränkt (wichtige Funktionen sind PR_SendTo, PR_RecvFrom)
  • Betriebssysteme bieten inzwischen Multi-Message-System-Calls (z. B. sendmmsg, recvmmsg) und Netzwerkoptimierungen wie Segmentation Offloading (GSO, GRO)
  • Diese Techniken können die Leistung von UDP-I/O deutlich steigern
  • Firefox untersuchte, ob sich diese Vorteile nutzen lassen, indem der bestehende UDP-I/O-Stack durch moderne System-Calls ersetzt wird

Überblick

  • Das Projekt startete Mitte 2024 mit dem Ziel, Firefoxs QUIC-UDP-I/O-Stack auf allen unterstützten Betriebssystemen mit modernen System-Calls neu aufzubauen
  • Neben Leistungsverbesserungen sollte durch speichersicheres Rust auch die Sicherheit von UDP-I/O erhöht werden
  • QUIC selbst war bereits in Rust implementiert, daher wurde die Entwicklung auf Basis der Rust-Bibliothek quinn-udp vorangetrieben
  • Unterschiede bei den System-Calls zwischen den Betriebssystemen erhöhten die Entwicklungskomplexität, doch dank quinn-udp ging die Entwicklung deutlich schneller voran
  • Stand Mitte 2025 läuft die Auslieferung an die meisten Firefox-Nutzer, und Leistungsbenchmarks zeigen einen deutlichen Anstieg auf bis zu 4 Gbit/s

Aufbau von UDP-I/O und moderne Optimierungsverfahren

Versand einzelner Datagramme

  • Das bisherige Verfahren nutzte sendto und recvfrom und konnte pro Aufruf nur ein einzelnes UDP-Datagramm senden oder empfangen
  • Die Kosten für den Wechsel zwischen User Space und Kernel Space fallen damit pro Datagramm an und sind bei hohem Traffic ineffizient
  • Beispiel: Werden Pakete unter 1500 Byte mit mehreren hundert Mbit/s pro Sekunde gesendet, entsteht erheblicher Overhead

Batch-Übertragung mehrerer Datagramme

  • Linux und einige andere Betriebssysteme unterstützen Multi-Message-System-Calls wie sendmmsg und recvmmsg
  • Mehrere Datagramme können in einem Aufruf gesendet oder empfangen werden, wodurch sich der Overhead stark reduzieren lässt

Ein einzelnes großes segmentiertes Datagramm

  • Offload-Techniken wie GSO (Senden) und GRO (Empfangen) ermöglichen es, große UDP-Datagramme automatisch im Betriebssystem oder auf der NIC zu segmentieren
  • Die Netzwerkschnittstelle übernimmt die Aufteilung in einzelne Pakete sowie Checksum-Berechnung und Header-Erstellung
  • Dadurch kann die Anwendung mit nur einem einzigen System-Call viele tatsächliche Pakete verarbeiten
  • Bei aktiviertem GSO ist die Paketunterstützung einiger Netzwerkanalyse-Tools wie Wireshark eingeschränkt

Der Ersatz von NSPR in Firefox

  • Zunächst wurde NSPR im Senden und Empfangen einzelner Datagramme durch quinn-udp ersetzt
  • Die Datagramm-Verarbeitungspipeline der QUIC-Implementierung wurde so refaktoriert, dass Batch-Senden/-Empfangen und Segmentierung unterstützt werden
  • Sowohl Multi-Message-Calls als auch Calls für Segmentation Offloading werden je nach Situation genutzt
  • Zusätzlich wurden plattformspezifische Ausnahmen und verschiedene I/O-Verbesserungen ergänzt

Details nach Plattform

Windows

  • Windows bietet WSASendMsg/WSARecvMsg und unterstützt traditionelle Datagramme in MTU-Größe sowie große segmentierte Datagramme
  • Das Windows-Gegenstück zu GSO/GRO unter Linux ist USO (Senden) / URO (Empfangen)
  • Anfangs wurden nur Aufrufe für einzelne Datagramme genutzt, was problemlos funktionierte. Mit aktiviertem URO trat jedoch in bestimmten Umgebungen (z. B. Windows on ARM + WSL) ein Bug auf, bei dem sich die Länge von QUIC-Paketen nicht bestimmen ließ, was zu fehlgeschlagenem Seitenladen führte
  • Auch USO für das Senden wurde verwendet, allerdings zeigten sich in Firefox-Installationen unter Windows Nebenwirkungen wie mehr Paketverlust und Abstürze von Netzwerktreibern
  • Derzeit bleiben URO/USO in Firefox deaktiviert, während weitere Debugging-Arbeiten laufen

macOS

  • Unter macOS wurde quinn-udp mit sendmsg/recvmsg anstelle von sendto/recvfrom eingeführt
  • Segmentation Offloading ist nicht aktiviert
  • Stattdessen unterstützen die nicht offiziell dokumentierten Aufrufe sendmsg_x/recvmsg_x Batch-Übertragungen, die inoffiziell in quinn-udp eingebaut wurden
  • Da Apple diese Aufrufe künftig entfernen könnte, wurden sie nur getestet und nicht standardmäßig aktiviert und daher nicht in die tatsächliche Veröffentlichung aufgenommen

Linux

  • sendmmsg/recvmmsg sowie GSO/GRO werden vollständig unterstützt, und quinn-udp priorisiert beim Senden standardmäßig GSO
  • Firefox verwendet für jede Verbindung einen eigenen UDP-Socket, um die Privatsphäre zu stärken (Unterscheidung per 4-Tuple)
  • In dieser Struktur werden die Vorteile von Segmentation Offloading maximiert, während die Vorteile von sendmmsg/recvmmsg über mehrere Übertragungen hinweg begrenzt sind
  • Abgesehen von kleineren Änderungen wie Netzwerk-Sandboxing und Laufzeitprüfungen für GSO-Support gelang die Einführung ohne größere Schwierigkeiten

Android

  • Android unterscheidet sich von Linux durch andere Pfade bei der Verarbeitung von System-Calls und durch Sicherheitsfilter (z. B. seccomp)
  • Es gab verschiedene Kompatibilitätsprobleme, etwa die Unterstützung sehr alter Plattformen wie Android 5 auf x86, das Umgehen von socketcall und die Fehlerbehandlung
  • In manchen Umgebungen führten Sendeaufrufe mit aktiviertem ECN-Bit zu Fehlern (EINVAL), worauf quinn-udp mit einer Strategie aus Wiederholungsversuchen und Deaktivierung der Option reagiert
  • Dank verschiedener Verbesserungen aus der Quinn-Community kann auch Firefox automatisch von diesen Verbesserungen profitieren

ECN-Unterstützung (Explicit Congestion Notification)

  • Durch die Einführung moderner System-Calls wurde die Unterstützung für ancillary data beim Senden und Empfangen möglich, wodurch QUIC-ECN unterstützt werden kann
  • Es gab kleinere Bugs, doch in Firefox Nightly läuft bei mehr als der Hälfte der QUIC-Verbindungen der ausgehende ECN-Pfad
  • Mit dem Aufkommen neuer Techniken wie L4S steigen Bedeutung und Nutzwert der ECN-Unterstützung

Zusammenfassung

  • Die QUIC-UDP-I/O-Schicht von Firefox wurde durch eine Rust-Implementierung auf Basis von quinn-udp ersetzt, wodurch sowohl Leistung als auch Sicherheit verbessert wurden
  • Anstelle veralteter System-Calls werden nun moderne I/O-System-Calls je nach Betriebssystem genutzt, was höheren Durchsatz und ECN-Support ermöglicht
  • Einige Optimierungsfunktionen, insbesondere unter Windows, benötigen wegen Kompatibilitätsproblemen noch weitere Verbesserungen
  • Da die Nutzung von QUIC weiter zunimmt, dürfte sich auch die Unterstützung auf Betriebssystem- und Treiberebene weiterentwickeln

1 Kommentare

 
GN⁺ 2025-09-28
Hacker-News-Kommentare
  • Der Kern des Artikels versteckt sich in der Mitte

    Im Extremfall verbesserte sich in einem CPU-gebundenen Benchmark der Durchsatz von unter 1 Gbit/s auf 4 Gbit/s. Im CPU-Flamegraph wurde der Großteil der Zeit für I/O-System-Calls und Verschlüsselungscode aufgewendet.
    Dass sich der Netzwerkdurchsatz um 400 % erhöht hat, bedeutet, dass die CPU-Nutzung bei UDP-Netzwerkverkehr stark gesunken ist.
    Das ist besonders beeindruckend im Hinblick auf bessere Energieeffizienz bei mobilen und tragbaren Clients wie Smartphones und Laptops.
    Solche Umstellungen werden oft pauschal als gut angesehen, aber dieser Beitrag wirkt erfrischend, weil er es mit echten Daten belegt.

    • Ich frage mich, ob irgendwann der Tag kommt, an dem Message Passing über die Grenze zwischen User- und Systemprogrammen hinweg per Hardwarebeschleunigung läuft.
  • Die hier genannten Verbesserungen sind zwar für wirklich hohe Geschwindigkeiten (100 Gb/s und mehr) notwendig, aber 4 Gb/s ist ehrlich gesagt gar nicht so schnell.
    Das sind 500 MB/s, also bedeutet es, dass irgendwo ein ernsthaft langsamer Flaschenhals steckt.
    Wenn Kernel-Kontextwechsel im Bereich von 1 µs liegen, ist das für einen System-Call eigentlich eher hoch.
    Bei im Schnitt nur etwa 500 Byte pro Paket lassen sich aber bereits 500 MB/s, also 4 Gb/s, erreichen.
    Die früheren 1 Gb/s bezogen sich wohl auf kleinere Pakete, und UDP-Pakete einfach in den NIC-Buffer zu schieben, sollte selbst mit Speicherkopiergeschwindigkeit problemlos machbar sein.
    Selbst wenn die Verschlüsselung langsam wäre, ist sie es in der Praxis eigentlich nicht.
    Ein Intel i5-6500 erreichte zum Beispiel bereits 1729 MB/s bei AES-128 GCM.
    Aktuelle CPUs schaffen pro Kern 3–5 GB/s, also 25–40 Gb/s; die hier genannten 4 Gb/s sind dafür viel zu niedrig.
    (Referenzlink zur AES-128-GCM-Leistung)

    • Es wurde gesagt, die System-Call-Latenz sei hoch; die Ursache könnten aber Maßnahmen gegen Spectre & Meltdown sein.
      TCP hat Path Binding, UDP dagegen nicht, daher gibt es Unterschiede bei der Pfadkonfiguration.
      Dass Verschlüsselung langsam sei, stimmt bei kleinen PDUs (Protocol Data Units).
      Das meiste ist auf große TCP-Frames optimiert und dafür benchmarkt, daher fallen in der Realität bei kleinen Paketen die Kosten für das Setzen des Zustands stärker ins Gewicht.
      In einem Tight Loop sehen Mikrobenchmarks gut aus, aber in realen Umgebungen mit mehr Zufälligkeit wird der Cache schlechter ausgenutzt, und bei Paketen unter 1 KB bricht die Effizienz stark ein.
      Dazu kommen zusätzlicher Framing-Overhead, Prüfungen der Gültigkeit von Daten außerhalb des Bands und weitere recht teure Arbeitsschritte.
      Auch der Speicher für UDP-Buffer ist mit den Standardwerten unzureichend und in der Praxis problematisch.
      Beim Betrieb von TCP wurden die Buffergrößen immer weiter erhöht, bei UDP ist man dagegen auf konservativen Werten aus den 90er- und 00er-Jahren stehen geblieben.
      Die wirklich nötige API müsste das Forken eines fd sowie vollständige Unterstützung für connect(2) und Route Binding bieten und danach auf einer Submission Queue basieren (uring, rio usw.).
      Auf der Kryptoseite könnte ein KDF-Ansatz die Zustandskosten stark senken.
      Der PSP-Ansatz wird zwar von einigen Anbietern anerkannt, aber bei IETF und anderswo oft abgelehnt, weshalb er sich nicht verbreitet hat.
      In groß angelegten Parallelitätstests von Anbietern zeigt sich eine deutlich bessere Skalierung als bei klassischem TLS.

    • Welche CPU-Klasse für den Benchmark verwendet wurde, wird überhaupt nicht erwähnt.
      Und der Verschlüsselungs-Overhead ist Verarbeitungskosten des QUIC-Protokolls selbst.
      QUIC ist beim Crypto-Offload (Hardwareverarbeitung) gegenüber TCP im Nachteil, während TCP mit kTLS-Offload einen Teil an die NIC auslagern kann.

  • Solche technischen Inhalte sind wirklich erfreulich.
    Ich wünschte, alle technischen Veröffentlichungen von Mozilla wären so tiefgehend und sauber von praktischen Ingenieuren geschrieben.
    Ohne oberflächlichen Optimismus (alegria) ist das sehr lesenswert.

  • Ich verstehe nicht, warum Android 5 immer noch unterstützt wird.
    Die Veröffentlichung liegt über zehn Jahre zurück, und die Nutzer solcher Geräte hängen noch tiefer im Legacy-Bereich fest.
    Das heutige Web ist so schwergewichtig, dass selbst normales Browsen auf solchen Altgeräten kaum noch vernünftig möglich sein dürfte; umso mehr frage ich mich, warum man sie weiter unterstützt.
    Vermutlich sind das nur noch Hacker, die alte Geräte wie frühe OnePlus-Modelle instand halten, am Ladegerät hängen lassen, kein verbreitetes ROM wie LineageOS installieren und Firefox aus einem alternativen App-Store nutzen.
    Realistisch betrachtet bremst das nur die gesamte Entwicklungsgeschwindigkeit.

  • Die Stelle, an der nach vielen Pingpong-Runden mit dem Issue-Reporter (ebenfalls ein Mozilla-Mitarbeiter) schließlich sogar derselbe Laptop, inklusive identischem Modell und identischer Farbe, gekauft wurde, um das Problem reproduzieren zu können
    Das war unterhaltsam, weil es die verrückte Realität der Reproduktion von Netzwerkproblemen zeigt, ganz wie in XKCD 2259.
    (Link zum xkcd-Comic)

    • Im Factorio-Entwicklungsblog „The map download struggle“ gibt es ebenfalls eine interessante dazu passende Episode; empfehlenswert.
      (Passender Blogbeitrag)

    • Wer schon einmal echte Netzwerkprobleme behandelt hat, kann wegen mysteriöser Packet Runts noch mehr damit anfangen.
      Die meisten Netzwerkgeräte kommen mit solchen Paketen nicht gut zurecht.
      UDP- oder QUIC-basierter Verkehr ist außerhalb großer Cloud-Umgebungen ab einer gewissen Größe leicht Angriffen ausgesetzt.
      Deshalb wird der Betrieb für kleine oder selbst betriebene Hosting-Anbieter immer schwieriger, und übrig bleiben nur diejenigen mit den Fähigkeiten für große Verkehrsmengen.
      Darum wird in den meisten LAN-Umgebungen der Großteil des UDP-Verkehrs verworfen und nur das Nötige mit Rate Limiting verarbeitet.

  • Mozilla-Bugtracker
    Unter macOS und Fedora erlebe ich beim Aufruf von von Cloudflare gehosteten Websites mit Firefox weiterhin dasselbe Verhalten.

  • Ich habe hier zum ersten Mal erfahren, dass es auch unter Windows und macOS Funktionen ähnlich zu GSO/GRO (Verarbeitung großer Netzwerkpakete) gibt.
    Schade nur, dass sie in der Praxis offenbar viele Bugs haben.

    • Ich frage mich, warum Microsoft und Apple nicht etwas mehr auf die Qualität ihrer Netzwerk-Stacks achten.
      Vermutlich sind nicht nur GSO/GRO fehlerhaft.
  • Kann jemand erklären, wie UDP GSO/GRO strukturell funktioniert?
    UDP ist doch ein ungeordnetes Paketprotokoll. Wenn ein QUIC-Paket auf mehrere UDP-Pakete aufgeteilt wird, gibt es im Header keine Sequenzinformationen; wie setzt der Empfänger sie dann in der richtigen Reihenfolge wieder zusammen?

    • Soweit ich verstanden habe, bekommt man auf Anwendungsebene selbst bei aktiviertem GRO in der Praxis kein tatsächlich zusammengeführtes UDP-Datagramm.
      Der Kernel packt mehrere Datagramme in eine Struktur und reicht sie weiter, wobei die Grenzen zwischen den Schichten erhalten bleiben, zum Beispiel als Data Fragments in sk_buff.
      Ich bin kein wirklicher Experte, aber bei der Recherche, wie das funktioniert, bin ich auf diesen Artikel gestoßen.
  • Es wurde erwähnt: „Wir haben die Entwicklung auf Basis von quinn-udp, der UDP-I/O-Bibliothek des Quinn-Projekts, begonnen, und das hat die Entwicklung deutlich beschleunigt.“
    Daher frage ich mich, ob das Quinn-Projekt vielleicht finanziell unterstützt wurde.
    (Link zur Quinn-Unterstützung)

    • Ich habe direkt nach finanzieller Unterstützung gefragt, und ein Senior Principal Software Engineer von Mozilla antwortete: „Mozilla hat kein Geld.“
      Allerdings hat Mozilla enorm viel Code beigesteuert, wofür wir sehr dankbar sind.
      (Ich bin der Hauptmaintainer von Quinn.)

    • Auf die Frage „Wurde gespendet?“ kam die Meinung auf, dass es eher zu Mozilla passe, Open-Source-Projekte nicht zu fördern und stattdessen mehrere Millionen Dollar zusätzlich für das CEO-Gehalt auszugeben.
      Und das, obwohl sogar das Flaggschiffprodukt (Firefox) zerfällt.

    • Mich würde interessieren, in welcher anderen Form, etwa mit Code, beigetragen wurde.

  • Es überrascht mich, dass sendmmsg/recvmmsg als „modern“ bezeichnet werden.
    Tatsächlich existieren diese System-Calls schon ziemlich lange.
    Bei Linux-Inhalten hätte ich außerdem erwartet, dass auch io_uring erwähnt wird; schade, dass das fehlt.

    • io_uring hat keine echte Batch-Funktion, um mehrere UDP-Datagramme auf einmal zu verarbeiten.
      Im besten Fall kann man mehrere sendmsg- bzw. recvmsg-Aufrufe auf einmal anfordern.
      GSO/GRO ist hier die richtige Antwort.
      sendmmsg/recvmmsg sind bereits sehr alte Technik, und unter Kernel-Entwicklern gibt es inzwischen sogar Leute, die sie am liebsten wieder loswerden würden.
      (Zugehörige GitHub-Diskussion)