18 puntos por GN⁺ 2025-12-23 | 1 comentarios | Compartir por WhatsApp
  • Al depurar problemas de latencia en sistemas distribuidos, lo primero que hay que revisar es la configuración de TCP_NODELAY
  • El algoritmo de Nagle fue propuesto en 1984 en el RFC896 y fue diseñado para reducir la sobrecarga de los encabezados TCP al enviar paquetes pequeños
  • Sin embargo, cuando se combina con el mecanismo de delayed ACK, la transmisión de datos se retrasa hasta recibir el ACK, lo que empeora el rendimiento de las aplicaciones sensibles a la latencia
  • En los centros de datos modernos, el RTT es muy corto y la mayoría de los sistemas ya envían mensajes grandes, por lo que las ventajas del algoritmo de Nagle prácticamente han desaparecido
  • Por eso, en los sistemas distribuidos modernos, TCP_NODELAY debería estar activado por defecto, y el algoritmo de Nagle ya no es necesario

Antecedentes del algoritmo de Nagle

  • El RFC896 de John Nagle de 1984 se propuso para resolver el problema de una sobrecarga del 4000%: 40 bytes de encabezado para 1 byte de datos, como ocurría con transmisiones pequeñas tipo entrada por teclado
    • En ese momento, el problema era que se enviaban paquetes pequeños cada vez que el usuario escribía un carácter, lo que reducía la eficiencia de la red
    • La solución fue restringir el envío de nuevos segmentos mientras los datos anteriores no hubieran sido confirmados con ACK
  • Ese enfoque era efectivo en las condiciones de red de la época, pero no encaja bien con los sistemas modernos donde la latencia es crítica

Interacción entre el algoritmo de Nagle y Delayed ACK

  • Delayed ACK (RFC813, RFC1122) es un mecanismo en el que el receptor no envía el ACK de inmediato, sino que lo retrasa hasta que haya datos de respuesta o expire un temporizador
  • El algoritmo de Nagle detiene el envío mientras espera el ACK, y delayed ACK retrasa ese ACK, por lo que ambos lados quedan esperando entre sí en un estado de bloqueo
  • El propio John Nagle describió esta combinación como una “combinación terrible”, señalando que ambas funciones se introdujeron de forma independiente, pero que juntas provocan latencia

Problemas en el entorno moderno

  • Dentro de un centro de datos, el RTT ronda los 500μs, e incluso dentro de la misma región suele estar en el orden de unos pocos milisegundos
  • En este entorno, retrasar una transmisión por un RTT completo se traduce en pérdida de rendimiento
  • Además, los sistemas distribuidos modernos ya envían mensajes suficientemente grandes debido a TLS, serialización y sobrecarga del protocolo, por lo que el problema de los paquetes de un solo byte casi no existe
  • La optimización de mensajes pequeños ahora se resuelve en la capa de aplicación

Por qué hace falta TCP_NODELAY

  • En los sistemas distribuidos sensibles a la latencia, se recomienda activar TCP_NODELAY para desactivar el algoritmo de Nagle
    • No se trata de algo “ineficiente” ni de una “mala configuración”, sino de una decisión acorde con el hardware moderno y las características actuales del tráfico
  • El autor sostiene que TCP_NODELAY debería ser el valor por defecto
    • Parte del código que “envía en cada llamada a write()” puede volverse más lento, pero ese tipo de código debería corregirse de raíz

Otras opciones relacionadas

  • La opción TCP_QUICKACK reduce el retraso del ACK, pero no es una solución de fondo debido a problemas de portabilidad y a su comportamiento inconsistente
  • El problema central es que el kernel retiene los datos más tiempo del que la aplicación pretende, y los datos deberían enviarse de inmediato cuando se llama a write()

Conclusión

  • El algoritmo de Nagle fue una gran invención para mejorar la eficiencia de red en el pasado, pero
    en el entorno moderno de redes de alta velocidad y sistemas distribuidos se ha convertido en una función anticuada que más bien introduce latencia
  • Por lo tanto, mantener TCP_NODELAY siempre activado se plantea como un principio básico del diseño de sistemas modernos

