From a9ff368ac7244009b7a961388fe95372fbd7179c Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 23 Aug 2017 13:11:34 -0700 Subject: QNativeSocketEngine: reduce memory usage in nativePendingDatagramSize() The Windows implementation had the right idea: by using a chunked read, we can tell the OS to reuse the same buffer over and over, so we don't need to grow a buffer to the size of the datagram when peeking. This commit implements that strategy on Unix and changes both implementations to start at 1500 bytes instead of 8192 (1500 is more than enough for almost all datagrams we're going to receive). Let's also not use a static buffer, but a stack-based one. No need to dedicate 1500 (or, worse, 8192) bytes for something that is only seldom called. Change-Id: I320d9d2f42284a69a4cbfffd14dd92a6775bf28b Reviewed-by: Edward Welbourne --- src/network/socket/qnativesocketengine_unix.cpp | 30 ++++++++++++++++++++----- src/network/socket/qnativesocketengine_win.cpp | 8 +++---- 2 files changed, 29 insertions(+), 9 deletions(-) (limited to 'src/network/socket') diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index a8f756dc31..bf94f6f606 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -868,22 +868,42 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const if (recvResult != -1) recvResult = value; #else - QVarLengthArray udpMessagePeekBuffer(8192); + // We need to grow the buffer to fit the entire datagram. + // We start at 1500 bytes (the MTU for Ethernet V2), which should catch + // almost all uses (effective MTU for UDP under IPv4 is 1468), except + // for localhost datagrams and those reassembled by the IP layer. + char udpMessagePeekBuffer[1500]; + struct msghdr msg; + struct iovec vec; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + vec.iov_base = udpMessagePeekBuffer; + vec.iov_len = sizeof(udpMessagePeekBuffer); for (;;) { // the data written to udpMessagePeekBuffer is discarded, so // this function is still reentrant although it might not look // so. - recvResult = ::recv(socketDescriptor, udpMessagePeekBuffer.data(), - udpMessagePeekBuffer.size(), MSG_PEEK); + recvResult = ::recvmsg(socketDescriptor, &msg, MSG_PEEK); if (recvResult == -1 && errno == EINTR) continue; - if (recvResult != (ssize_t) udpMessagePeekBuffer.size()) + // was the result truncated? + if ((msg.msg_flags & MSG_TRUNC) == 0) break; - udpMessagePeekBuffer.resize(udpMessagePeekBuffer.size() * 2); + // grow by 16 times + msg.msg_iovlen *= 16; + if (msg.msg_iov != &vec) + delete[] msg.msg_iov; + msg.msg_iov = new struct iovec[msg.msg_iovlen]; + std::fill_n(msg.msg_iov, msg.msg_iovlen, vec); } + + if (msg.msg_iov != &vec) + delete[] msg.msg_iov; #endif #if defined (QNATIVESOCKETENGINE_DEBUG) diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index e21bf29d4b..7556bb98f4 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -1146,10 +1146,10 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const DWORD bufferCount = 5; WSABUF * buf = 0; for (;;) { - // the data written to udpMessagePeekBuffer is discarded, so - // this function is still reentrant although it might not look - // so. - static char udpMessagePeekBuffer[8192]; + // We start at 1500 bytes (the MTU for Ethernet V2), which should catch + // almost all uses (effective MTU for UDP under IPv4 is 1468), except + // for localhost datagrams and those reassembled by the IP layer. + char udpMessagePeekBuffer[1500]; buf = new WSABUF[bufferCount]; for (DWORD i=0; i