Subject: [PATCH] Add support for TCP_NODELAY on network streams

From: Eric Marsden <[email-masked]>
Date: Wed, 11 Dec 2024 17:17:38 +0100

Hi,

The attached patch adds support for the socket option TCP_NODELAY,
to disable Nagle's algorithm. This means that network segments are
sent as soon as possible, even when they contain little data. This
reduces network latency on the network connection, but can lead to
many small packets being sent.

I have only tested on Linux/AMD64, but support for this socket option
is very widespread. Patch reformatted following feedback from Robert
Pluim and Eli Zaretskii.

My motivation for adding this support is that Emacs is very slow
when connecting to the PostgreSQL database over the network, using
my pg-el library, and is unable to saturate CPU when sending a
stream of messages. It is much faster when using a local Unix
connection and saturates CPU.

With this patch, a test case that takes 911 seconds goes down to
76 seconds (x 12 speedup).

Eric
From 188af9153f230ad08c5682f55af72879c4358276 Mon Sep 17 00:00:00 2001
From: Eric Marsden <[email-masked]>
Date: Wed, 11 Dec 2024 16:59:45 +0100
Subject: [PATCH] Add support for TCP_NODELAY on network streams

* src/process.c (socket_options): add entry for TCP_NODELAY.
* lisp/emacs-lisp/bytecomp.el: add :nodelay to valid keywords
  for make-network-process compiler-macro.
* doc/lispref/processes.texi: document :nodelay keyword argument
  to set-network-process-option and make-network-process.
---
 doc/lispref/processes.texi  | 7 +++++++
 lisp/emacs-lisp/bytecomp.el | 4 ++--
 src/process.c               | 5 +++++
 3 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/doc/lispref/processes.texi b/doc/lispref/processes.texi
index 79ef959ae65..e0d71ac22b6 100644
--- a/doc/lispref/processes.texi
+++ b/doc/lispref/processes.texi
@@ -3090,6 +3090,13 @@ Network Options
 may be a period of time after the last use of that port (by any
 process on the host) where it is not possible to make a new server on
 that port.
+
+@item :nodelay @var{nodelay-flag}
+If @var{nodelay-flag} is non-@code{nil}, the @code{TCP_NODELAY} option
+is enabled on the socket.  This disables the Nagle algorithm, meaning
+that network segments are sent as soon as possible, even when they
+contain little data.  This reduces network latency on the network
+connection, but can lead to many small packets being sent.
 @end table
 
 @defun set-network-process-option process option value &optional no-error
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index f058fc48cc7..07eb4690fce 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -6049,8 +6049,8 @@ bytecomp--check-keyword-args
             :buffer :host :service :type :family :local :remote :coding
             :nowait :noquery :stop :filter :filter-multibyte :sentinel
             :log :plist :tls-parameters :server :broadcast :dontroute
-            :keepalive :linger :oobinline :priority :reuseaddr :bindtodevice
-            :use-external-socket)
+            :keepalive :linger :oobinline :priority :reuseaddr :nodelay
+            :bindtodevice :use-external-socket)
           '(:name :service))))
 
 (provide 'byte-compile)
diff --git a/src/process.c b/src/process.c
index b71ba3daf2d..cd1378f07ad 100644
--- a/src/process.c
+++ b/src/process.c
@@ -38,6 +38,7 @@ Copyright (C) 1985-1988, 1993-1996, 1998-1999, 2001-2024 Free Software
 #include <sys/socket.h>
 #include <netdb.h>
 #include <netinet/in.h>
+#include <netinet/tcp.h>
 #include <arpa/inet.h>
 
 #else
@@ -2860,6 +2861,9 @@ DEFUN ("set-process-datagram-address", Fset_process_datagram_address, Sset_proce
 #endif
 #ifdef SO_REUSEADDR
     { ":reuseaddr", SOL_SOCKET, SO_REUSEADDR, SOPT_BOOL, OPIX_REUSEADDR },
+#endif
+#ifdef TCP_NODELAY
+    { ":nodelay", IPPROTO_TCP, TCP_NODELAY, SOPT_BOOL, OPIX_MISC },
 #endif
     { 0, 0, 0, SOPT_UNKNOWN, OPIX_NONE }
   };
@@ -3899,6 +3903,7 @@ DEFUN ("make-network-process", Fmake_network_process, Smake_network_process,
 :broadcast BOOL    -- Allow send and receive of datagram broadcasts.
 :dontroute BOOL    -- Only send to directly connected hosts.
 :keepalive BOOL    -- Send keep-alive messages on network stream.
+:nodelay BOOL      -- Set TCP_NODELAY on the network socket.
 :linger BOOL or TIMEOUT -- Send queued messages before closing.
 :oobinline BOOL    -- Place out-of-band data in receive data stream.
 :priority INT      -- Set protocol defined priority for sent packets.
-- 
2.45.2