1 comentarios

 
GN⁺ 2025-12-23
Comentarios de Hacker News
  • Explica el contexto en el que se creó el algoritmo de Nagle en la época de las redes multipunto
    En ese entonces varios hosts compartían un mismo canal Ethernet, así que se usaba CSMA/CD para evitar colisiones
    Pero hoy en día la mayoría de Ethernet es punto a punto, en un entorno full dúplex donde se puede enviar y recibir al mismo tiempo
    Por eso CSMA ya no es necesario, y considera que en la mayoría de los casos tiene sentido configurar TCP_NODELAY para desactivar el algoritmo de Nagle
    • Tiene curiosidad por saber si la motivación relacionada con CSMA realmente estuvo presente en el diseño del algoritmo de Nagle, o si solo se mencionó como contexto histórico
    • En realidad, el algoritmo de Nagle solo tenía como objetivo la coalescencia de paquetes
      Cree que haberlo dejado activado por defecto fue uno de los grandes errores en la historia del networking
    • Como referencia, Ethernet usa CSMA/CD y WiFi usa CSMA/CA
      Cuenta que alrededor de 2014, al reemplazar switches de datacenter, tuvo que conservar algunos equipos antiguos porque no soportaban 10Mbit half dúplex
    • Nagle es bastante razonable cuando la aplicación no se preocupa por el tamaño de los paquetes o no es sensible a la latencia
      Ayuda a evitar la generación de paquetes demasiado pequeños
    • Parece haber una confusión de capas
      Nagle es una optimización en la capa TCP, cuya función es agrupar paquetes pequeños para mejorar la eficiencia
      CSMA es un problema de la capa física/enlace de datos, y es independiente de Nagle
  • Encontró este artículo mientras depuraba un problema de latencia de red durante el desarrollo de un juego
    El backend escrito en Go ya tenía TCP_NODELAY activado por defecto, así que no era la causa, pero le pareció interesante la parte sobre cómo se percibe el problema de Nagle
    También hubo una discusión anterior; se puede consultar este hilo
    • También recomienda un buen artículo de Julia Evans
      En comunicaciones tipo chat como el protocolo DICOM, configurar TCP_NODELAY=1 mejora mucho el rendimiento
    • Le da curiosidad saber qué juego está desarrollando. A él también le gusta desarrollar juegos con Ebitengine y Golang
  • El propio Nagle dijo hace unos 10 años que el verdadero problema es el delayed ACK
    Se puede ver en este enlace relacionado
    Piensa que en las cargas modernas delayed ACK no aporta grandes beneficios
    En el entorno moderno centrado en HTTP, considera mejor desactivar tanto Nagle como delayed ACK
    • El texto original también trata ese punto
      Como el RTT entre datacenters es de cientos de microsegundos, retrasarlo siquiera un RTT puede ser contraproducente
  • En polaco, “nagle” significa “de repente”, y le sorprendió lo bien que encaja con el nombre del algoritmo
    • Parece otro caso de determinismo nominativo
      Enlace de Wikipedia
    • Curiosamente, cuando “NODELAY on” envía de repente, y cuando está “off” manda todo junto, así que el sentido de la palabra parece abarcar ambas configuraciones
    • En realidad, es un algoritmo basado en el RFC 896 escrito por John Nagle
  • Le parece raro que el algoritmo de Nagle esté configurado como valor predeterminado del kernel
    La aplicación debería decidir cuándo enviar y cuándo hacer buffering
  • Le sorprendió que el artículo no mencionara MSG_MORE
    En Linux sirve como pista para indicarle al kernel que pronto se enviarán más datos, y es útil cuando se mandan encabezados y datos por separado
    Con io_uring resulta todavía más eficiente
    • De hecho, también se pueden enviar varios fragmentos de datos sin copia en una sola llamada al sistema
  • Cree que el problema del algoritmo de Nagle es que la API de sockets no tiene una función de envío inmediato (flush)
    Sería bueno poder vaciar el búfer después de un mensaje que requiere respuesta inmediata
    Hoy en día los canales TCP mezclan mensajes síncronos y asíncronos, así que es más complejo
    Ojalá protocolos como SCTP se hubieran adoptado más
    • Coincide en que la API de streams no tiene función de flush. Le parece una omisión de diseño evidente
    • Entiende la filosofía UNIX de tratar la E/S de red como si fuera archivo, pero si hubiera existido desde el inicio una API orientada a mensajes, estos problemas serían menores
      Incluso con wrappers como TLS, encontrar los límites de los mensajes resulta engorroso
    • Tal vez poner MSG_MORE en cada send y quitarlo solo al final produciría indirectamente un efecto de flush
    • La API de streams es incómoda en muchos sentidos
      Idealmente debería poder activarse un bit de “buffering permitido” para dividir una transmisión grande, y al final indicar “enviar inmediatamente”
      TCP_CORK es una alternativa más o menos parecida, pero algo tosca
      La E/S de archivos también sufre problemas similares
    • Tiene curiosidad por saber qué es TCP_CORK
  • La discusión anterior de (2024) estuvo en este enlace
  • Este tema se trata en un episodio del pódcast Oxide and Friends
    Es bastante interesante
    • Oxide es una empresa que rediseña desde cero el SO de servidores y el hardware, así que revisar protocolos tradicionales va muy de acuerdo con su filosofía de marca
  • El algoritmo de Nagle se siente raro porque parece política metida en el kernel
    La aplicación debería poder ajustar directamente el equilibrio entre latencia y rendimiento
    • Sin delayed ack es un algoritmo razonable, y existe como parte del stack TCP porque se intentó resolver el problema en esa capa
      Pero implementarlo a nivel de aplicación sería ineficiente porque habría que conocer los unacked data
    • En teoría es cierto, pero en la práctica la mayoría del código en espacio de usuario no presta atención a los detalles bajos de la red
      Incluso un simple temporizador de flush de 20ms habría sido mucho mejor
    • En realidad TCP_NODELAY se configura por socket, así que lo ve más como una decisión en espacio de usuario tomada por la propia aplicación
    • Como los trade-offs de un programa pueden afectar a otros, piensa que hace falta que el kernel actúe como árbitro desde la perspectiva de todo el sistema