- Environ 20 % du trafic HTTP de Firefox utilise HTTP/3, qui fonctionne sur QUIC et UDP
- En remplaçant NSPR, l’ancienne couche d’E/S réseau, par quinn-udp basé sur Rust, Firefox renforce à la fois les performances et la sûreté mémoire
- Les optimisations s’appuient activement sur les appels système les plus récents propres à chaque système d’exploitation (multi-message, segmentation offloading, etc.)
- Sur Windows et macOS, certaines fonctions ont été limitées en raison de problèmes de compatibilité ou de pilotes, mais Linux a montré les meilleures performances
- Les différents essais, erreurs et corrections de bugs autour de la prise en charge d’ECN pour QUIC et des E/S UDP selon les plateformes devraient aussi profiter à de futurs projets et à l’écosystème open source
Motivation
- Environ 20 % du trafic HTTP de Firefox utilise HTTP/3, qui fonctionne via QUIC, lui-même implémenté au-dessus de UDP
- Historiquement, Firefox utilisait la bibliothèque NSPR pour les E/S réseau, mais ses fonctions liées aux E/S UDP sont anciennes et limitées (les principales sont
PR_SendTo et PR_RecvFrom)
- Les systèmes d’exploitation offrent désormais des appels système multi-message (par ex.
sendmmsg, recvmmsg) ainsi que des optimisations réseau comme le segmentation offload (GSO, GRO)
- Ces techniques peuvent améliorer fortement les performances des E/S UDP
- Firefox a donc cherché à savoir s’il pouvait bénéficier de ces avancées en remplaçant sa pile UDP historique par des appels système modernes
Vue d’ensemble
- Le projet a démarré à la mi-2024, avec pour objectif de reconstruire la pile QUIC/UDP de Firefox à l’aide d’appels système modernes sur tous les OS pris en charge
- En plus des gains de performance, l’objectif était aussi d’améliorer la sécurité en s’appuyant sur Rust, qui garantit la sûreté mémoire pour les E/S UDP
- QUIC étant déjà implémenté en Rust, le développement s’est appuyé sur la bibliothèque quinn-udp elle aussi écrite en Rust
- Les différences d’appels système entre OS ont compliqué le développement, mais quinn-udp a permis d’accélérer nettement les travaux
- À la mi-2025, le déploiement est en cours pour la majorité des utilisateurs de Firefox, avec des benchmarks montrant une hausse importante des performances, jusqu’à 4 Gbit/s
Structure des E/S UDP et méthodes modernes d’optimisation
Envoi d’un seul datagramme
- L’approche historique utilisait
sendto et recvfrom, avec émission ou réception d’un seul datagramme UDP à la fois
- Le coût des transitions entre espace utilisateur et noyau est payé pour chaque datagramme, ce qui devient inefficace dans les environnements à fort trafic
- Exemple : envoyer des paquets de moins de 1500 octets à plusieurs centaines de Mbit/s par seconde entraîne un surcoût important
Envoi par lots de plusieurs datagrammes
- Linux et certains autres OS prennent en charge des appels système multi-message comme
sendmmsg et recvmmsg
- Cela permet d’envoyer et de recevoir plusieurs datagrammes en une seule fois, ce qui réduit fortement l’overhead
Un seul grand datagramme segmenté
- Des techniques d’offload comme GSO (émission) et GRO (réception) permettent de segmenter automatiquement de gros datagrammes UDP au niveau de l’OS ou de la carte réseau
- L’interface réseau se charge de découper les paquets, de calculer les checksums et d’ajouter les en-têtes
- L’application peut ainsi traiter de nombreux paquets réels avec un seul appel système
- Lorsque GSO est activé, certains outils réseau comme Wireshark analysent moins bien les paquets
Remplacement de NSPR dans Firefox
- La première étape a consisté à remplacer NSPR par quinn-udp dans le modèle d’émission/réception d’un seul datagramme
- Le pipeline de traitement des datagrammes de l’implémentation QUIC a ensuite été refactorisé pour prendre en charge les envois/réceptions par lots ainsi que la segmentation
- Les appels multi-message et les appels liés au segmentation offload sont utilisés selon les situations
- Des traitements d’exception propres à chaque plateforme ainsi que diverses améliorations des E/S ont été ajoutés
Détails par plateforme
Windows
- Windows fournit
WSASendMsg et WSARecvMsg, avec prise en charge des datagrammes de taille MTU classique ou de grands datagrammes segmentés
- Les équivalents Windows de GSO/GRO sous Linux sont USO (émission) et URO (réception)
- Au départ, l’utilisation de simples appels à datagramme unique ne posait pas de problème, mais une fois URO activé, un bug a provoqué l’échec du chargement des pages dans certains environnements (par ex. Windows on ARM + WSL), car la longueur des paquets QUIC ne pouvait plus être déterminée
- USO a aussi été utilisé côté émission, mais des effets indésirables ont été observés dans l’environnement d’installation de Firefox sous Windows, notamment une hausse des pertes de paquets et des crashs de pilotes réseau
- Dans Firefox, URO et USO restent donc désactivés pour l’instant, avec des investigations supplémentaires en cours
macOS
- Sur macOS, quinn-udp a été introduit en remplaçant
sendto/recvfrom par sendmsg/recvmsg
- La fonction de segmentation offload n’y est pas activée
- En revanche, les appels non officiellement documentés
sendmsg_x et recvmsg_x permettent un envoi par lots et ont été intégrés de façon non officielle à quinn-udp
- Comme Apple pourrait supprimer ces appels à l’avenir, ils n’ont pas été activés par défaut : seuls des tests ont été effectués, sans intégration dans la version publiée
Linux
- sendmmsg/recvmmsg ainsi que GSO/GRO sont tous pris en charge, et quinn-udp privilégie par défaut GSO à l’émission
- Firefox utilise un socket UDP distinct par connexion afin de renforcer la confidentialité (distinction par 4-tuple)
- Avec cette architecture, les bénéfices du segmentation offload sont maximisés, tandis que les avantages des envois croisés via sendmmsg/recvmmsg restent limités
- Hormis quelques petits ajustements, comme le sandbox réseau et la vérification à l’exécution de la prise en charge de GSO, l’intégration s’est faite sans difficulté majeure
Android
- Android diffère de Linux dans le traitement des appels système et dans ses filtres de sécurité (par ex. seccomp)
- Il a fallu gérer divers problèmes de compatibilité, notamment la prise en charge de très anciennes plateformes comme Android 5 sur x86, le contournement de
socketcall et certains cas d’erreur
- Dans certains environnements, les appels d’émission avec le bit ECN activé échouaient avec une erreur
EINVAL ; quinn-udp applique donc une stratégie de nouvelle tentative et de désactivation de l’option
- Grâce aux nombreuses améliorations apportées par la communauté Quinn, Firefox bénéficie automatiquement de ces progrès
Prise en charge d’ECN (notification explicite de congestion)
- L’adoption d’appels système modernes a permis de gérer l’envoi et la réception d’ancillary data, rendant possible la prise en charge d’ECN pour QUIC
- Il y a bien eu quelques bugs mineurs, mais dans Firefox Nightly, plus de la moitié des connexions QUIC utilisent déjà la voie sortante ECN
- Avec la montée en puissance de nouvelles technologies comme L4S, l’importance et l’utilité d’ECN augmentent elles aussi
Résumé de la conclusion
- La couche d’E/S UDP de QUIC dans Firefox a été remplacée par une implémentation Rust basée sur quinn-udp, apportant à la fois de meilleures performances et une sécurité accrue
- À la place d’appels système vieillissants, Firefox exploite désormais les appels d’E/S modernes propres à chaque OS, ce qui améliore le débit et permet la prise en charge d’ECN
- Certaines optimisations, notamment sous Windows, nécessitent encore des améliorations en raison de problèmes de compatibilité
- À mesure que l’usage de QUIC continue d’augmenter, la prise en charge au niveau des OS et des pilotes devrait elle aussi continuer à progresser
1 commentaires
Commentaires sur Hacker News
Le point essentiel de l’article est caché au milieu
Les améliorations présentées ici sont certes indispensables pour de très hauts débits réels (100 Gb/s et plus), mais 4 Gb/s n’est en fait pas un débit si élevé.
Cela fait 500 MB/s, ce qui signifie qu’il y a quelque part un goulet d’étranglement sérieusement lent.
On parle de changements de contexte noyau autour de 1 µs, ce qui est en réalité plutôt élevé pour des appels système.
Mais avec une moyenne d’environ 500 octets par paquet, on peut déjà atteindre 500 MB/s, donc 4 Gb/s.
L’ancien débit de 1 Gb/s correspondait à des tailles de paquet plus petites, et pousser des paquets UDP simplement dans le buffer du NIC reste quelque chose qu’on peut largement faire au débit de copie mémoire.
Même si le chiffrement était lent, en pratique ce n’est pas vraiment le cas.
Par exemple, un Intel i5-6500 a déjà atteint 1729 MB/s en AES-128 GCM.
Les CPU actuels peuvent faire 3 à 5 GB/s par cœur, soit 25 à 40 Gb/s, donc les 4 Gb/s mentionnés ici sont vraiment faibles.
(lien de référence sur les performances AES-128 GCM)
Il a été dit que la latence des appels système était élevée, mais cela peut être dû aux mesures d’atténuation contre Spectre et Meltdown.
TCP a une liaison de chemin, contrairement à UDP, donc la configuration du chemin diffère beaucoup.
Dire que le chiffrement est lent est vrai pour les petits PDU (unités de données de protocole).
La plupart des optimisations et benchmarks sont faits à partir de grosses trames TCP, si bien qu’en pratique le coût de mise en état ressort fortement sur les petits paquets.
En microbenchmark dans une tight loop, les chiffres sont bons, mais dans un environnement réel avec de l’aléa, l’efficacité du cache baisse aussi, et sous 1 KB les performances chutent.
À cela s’ajoutent le surcoût de framing, la validation des données hors bande, etc., qui coûtent assez cher.
La mémoire tampon UDP par défaut est aussi insuffisante, ce qui pose beaucoup de problèmes en usage réel.
En exploitant TCP, on a continuellement augmenté les tailles de buffer, alors qu’UDP est resté sur des valeurs conservatrices héritées des années 90-2000.
L’API réellement nécessaire serait : fork du fd, prise en charge complète de
connect(2)et du route binding, puis une file de soumission ensuite (uring,rio, etc.).Du côté du chiffrement, une approche KDF pourrait beaucoup réduire le coût d’état.
L’approche PSP est reconnue par certains fournisseurs, mais largement rejetée par l’IETF et d’autres, donc elle ne s’est pas diffusée.
Dans les tests de très forte concurrence menés par des fournisseurs, elle montre une montée en charge très supérieure aux variantes TLS classiques.
Le type exact de CPU utilisé pour le benchmark n’est absolument pas mentionné.
Et le surcoût de chiffrement vient du coût de traitement propre au protocole QUIC.
QUIC reste en retard sur le déchargement matériel du chiffrement par rapport à TCP, alors que TCP peut déléguer une partie du traitement au NIC via le déchargement kTLS.
J’ai vraiment apprécié ce contenu technique.
J’aimerais que toute la documentation technique de Mozilla soit de ce niveau, avec de la profondeur et rédigée correctement par des ingénieurs de terrain.
Sans optimisme béat, juste quelque chose qui vaut vraiment la lecture.
Je ne comprends pas pourquoi Android 5 est encore pris en charge.
Cela fait plus de 10 ans qu’il est sorti, et les utilisateurs de ces appareils sont du legacy encore plus ancien.
Le Web moderne est tellement lourd qu’il doit déjà être difficile de naviguer correctement sur de tels appareils ; je me demande vraiment pourquoi continuer à les supporter.
J’imagine que cela concerne surtout des hackers qui retapent de vieux appareils type OnePlus, les laissent branchés au chargeur, n’installent même pas une ROM grand public comme LineageOS, et utilisent Firefox via un store alternatif.
En pratique, c’est un coût qui ralentit l’ensemble du développement.
Je recommande aussi le billet du blog de développement de Factorio intitulé « The map download struggle », qui contient une anecdote intéressante sur le même sujet.
(billet de blog lié)
Quiconque a déjà traité de vrais problèmes réseau s’y reconnaîtra encore plus à cause des mystérieux runt packets.
La plupart des équipements réseau gèrent mal ce genre de paquets.
Le trafic basé sur UDP ou QUIC est facilement vulnérable aux attaques dès qu’on n’est pas dans un environnement cloud d’une certaine taille.
Pour cette raison, les petits hébergeurs ou ceux qui opèrent leur propre infrastructure ont de plus en plus de mal à fonctionner, et seuls restent ceux qui savent absorber de gros volumes de trafic.
C’est pourquoi, dans la plupart des environnements LAN, on laisse tomber la majorité du trafic UDP et on ne traite avec rate limiting que ce qui est nécessaire.
Bug tracker de Mozilla
Sur macOS et Fedora, je rencontre toujours le même phénomène en accédant avec Firefox à des sites hébergés par Cloudflare.
J’ai appris cette fois que Windows et macOS disposent eux aussi de fonctions comparables à GSO/GRO (traitement de gros paquets réseau).
Dommage qu’en pratique elles semblent pleines de bugs.
J’ai aussi l’impression que GSO/GRO ne doit pas être le seul composant bogué.
Quelqu’un peut-il expliquer comment UDP GSO/GRO fonctionne structurellement ?
UDP transporte des paquets sans ordre ; quand un paquet QUIC est découpé en plusieurs paquets UDP, il n’y a pas non plus d’information de séquence dans l’en-tête, donc je me demande comment le récepteur reconstitue l’ordre et les regroupe.
Le noyau met plusieurs datagrammes dans une seule structure, tout en conservant les frontières entre eux à chaque niveau (par exemple les fragments de données dans
sk_buff).Je ne suis pas un spécialiste absolu, mais en cherchant comment cela fonctionne, je suis tombé sur cet article.
Ils disent : « Nous avons commencé le développement au-dessus de
quinn-udp, la bibliothèque d’E/S UDP du projet Quinn, ce qui nous a permis d’aller beaucoup plus vite ».Du coup, je me demande s’ils ont financé le projet Quinn.
(lien de soutien à Quinn)
J’ai posé directement la question au sujet du soutien financier, et un Senior Principal Software Engineer de Mozilla m’a répondu : « Mozilla n’a pas d’argent ».
En revanche, ils ont énormément contribué en code, donc nous leur en sommes vraiment reconnaissants.
(Je suis le mainteneur principal de Quinn.)
À la question « Ont-ils fait un don ? », quelqu’un avance que chez Mozilla, on préfère dépenser plusieurs millions de dollars supplémentaires pour le salaire du CEO plutôt que de financer l’open source.
Et cela alors même que leur produit phare, Firefox, est en train de s’effondrer.
Je serais curieux de savoir de quelles autres manières ils ont contribué, en dehors du code.
Il est étonnant de voir
sendmmsg/recvmmsgqualifiés de « récents ».Ce sont en réalité des appels système qui existent depuis assez longtemps.
Je m’attendais aussi à voir
io_uringmentionné dans cet article Linux, mais ce n’est pas le cas.io_uringn’a pas de vraie capacité de batch pour traiter plusieurs datagrammes UDP d’un coup.Au mieux, on peut simplement soumettre plusieurs
sendmsgetrecvmsgen une fois.GSO/GRO est la vraie réponse.
sendmmsg/recvmmsgest déjà une technologie très ancienne, et certains développeurs du noyau aimeraient même désormais s’en débarrasser.(discussion GitHub liée)