Le problème, c’est toujours TCP_NODELAY
(brooker.co.za)- Lors du débogage des problèmes de latence dans les systèmes distribués, la première chose à vérifier est toujours le paramètre TCP_NODELAY
- L’algorithme de Nagle est une approche proposée en 1984 dans la RFC896, conçue pour réduire la surcharge des en-têtes TCP lors de l’envoi de petits paquets
- Cependant, lorsqu’il est combiné au mécanisme de delayed ACK, l’envoi des données est retardé jusqu’à la réception de l’ACK, ce qui dégrade les performances des applications sensibles à la latence
- Dans les environnements modernes de datacenter, le RTT est très court et la plupart des systèmes envoient déjà de gros messages, si bien que les bénéfices de l’algorithme de Nagle ont presque disparu
- Par conséquent, dans les systèmes distribués modernes, TCP_NODELAY devrait être activé par défaut, et l’algorithme de Nagle n’est plus vraiment nécessaire
Contexte de l’algorithme de Nagle
- En 1984, la RFC896 de John Nagle a été proposée pour résoudre le problème d’une surcharge de 4000 % avec 40 octets d’en-tête pour 1 octet de données, typique des petits envois comme la saisie au clavier
- À l’époque, le problème venait du fait que de petits paquets étaient envoyés à chaque caractère tapé, ce qui réduisait l’efficacité réseau
- La solution consistait à interdire l’envoi d’un nouveau segment tant que les données précédentes n’avaient pas été acquittées
- Cette approche était efficace dans les conditions réseau de l’époque, mais elle est inadaptée aux systèmes modernes où la latence est cruciale
Interaction entre l’algorithme de Nagle et le delayed ACK
- Le delayed ACK (RFC813, RFC1122) est un mécanisme où le récepteur n’envoie pas immédiatement un ACK, mais le retarde jusqu’à ce qu’il ait des données de réponse ou qu’un timer expire
- L’algorithme de Nagle suspend l’envoi en attendant un ACK, tandis que le delayed ACK retarde cet ACK, ce qui crée une situation de blocage où les deux côtés s’attendent mutuellement
- John Nagle lui-même a qualifié cette combinaison de « combinaison horrible », soulignant que ces deux mécanismes ont été introduits indépendamment mais provoquent de la latence lorsqu’ils sont utilisés ensemble
Les problèmes dans l’environnement moderne
- Dans un datacenter, le RTT est d’environ 500 μs, et même dans une même région on reste à quelques millisecondes seulement
- Dans un tel contexte, retarder l’envoi d’un RTT complet entraîne une perte de performance
- De plus, les systèmes distribués modernes envoient déjà des messages suffisamment volumineux en raison de TLS, de la sérialisation et de la surcharge des protocoles, si bien que le problème des paquets d’un seul octet a quasiment disparu
- L’optimisation des petits messages est désormais traitée au niveau applicatif
Pourquoi TCP_NODELAY est nécessaire
- Dans les systèmes distribués sensibles à la latence, il est recommandé d’activer TCP_NODELAY afin de désactiver l’algorithme de Nagle
- Ce n’est ni « inefficace » ni un « mauvais réglage », mais un choix adapté aux caractéristiques du matériel moderne et du trafic actuel
- L’auteur soutient que TCP_NODELAY devrait être la valeur par défaut
- Certains codes qui « envoient à chaque appel à
write()» peuvent devenir plus lents, mais ce type de code devrait être corrigé à la racine
- Certains codes qui « envoient à chaque appel à
Autres options liées
- L’option TCP_QUICKACK réduit le délai des ACK, mais les problèmes de portabilité et le comportement incohérent en font une solution non fondamentale
- Le vrai problème est que le noyau conserve les données plus longtemps que le moment voulu par l’application, alors qu’elles devraient être envoyées immédiatement lors de l’appel à
write()
Conclusion
- L’algorithme de Nagle a été une excellente invention pour améliorer l’efficacité réseau par le passé, mais
dans les réseaux rapides modernes et les environnements de systèmes distribués, il est devenu une fonctionnalité d’un autre âge qui ajoute de la latence - Par conséquent, activer systématiquement TCP_NODELAY est présenté comme un principe de base de la conception des systèmes modernes
1 commentaires
Commentaires sur Hacker News
À l’époque, plusieurs hôtes partageaient un même canal Ethernet, donc on utilisait le CSMA/CD pour éviter les collisions
Mais aujourd’hui, la plupart des réseaux Ethernet sont en point à point, dans un environnement full duplex où l’envoi et la réception peuvent se faire simultanément
Le CSMA n’est donc plus nécessaire, et il semble raisonnable dans la plupart des cas d’activer TCP_NODELAY pour désactiver l’algorithme de Nagle
Le fait qu’il ait été activé par défaut est à mon avis l’une des grandes erreurs de l’histoire du réseau
Vers 2014, lors d’un remplacement de commutateurs en datacenter, j’ai dû conserver certains anciens équipements parce qu’ils ne prenaient pas en charge le 10 Mbit half duplex
Cela évite de générer trop de minuscules paquets
Nagle est une optimisation au niveau TCP, qui améliore l’efficacité en regroupant les petits paquets
Le CSMA est un problème de couche physique / liaison de données, distinct de Nagle
Le backend écrit en Go active TCP_NODELAY par défaut, donc ce n’était pas la cause, mais le point sur la perception du problème posé par Nagle m’a semblé intéressant
Il y avait aussi une discussion précédente ; voir ce fil
Dans des protocoles de type bavard comme DICOM, configurer TCP_NODELAY=1 améliore nettement le débit
Voir ce lien
Je pense que le delayed ACK n’apporte plus grand-chose pour les charges modernes
Dans l’environnement actuel centré sur HTTP, il vaut mieux à mon avis désactiver à la fois Nagle et delayed ACK
Quand le RTT entre datacenters se compte en centaines de microsecondes, retarder ne serait-ce qu’un RTT peut au contraire être pénalisant
Lien Wikipédia
Ce devrait être à l’application de décider quand envoyer et quand mettre en tampon
Sous Linux, c’est un indice envoyé au noyau pour signaler que d’autres données vont suivre bientôt, utile quand on envoie séparément les en-têtes et les données
Avec io_uring, c’est encore plus efficace
Il serait utile de pouvoir vider le tampon juste après un message qui nécessite une réponse immédiate
Les canaux TCP actuels mélangent souvent messages synchrones et asynchrones, ce qui complique encore les choses
J’aimerais que des protocoles comme SCTP soient plus largement utilisés
Même avec un encapsulage comme TLS, retrouver les frontières des messages est pénible
Dans l’idéal, on devrait pouvoir définir un bit “mise en tampon autorisée” pour découper un gros envoi, puis demander “envoi immédiat” à la fin
TCP_CORK est une alternative à peu près comparable, mais assez grossière
Les E/S fichier souffrent aussi d’un problème similaire
C’est assez intéressant
L’application devrait pouvoir ajuster elle-même l’équilibre entre latence et débit
Mais l’implémenter au niveau applicatif serait inefficace, car il faudrait connaître les données non acquittées
Un simple temporisateur de flush de 20 ms aurait déjà été bien meilleur