Delayed ACK and TCP Performance
uIP and NuttX
The heart of the NuttX IP stack derived from Adam Dunkel's tiny uIP stack back at version 1.0.
The NuttX TCP/IP stack contains the uIP TCP state machine and some uIP "ways of doing things," but otherwise, there is now little in common between these two designs.
NOTE: uIP is also built into Adam Dunkel's Contiki operating system.
uIP, Delayed ACKs, and Split Packets
In uIP, TCP packets are sent and ACK'ed one at a time.
That is, after one TCP packet is sent, the next packet cannot be sent until the previous packet has been ACKed by the receiving side.
The TCP protocol, of course, supports sending multiple packets which can be ACKed be the receiving time asynchronously.
This one-packet-at-a-time logic is a simplification in the uIP design; because of this, uIP needs only a single packet buffer any you can use uIP in even the tiniest environments.
This is a good thing for the objectives of uIP.
...
uIP has an option to work around this:
It has logic that can be enable to split each packet into half, sending half as much data in each packet.
Sending more, smaller packets does not sound like a performance improvement.
This tricks the recipient that follows RFC 1122 into receiving the two, smaller back-to-back packets and ACKing the second immediately.
References: uip-split.c
and uip-split.h
.
The NuttX TCP/IP Stack and Delay ACKs
The NuttX, low-level TCP/IP stack does not have the limitations of the uIP TCP/IP stack.
It can send numerous TCP/IP packets regardless of whether they have been ACKed or not.
That is because in NuttX, the accounting for which packets have been ACKed and which have not has been moved to a higher level in the architecture.
...
So the NuttX approach is similar to the uIP way of doing things, but does add one more buffer, the user provided buffer to send()
, that can be used to improve TCP/IP performance (of course, this user provided buffer is also required by in order to be compliant with send()"" specification
.
The NuttX Split Packet Configuration
But what happens if all of the user buffer is smaller than the MSS of one TCP packet?
Suppose the MTU is 1500 and the user I/O buffer is only 512 bytes?
In this case, send()
performance degenerates to the same behavior as uIP:
An ACK is required for each packet before send()
can return and before send()
can be called again to send the next packet.
...
NOTE: NuttX is not an RFC 1122 recipient; NuttX will ACK every TCP/IP packet that it receives.
Write Buffering
The best technical solution to the delayed ACK problem would be to support write buffering.
Write buffering is enabled with CONFIG_NET_TCP_WRITE_BUFFERS
. If this option is selected, the NuttX networking layer will pre-allocate several write buffers at system intialization time. The sending a buffer of data then works like this:
...