- recall est un service qui fournit des bots de réunion à des centaines d’entreprises et exploite une infrastructure à grande échelle sur AWS
- pour proposer un service rentable, l’entreprise cherche à tirer le maximum des performances matérielles
- ces dernières années, la disponibilité des GPU chez les fournisseurs cloud ayant été instable, le traitement vidéo a été effectué sur CPU plutôt que sur GPU
- en profilant les bots utilisant Chromium headless, ils ont constaté que la majeure partie du temps CPU n’était pas consommée par le traitement vidéo (encodage/décodage), mais par les fonctions de copie mémoire
__memmove_avx_unaligned_erms et __memcpy_avx_unaligned_erms
memmove et memcpy sont des fonctions de copie de blocs mémoire de la bibliothèque standard C (glibc)
memmove gère quelques cas particuliers liés à la copie de zones mémoire qui se chevauchent, mais les deux peuvent être classées comme des fonctions de « copie mémoire »
- le suffixe
avx_unaligned_erms indique une optimisation pour les systèmes prenant en charge les Advanced Vector Extensions (AVX), y compris pour les accès mémoire non alignés
erms signifie Enhanced REP MOVSB/STOSB, une optimisation des processeurs Intel récents pour accélérer les déplacements mémoire. On peut le comprendre comme « une implémentation plus rapide pour certains processeurs »
- le profilage a montré que ces fonctions étaient le plus souvent appelées par le client WebSocket Python qui recevait les données
- l’implémentation WebSocket de Chromium, qui envoyait les données, arrivait juste après en nombre d’appels
Les problèmes de WebSocket
- un serveur WebSocket local était utilisé pour transmettre les données vidéo brutes depuis l’environnement JS de Chromium vers l’encodeur
- un flux vidéo brut en 1080p à 30 fps exige une bande passante élevée, de plus de 93 MB/s
- l’usage de WebSocket entraînait un coût de calcul important, principalement à cause de la fragmentation et du masquage
- fragmentation : l’implémentation WebSocket de Chromium fragmente les messages de plus de 131 KB en plusieurs frames. Une frame vidéo brute de plus de 3 MB est ainsi découpée en plus de 24 frames distinctes pour être transmise
- masquage : pour des raisons de sécurité, WebSocket masque toutes les frames envoyées du client vers le serveur. Sur de gros volumes de données dépassant 100 MB/s, cela représente un surcoût significatif
Recherche d’alternatives
- avec les API du navigateur, il était difficile de mettre en place quelque chose de bien plus performant que WebSocket, ils ont donc décidé de forker Chromium pour y implémenter une fonctionnalité personnalisée
- trois alternatives ont été envisagées : raw TCP/IP, Unix Domain Socket, Shared Memory
- TCP/IP : cela évite les problèmes de fragmentation/masquage de WebSocket, mais la taille maximale des paquets reste faible, donc le problème de fragmentation subsiste. Il y a aussi le surcoût de copie vers l’espace noyau
- Unix Domain Socket : permet de contourner entièrement la pile réseau, mais nécessite quand même une copie des données entre l’espace utilisateur et l’espace noyau
- Shared Memory : une mémoire accessible simultanément par plusieurs processus. Chromium peut y écrire directement, et l’encodeur peut lire immédiatement, sans copie intermédiaire
Implémentation d’un transport basé sur la mémoire partagée
- les données ont été implémentées sous forme de ring buffer afin de permettre des lectures et écritures continues dans la mémoire partagée
- exigences : lock-free, multi-producteur / mono-consommateur, taille de frame variable, lecture zero-copy, compatibilité sandbox, signalisation à faible latence
- après évaluation de plusieurs implémentations existantes de ring buffer, aucune ne satisfaisait l’ensemble des exigences, ils ont donc décidé de l’implémenter eux-mêmes
- pour prendre en charge la lecture zero-copy, les pointeurs ont été séparés en trois : write, peek et read
- pour garantir la sûreté entre threads, des opérations atomiques ont été utilisées, ainsi que des named semaphores pour signaler l’arrivée de nouvelles données ou la libération d’espace
- grâce à l’implémentation de ce ring buffer en mémoire partagée et à d’autres optimisations, ils ont pu réduire jusqu’à 50 % l’utilisation CPU des bots. Au final, cela a permis d’économiser plus d’un million de dollars par an sur les coûts AWS.
3 commentaires
Avis Hacker News
C’est l’histoire typique d’une startup qui choisit un raccourci « suffisamment bon » puis optimise plus tard.
Certains trouvent surprenante la bande passante très élevée des données vidéo brutes.
Certains estiment que le problème ne vient pas d’AWS, mais du gaspillage de cycles CPU.
Certains soulignent que le MTU et le MSS des réseaux TCP/IP sont petits par rapport à la taille des frames vidéo.
Certains disent qu’avec Mojo de Chromium, il n’y a pas besoin de se soucier du code spécifique à chaque plateforme.
Certains estiment que le problème n’est pas le réseau, mais une mauvaise compréhension des codecs vidéo.
Certains saluent la transparence et disent vouloir aussi de la transparence sur le prix du produit.
Certains expliquent que le masking du protocole WebSocket est une tentative de résoudre les problèmes liés aux intermédiaires.
Certains trouvent étrange d’envoyer des données vidéo sans compression.
Certains se disent surpris par l’approche initiale consistant à envoyer de la vidéo brute via WebSocket.
Dès le départ, le développement a été mal fait…
« Leur approche initiale consistant à transmettre de la vidéo brute via WebSocket est sidérante. » Je partage cet avis.