Skip to content

Commit

Permalink
Various performance improvements (#482)
Browse files Browse the repository at this point in the history
- Call AdvanceTo in RequestBuffer only once
- Remove ConfigureAwait() calls
- Do not flush in pipelining mode
  • Loading branch information
Kaliumhexacyanoferrat authored Apr 23, 2024
1 parent e95724b commit 64284c6
Show file tree
Hide file tree
Showing 41 changed files with 106 additions and 106 deletions.
2 changes: 1 addition & 1 deletion Engine/Infrastructure/CoreRouter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ internal CoreRouter(IHandlerBuilder content, IEnumerable<IConcernBuilder> concer

public async ValueTask PrepareAsync()
{
await Content.PrepareAsync().ConfigureAwait(false);
await Content.PrepareAsync();

await Template.PrepareAsync();

Expand Down
9 changes: 5 additions & 4 deletions Engine/Infrastructure/Endpoints/EndPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ protected EndPoint(IServer server, IPEndPoint endPoint, NetworkConfiguration con
throw new BindingException($"Failed to bind to {endPoint}.", e);
}

Task = Task.Run(() => Listen().ConfigureAwait(false));
Task = Task.Run(() => Listen());
}

#endregion
Expand All @@ -78,7 +78,7 @@ private async Task Listen()
{
do
{
Handle(await Socket.AcceptAsync().ConfigureAwait(false));
Handle(await Socket.AcceptAsync());
}
while (!shuttingDown);
}
Expand All @@ -95,14 +95,15 @@ private void Handle(Socket client)
{
using var _ = ExecutionContext.SuppressFlow();

Task.Run(() => Accept(client).ConfigureAwait(false))
.ConfigureAwait(false);
Task.Run(() => Accept(client));
}

protected abstract PooledValueTask Accept(Socket client);

protected PooledValueTask Handle(Socket client, Stream inputStream)
{
client.NoDelay = true;

return new ClientHandler(client, inputStream, Server, this, Configuration).Run();
}

Expand Down
6 changes: 3 additions & 3 deletions Engine/Infrastructure/Endpoints/SecureEndPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ internal SecureEndPoint(IServer server, IPEndPoint endPoint, SecurityConfigurati

protected override async PooledValueTask Accept(Socket client)
{
var stream = await TryAuthenticate(client).ConfigureAwait(false);
var stream = await TryAuthenticate(client);

if (stream is not null)
{
await Handle(client, new PoolBufferedStream(stream)).ConfigureAwait(false);
await Handle(client, new PoolBufferedStream(stream));
}
else
{
Expand All @@ -80,7 +80,7 @@ protected override async PooledValueTask Accept(Socket client)
{
var stream = new SslStream(new NetworkStream(client), false);

await stream.AuthenticateAsServerAsync(AuthenticationOptions, CancellationToken.None).ConfigureAwait(false);
await stream.AuthenticateAsServerAsync(AuthenticationOptions, CancellationToken.None);

return stream;
}
Expand Down
6 changes: 3 additions & 3 deletions Engine/Protocol/ChunkedStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public override async Task WriteAsync(byte[] buffer, int offset, int count, Canc
{
if (count > 0)
{
await WriteAsync(count).ConfigureAwait(false);
await WriteAsync(count);
await WriteAsync(NL);

await Target.WriteAsync(buffer.AsMemory(offset, count), cancellationToken);
Expand All @@ -99,7 +99,7 @@ public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, Cancella
{
if (!buffer.IsEmpty)
{
await WriteAsync(buffer.Length).ConfigureAwait(false);
await WriteAsync(buffer.Length);
await WriteAsync(NL);

await Target.WriteAsync(buffer, cancellationToken);
Expand All @@ -110,7 +110,7 @@ public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, Cancella

public async ValueTask FinishAsync()
{
await WriteAsync("0").ConfigureAwait(false);
await WriteAsync("0");
await WriteAsync(NL);

await WriteAsync(NL);
Expand Down
14 changes: 7 additions & 7 deletions Engine/Protocol/ClientHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ internal ClientHandler(Socket socket, Stream stream, IServer server, IEndPoint e

Stream = stream;

ResponseHandler = new ResponseHandler(Server, Stream, Connection, Configuration);
ResponseHandler = new ResponseHandler(Server, Stream, Configuration);
}

#endregion
Expand All @@ -70,7 +70,7 @@ internal async PooledValueTask Run()
{
try
{
await HandlePipe(PipeReader.Create(Stream, READER_OPTIONS)).ConfigureAwait(false);
await HandlePipe(PipeReader.Create(Stream, READER_OPTIONS));
}
catch (Exception e)
{
Expand Down Expand Up @@ -115,9 +115,9 @@ private async PooledValueTask HandlePipe(PipeReader reader)

RequestBuilder? request;

while (Server.Running && (request = await parser.TryParseAsync(buffer).ConfigureAwait(false)) is not null)
while (Server.Running && (request = await parser.TryParseAsync(buffer)) is not null)
{
if (!await HandleRequest(request))
if (!await HandleRequest(request, !buffer.ReadRequired))
{
break;
}
Expand All @@ -129,7 +129,7 @@ private async PooledValueTask HandlePipe(PipeReader reader)
}
}

private async PooledValueTask<bool> HandleRequest(RequestBuilder builder)
private async PooledValueTask<bool> HandleRequest(RequestBuilder builder, bool dataRemaining)
{
var address = (Connection.RemoteEndPoint as IPEndPoint)?.Address;

Expand All @@ -139,9 +139,9 @@ private async PooledValueTask<bool> HandleRequest(RequestBuilder builder)

bool keepAlive = (bool)KeepAlive;

using var response = await Server.Handler.HandleAsync(request).ConfigureAwait(false) ?? throw new InvalidOperationException("The root request handler did not return a response");
using var response = await Server.Handler.HandleAsync(request) ?? throw new InvalidOperationException("The root request handler did not return a response");

var success = await ResponseHandler.Handle(request, response, keepAlive);
var success = await ResponseHandler.Handle(request, response, keepAlive, dataRemaining);

if (!success || !keepAlive)
{
Expand Down
2 changes: 1 addition & 1 deletion Engine/Protocol/Parser/RequestScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ private static async PooledValueTask<bool> Fill(RequestBuffer buffer, bool force
{
if (buffer.ReadRequired || force)
{
await buffer.Read(force).ConfigureAwait(false);
await buffer.Read(force);
}

return !buffer.Data.IsEmpty;
Expand Down
22 changes: 12 additions & 10 deletions Engine/Protocol/RequestBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ namespace GenHTTP.Engine.Protocol
/// </remarks>
internal sealed class RequestBuffer : IDisposable
{
private ReadOnlySequence<byte>? _Data;

#region Get-/Setters

Expand All @@ -34,9 +35,9 @@ internal sealed class RequestBuffer : IDisposable

private CancellationTokenSource? Cancellation { get; set; }

internal ReadOnlySequence<byte> Data { get; private set; }
internal ReadOnlySequence<byte> Data => _Data ?? new();

internal bool ReadRequired => Data.IsEmpty;
internal bool ReadRequired => (_Data == null) || _Data.Value.IsEmpty;

#endregion

Expand All @@ -46,8 +47,6 @@ internal RequestBuffer(PipeReader reader, NetworkConfiguration configuration)
{
Reader = reader;
Configuration = configuration;

Data = new();
}

#endregion
Expand All @@ -56,8 +55,13 @@ internal RequestBuffer(PipeReader reader, NetworkConfiguration configuration)

internal async PooledValueTask<long?> Read(bool force = false)
{
if ((Data.Length == 0) || force)
if (ReadRequired || force)
{
if (_Data != null)
{
Reader.AdvanceTo(_Data.Value.Start);
}

if (Cancellation is null)
{
Cancellation = new();
Expand All @@ -67,7 +71,7 @@ internal RequestBuffer(PipeReader reader, NetworkConfiguration configuration)
{
Cancellation.CancelAfter(Configuration.RequestReadTimeout);

Data = (await Reader.ReadAsync(Cancellation.Token).ConfigureAwait(false)).Buffer;
_Data = (await Reader.ReadAsync(Cancellation.Token)).Buffer;

Cancellation.CancelAfter(int.MaxValue);
}
Expand All @@ -85,14 +89,12 @@ internal RequestBuffer(PipeReader reader, NetworkConfiguration configuration)

internal void Advance(SequencePosition position)
{
Data = Data.Slice(position);
Reader.AdvanceTo(Data.Start);
_Data = Data.Slice(position);
}

internal void Advance(long bytes)
{
Data = Data.Slice(bytes);
Reader.AdvanceTo(Data.Start);
_Data = Data.Slice(bytes);
}

#endregion
Expand Down
2 changes: 1 addition & 1 deletion Engine/Protocol/RequestContentParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ internal async Task<Stream> GetBody(RequestBuffer buffer)

while (toFetch > 0)
{
await buffer.Read().ConfigureAwait(false);
await buffer.Read();

var toRead = Math.Min(buffer.Data.Length, Math.Min(Configuration.TransferBufferSize, toFetch));

Expand Down
33 changes: 15 additions & 18 deletions Engine/Protocol/ResponseHandler.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Buffers;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

Expand Down Expand Up @@ -30,20 +29,17 @@ internal sealed class ResponseHandler

private Stream OutputStream { get; }

private Socket Socket { get; }

internal NetworkConfiguration Configuration { get; }

#endregion

#region Initialization

internal ResponseHandler(IServer server, Stream outputstream, Socket socket, NetworkConfiguration configuration)
internal ResponseHandler(IServer server, Stream outputstream, NetworkConfiguration configuration)
{
Server = server;

OutputStream = outputstream;
Socket = socket;

Configuration = configuration;
}
Expand All @@ -52,11 +48,11 @@ internal ResponseHandler(IServer server, Stream outputstream, Socket socket, Net

#region Functionality

internal async ValueTask<bool> Handle(IRequest request, IResponse response, bool keepAlive)
internal async ValueTask<bool> Handle(IRequest request, IResponse response, bool keepAlive, bool dataRemaining)
{
try
{
await WriteStatus(request, response).ConfigureAwait(false);
await WriteStatus(request, response);

await WriteHeader(response, keepAlive);

Expand All @@ -67,11 +63,12 @@ internal async ValueTask<bool> Handle(IRequest request, IResponse response, bool
await WriteBody(response);
}

Socket.NoDelay = true;

await OutputStream.FlushAsync();

Socket.NoDelay = false;
// flush if the client waits for this response
// otherwise save flushes for improved performance when pipelining
if (!dataRemaining)
{
await OutputStream.FlushAsync();
}

Server.Companion?.OnRequestHandled(request, response);

Expand Down Expand Up @@ -104,11 +101,11 @@ private async ValueTask WriteHeader(IResponse response, bool keepAlive)
{
if (response.Headers.TryGetValue(SERVER_HEADER, out var server))
{
await WriteHeaderLine(SERVER_HEADER, server).ConfigureAwait(false);
await WriteHeaderLine(SERVER_HEADER, server);
}
else
{
await Write("Server: GenHTTP/", Server.Version, NL).ConfigureAwait(false);
await Write("Server: GenHTTP/", Server.Version, NL);
}

await WriteHeaderLine("Date", DateHeader.GetValue());
Expand Down Expand Up @@ -183,13 +180,13 @@ private async ValueTask WriteBody(IResponse response)
{
using var chunked = new ChunkedStream(OutputStream);

await response.Content.WriteAsync(chunked, Configuration.TransferBufferSize).ConfigureAwait(false);
await response.Content.WriteAsync(chunked, Configuration.TransferBufferSize);

await chunked.FinishAsync();
}
else
{
await response.Content.WriteAsync(OutputStream, Configuration.TransferBufferSize).ConfigureAwait(false);
await response.Content.WriteAsync(OutputStream, Configuration.TransferBufferSize);
}
}
}
Expand All @@ -204,7 +201,7 @@ private async ValueTask WriteBody(IResponse response)

private async ValueTask WriteCookie(Cookie cookie)
{
await Write("Set-Cookie: ", cookie.Name, "=", cookie.Value).ConfigureAwait(false);
await Write("Set-Cookie: ", cookie.Name, "=", cookie.Value);

if (cookie.MaxAge is not null)
{
Expand Down Expand Up @@ -264,7 +261,7 @@ private async ValueTask Write(string part1, string? part2 = null, string? part3
ASCII.GetBytes(part7, 0, part7.Length, buffer, index);
}

await OutputStream.WriteAsync(buffer.AsMemory(0, length)).ConfigureAwait(false);
await OutputStream.WriteAsync(buffer.AsMemory(0, length));
}
finally
{
Expand Down
10 changes: 5 additions & 5 deletions Engine/Utilities/PoolBufferedStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public PoolBufferedStream(Stream stream)
{
Stream = stream;

Buffer = POOL.Rent(8192);
Buffer = POOL.Rent(4096);
Current = 0;
}

Expand Down Expand Up @@ -115,12 +115,12 @@ public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, Cancella

if (Current == Buffer.Length)
{
await WriteBufferAsync(cancellationToken).ConfigureAwait(false);
await WriteBufferAsync(cancellationToken);
}
}
else
{
await WriteBufferAsync(cancellationToken).ConfigureAwait(false);
await WriteBufferAsync(cancellationToken);

await Stream.WriteAsync(buffer, cancellationToken);
}
Expand All @@ -135,7 +135,7 @@ public override void Flush()

public override async Task FlushAsync(CancellationToken cancellationToken)
{
await WriteBufferAsync(cancellationToken).ConfigureAwait(false);
await WriteBufferAsync(cancellationToken);

await Stream.FlushAsync(cancellationToken);
}
Expand All @@ -154,7 +154,7 @@ private async ValueTask WriteBufferAsync(CancellationToken cancellationToken)
{
if (Current > 0)
{
await Stream.WriteAsync(Buffer.AsMemory(0, Current), cancellationToken).ConfigureAwait(false);
await Stream.WriteAsync(Buffer.AsMemory(0, Current), cancellationToken);

Current = 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ public WebAuthenticationConcern(IHandler parent, Func<IHandler, IHandler> conten
{
request.Target.Advance();

return await ResourceHandler.HandleAsync(request).ConfigureAwait(false);
return await ResourceHandler.HandleAsync(request);
}

if (await Integration.CheckSetupRequired(request).ConfigureAwait(false))
if (await Integration.CheckSetupRequired(request))
{
if (segment?.Value != Integration.SetupRoute)
{
Expand Down
Loading

0 comments on commit 64284c6

Please sign in to comment.