Add inline scheduler option for Sockets transport (#24638)

This commit is contained in:
Brennan 2020-08-17 22:00:15 -07:00 committed by GitHub
parent 512a49c401
commit c92eaa28f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 30 additions and 7 deletions

View File

@ -63,7 +63,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
_trace,
_options.MaxReadBufferSize,
_options.MaxWriteBufferSize,
_options.WaitForDataBeforeAllocatingBuffer);
_options.WaitForDataBeforeAllocatingBuffer,
_options.UnsafePreferInlineScheduling);
socketConnection.Start();
return socketConnection;

View File

@ -37,11 +37,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal
internal SocketConnection(Socket socket,
MemoryPool<byte> memoryPool,
PipeScheduler scheduler,
PipeScheduler transportScheduler,
ISocketsTrace trace,
long? maxReadBufferSize = null,
long? maxWriteBufferSize = null,
bool waitForData = true)
bool waitForData = true,
bool useInlineSchedulers = false)
{
Debug.Assert(socket != null);
Debug.Assert(memoryPool != null);
@ -60,7 +61,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal
// On *nix platforms, Sockets already dispatches to the ThreadPool.
// Yes, the IOQueues are still used for the PipeSchedulers. This is intentional.
// https://github.com/aspnet/KestrelHttpServer/issues/2573
var awaiterScheduler = IsWindows ? scheduler : PipeScheduler.Inline;
var awaiterScheduler = IsWindows ? transportScheduler : PipeScheduler.Inline;
var applicationScheduler = PipeScheduler.ThreadPool;
if (useInlineSchedulers)
{
transportScheduler = PipeScheduler.Inline;
awaiterScheduler = PipeScheduler.Inline;
applicationScheduler = PipeScheduler.Inline;
}
_receiver = new SocketReceiver(_socket, awaiterScheduler);
_sender = new SocketSender(_socket, awaiterScheduler);
@ -68,8 +77,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal
maxReadBufferSize ??= 0;
maxWriteBufferSize ??= 0;
var inputOptions = new PipeOptions(MemoryPool, PipeScheduler.ThreadPool, scheduler, maxReadBufferSize.Value, maxReadBufferSize.Value / 2, useSynchronizationContext: false);
var outputOptions = new PipeOptions(MemoryPool, scheduler, PipeScheduler.ThreadPool, maxWriteBufferSize.Value, maxWriteBufferSize.Value / 2, useSynchronizationContext: false);
var inputOptions = new PipeOptions(MemoryPool, applicationScheduler, transportScheduler, maxReadBufferSize.Value, maxReadBufferSize.Value / 2, useSynchronizationContext: false);
var outputOptions = new PipeOptions(MemoryPool, transportScheduler, applicationScheduler, maxWriteBufferSize.Value, maxWriteBufferSize.Value / 2, useSynchronizationContext: false);
var pair = DuplexPipe.CreateConnectionPair(inputOptions, outputOptions);

View File

@ -124,7 +124,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
}
var connection = new SocketConnection(acceptSocket, _memoryPool, _schedulers[_schedulerIndex], _trace,
_options.MaxReadBufferSize, _options.MaxWriteBufferSize, _options.WaitForDataBeforeAllocatingBuffer);
_options.MaxReadBufferSize, _options.MaxWriteBufferSize, _options.WaitForDataBeforeAllocatingBuffer,
_options.UnsafePreferInlineScheduling);
connection.Start();

View File

@ -44,6 +44,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets
public long? MaxWriteBufferSize { get; set; } = 64 * 1024;
/// <summary>
/// Inline application and transport continuations instead of dispatching to the threadpool.
/// </summary>
/// <remarks>
/// This will run application code on the IO thread which is why this is unsafe.
/// It is recommended to set the DOTNET_SYSTEM_NET_SOCKETS_INLINE_COMPLETIONS environment variable to '1' when using this setting to also inline the completions
/// at the runtime layer as well.
/// This setting can make performance worse if there is expensive work that will end up holding onto the IO thread for longer than needed.
/// Test to make sure this setting helps performance.
/// </remarks>
public bool UnsafePreferInlineScheduling { get; set; }
internal Func<MemoryPool<byte>> MemoryPoolFactory { get; set; } = System.Buffers.SlabMemoryPoolFactory.Create;
}
}