From dd71863f6f0f762dbe3806b77596a868ffdc51c9 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 25 May 2020 18:00:57 +0100 Subject: [PATCH 1/5] If not waiting for data, allocate memory before flush --- .../src/Internal/SocketConnection.cs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs b/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs index 4088dab97871..1bb2f832245a 100644 --- a/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs +++ b/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs @@ -187,17 +187,24 @@ private async Task ProcessReceives() { // Resolve `input` PipeWriter via the IDuplexPipe interface prior to loop start for performance. var input = Input; + + Memory buffer = default; + if (!_waitForData) + { + // Ensure we have some reasonable amount of buffer space to start. + buffer = input.GetMemory(MinAllocBufferSize); + } + while (true) { if (_waitForData) { // Wait for data before allocating a buffer. await _receiver.WaitForDataAsync(); + // Data ready, allocate some memory to receive it. + buffer = input.GetMemory(MinAllocBufferSize); } - // Ensure we have some reasonable amount of buffer space - var buffer = input.GetMemory(MinAllocBufferSize); - var bytesReceived = await _receiver.ReceiveAsync(buffer); if (bytesReceived == 0) @@ -209,6 +216,13 @@ private async Task ProcessReceives() input.Advance(bytesReceived); + if (!_waitForData) + { + // If not waiting for data allocate another buffer before flushing, + // to reduce reader/writer contention on MemoryPool. + buffer = input.GetMemory(MinAllocBufferSize); + } + var flushTask = input.FlushAsync(); var paused = !flushTask.IsCompleted; From ed40eac211581847830c28dbd54b24c9f61a0bce Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 26 May 2020 16:59:55 +0100 Subject: [PATCH 2/5] Flush invalidates the memory --- .../Transport.Sockets/src/Internal/SocketConnection.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs b/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs index 1bb2f832245a..d14d8a0fa221 100644 --- a/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs +++ b/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs @@ -216,15 +216,14 @@ private async Task ProcessReceives() input.Advance(bytesReceived); + var flushTask = input.FlushAsync(); if (!_waitForData) { - // If not waiting for data allocate another buffer before flushing, - // to reduce reader/writer contention on MemoryPool. + // If not waiting for data allocate another buffer immediately after flushing, + // to try and aquire some before the reader takes the lock. buffer = input.GetMemory(MinAllocBufferSize); } - var flushTask = input.FlushAsync(); - var paused = !flushTask.IsCompleted; if (paused) From 9d98835570fbc4e22d493b775dae9bb58e6403b6 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 27 May 2020 07:36:15 +0100 Subject: [PATCH 3/5] Half size of min "no wait" buffer --- .../Transport.Sockets/src/Internal/SocketConnection.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs b/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs index d14d8a0fa221..82c829f1a320 100644 --- a/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs +++ b/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs @@ -18,6 +18,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal internal sealed class SocketConnection : TransportConnection { private static readonly int MinAllocBufferSize = SlabMemoryPool.BlockSize / 2; + private static readonly int MinAllocNoWaitBufferSize = MinAllocBufferSize / 2; private static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); private static readonly bool IsMacOS = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); @@ -192,7 +193,7 @@ private async Task ProcessReceives() if (!_waitForData) { // Ensure we have some reasonable amount of buffer space to start. - buffer = input.GetMemory(MinAllocBufferSize); + buffer = input.GetMemory(MinAllocNoWaitBufferSize); } while (true) @@ -221,7 +222,7 @@ private async Task ProcessReceives() { // If not waiting for data allocate another buffer immediately after flushing, // to try and aquire some before the reader takes the lock. - buffer = input.GetMemory(MinAllocBufferSize); + buffer = input.GetMemory(MinAllocNoWaitBufferSize); } var paused = !flushTask.IsCompleted; From 1fbde965706f2d8b998a5e5844b29190745083c1 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 27 May 2020 18:16:17 +0100 Subject: [PATCH 4/5] Revert buffer size change --- .../Transport.Sockets/src/Internal/SocketConnection.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs b/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs index 82c829f1a320..d14d8a0fa221 100644 --- a/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs +++ b/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs @@ -18,7 +18,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal internal sealed class SocketConnection : TransportConnection { private static readonly int MinAllocBufferSize = SlabMemoryPool.BlockSize / 2; - private static readonly int MinAllocNoWaitBufferSize = MinAllocBufferSize / 2; private static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); private static readonly bool IsMacOS = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); @@ -193,7 +192,7 @@ private async Task ProcessReceives() if (!_waitForData) { // Ensure we have some reasonable amount of buffer space to start. - buffer = input.GetMemory(MinAllocNoWaitBufferSize); + buffer = input.GetMemory(MinAllocBufferSize); } while (true) @@ -222,7 +221,7 @@ private async Task ProcessReceives() { // If not waiting for data allocate another buffer immediately after flushing, // to try and aquire some before the reader takes the lock. - buffer = input.GetMemory(MinAllocNoWaitBufferSize); + buffer = input.GetMemory(MinAllocBufferSize); } var paused = !flushTask.IsCompleted; From e864e2bf325242b2eb90bdb1fb2071b3171e4bc3 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 28 May 2020 16:40:48 +0100 Subject: [PATCH 5/5] Use isMoreData FlushAsync overload --- .../src/Internal/SocketConnection.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs b/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs index d14d8a0fa221..e145d87cec9e 100644 --- a/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs +++ b/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs @@ -216,12 +216,17 @@ private async Task ProcessReceives() input.Advance(bytesReceived); - var flushTask = input.FlushAsync(); + var flushTask = input.FlushAsync(isMoreData: !_waitForData); if (!_waitForData) { - // If not waiting for data allocate another buffer immediately after flushing, - // to try and aquire some before the reader takes the lock. - buffer = input.GetMemory(MinAllocBufferSize); + if (buffer.Length - bytesReceived >= MinAllocBufferSize) + { + buffer = buffer.Slice(bytesReceived); + } + else + { + buffer = input.GetMemory(MinAllocBufferSize); + } } var paused = !flushTask.IsCompleted;