From 54e2e2a7ad12363e70bd13557322a2f7dd224841 Mon Sep 17 00:00:00 2001 From: Pieter Hintjens Date: Sun, 1 Nov 2015 12:57:32 +0100 Subject: [PATCH] Problem: Windows 7 TCP slow start See issue #1608. This is an old issue with Windows 7. The effect is that we see a latency ramp on the first 500 messages. * The ramp is unaffected by message size. * Sleeping up to 100msec between sends has no effect except to switch off ZeroMQ batching so making the ramp more visible. * After 500 messages, latency falls back down to ~10-40 usec. * Over inproc:// the ramp happens when we use the signaler class. * Client-server over inproc:// does not show the ramp. * Client-server over tcp:// shows a similar ramp. We know that the signaller is using TCP on Windows. We can 'prime' the connection by doing 500 dummy sends. This potentially causes new sockets to be delayed on creation, which is not a good solution. Note that the signaller sends zero-byte messages. This may also be confusing TCP. Solution: flood the receive buffer when creating a new FD pair; send a 1M buffer and discard it. Fixes #1608 --- src/signaler.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/signaler.cpp b/src/signaler.cpp index 581c4b00..1164255b 100644 --- a/src/signaler.cpp +++ b/src/signaler.cpp @@ -511,6 +511,27 @@ int zmq::signaler_t::make_fdpair (fd_t *r_, fd_t *w_) if (rc != SOCKET_ERROR) *r_ = accept (listener, NULL, NULL); + // Send/receive large chunk to work around TCP slow start + // This code is a workaround for #1608 + if (*r_ != INVALID_SOCKET) { + size_t dummy_size = 1024 * 1024; // 1M to overload default receive buffer + unsigned char *dummy = (unsigned char *) malloc (dummy_size); + int still_to_send = (int) dummy_size; + int still_to_recv = (int) dummy_size; + while (still_to_send || still_to_recv) { + int nbytes; + if (still_to_send > 0) { + nbytes = ::send (*w_, (char *) (dummy + dummy_size - still_to_send), still_to_send, 0); + wsa_assert (nbytes != SOCKET_ERROR); + still_to_send -= nbytes; + } + nbytes = ::recv (*r_, (char *) (dummy + dummy_size - still_to_recv), still_to_recv, 0); + wsa_assert (nbytes != SOCKET_ERROR); + still_to_recv -= nbytes; + } + free (dummy); + } + // Save errno if error occurred in bind/listen/connect/accept. int saved_errno = 0; if (*r_ == INVALID_SOCKET)