This repo is meant for trying out implementations for the async engine that powers .NET Core Socket
implementation on Linux. The .NET Core implementation is in SocketAsyncEngine.Unix.cs and related classes.
To compare implementations they need to be benchmarks, so the repo will expose a Socket
class that provides a subset of System.Net.Sockets.Socket
members. These members need to have the same semantics as those of the real Socket. Having this Socket
type should allow re-using existing benchmarks written against the Socket
class. On top of this Socket
we'll also make an ASP.NET Transport implementation, which will allow to run ASP.NET Core benchmarks (like TechEmpower scenarios).
The async engine implementation should also allow implementing async operations for pipe-type FileStream
.
For accessing native system functions Tmds.LibC is used. This avoid having to include a native shim library.
tkp1n/IoUring is used for io_uring
APIs.
Two async engines implementations are provided. They can be configured an used by setting AsyncEngine.SocketEngine
:
// epoll-based with linux AIO for batching
AsyncEngine.SocketEngine = new EPollAsyncEngine(
threadCount: Environment.ProcessorCount,
useLinuxAio: true);
// io_uring-based
AsyncEngine.SocketEngine = new IOUringAsyncEngine(
threadCount: Environment.ProcessorCount);
To use the Socket
from this package, add these to the top of your code file:
using Socket = Tmds.LinuxAsync.Socket;
using SocketAsyncEventArgs = Tmds.LinuxAsync.SocketAsyncEventArgs;
Additional properties are provided on SocketAsyncEventArgs
:
class SocketAsyncEventArgs
{
public bool RunContinuationsAsynchronously { get; set; } = true;
public bool PreferSynchronousCompletion { get; set; } = true;
}
Setting RunContinuationsAsynchronously
to false
allows SocketAsyncEngine
to invoke callbacks directly from the epoll
/io_uring
thread. This avoid context switching to the ThreadPool
.
This is a copy of ASP.NET Core Transport.Sockets that uses Tmds.LinuxAsync.Socket
instead of System.Net.Sockets.Socket
.
The Transport can be used in ASP.NET Core by calling the UseLinuxAsyncSockets
IWebHostBuilder
extension methods.
public enum OutputScheduler
{
IOQueue,
Inline,
IOThread,
ThreadPool
}
public enum InputScheduler
{
Inline,
ThreadPool
}
public enum SocketContinuationScheduler
{
Inline,
ThreadPool
}
class SocketTransportOptions
{
public bool DispatchContinuations { get; set; } = true; // Sets RunContinuationsAsynchronously
public bool DeferSends { get; set; } = false; // Sets !PreferSynchronousCompletion for sends
public bool DeferReceives { get; set; } = false; // Sets !PreferSynchronousCompletion for receives
public OutputScheduler OutputScheduler { get; set; } = OutputScheduler.IOQueue;
public InputScheduler InputScheduler { get; set; } = InputScheduler.ThreadPool;
public SocketContinuationScheduler SocketContinuationScheduler { get; set; } = SocketContinuationScheduler.ThreadPool;
public bool DontAllocateMemoryForIdleConnections { get; set; } = true;
}
Setting OutputScheduler
to IOQueue
/IOThread
/ThreadPool
defers write operations to an IOQueue
/IOThread
. If more data is written to the Pipe
before the operation is executed on the IOQueue
/IOThread``ThreadPool
, it will be part of a single write operation.
Setting ApplicationCodeIsNonBlocking
to true
causes reads from ASP.NET Core to not get deferred to the ThreadPool
.
Note something in ASP.NET Core still defers the HTTP Handler to run on the ThreadPool
.
Setting DontAllocateMemoryForIdleConnections
to false
will allocate a buffer for every connection up-front. For idle connections, this is a waste of memory, but it eliminates a syscall to wait for data to be available.