Skip to content

Commit

Permalink
Faster strongly typed features for server FeatureCollection (#31322)
Browse files Browse the repository at this point in the history
- Use strongly typed features
- Use Unsafe.As to workaround dotnet/runtime#49614
- Do the casting it for Set<T> as well
- Add more benchmarks
- Reorder features (order of reset);
  • Loading branch information
benaadams committed Mar 29, 2021
1 parent a61d535 commit c0d17cf
Show file tree
Hide file tree
Showing 14 changed files with 620 additions and 407 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,6 @@ private void ValidateNonOriginHostHeader(string hostText)

protected override void OnReset()
{
ResetHttp1Features();

_requestTimedOut = false;
_requestTargetForm = HttpRequestTarget.Unknown;
Expand All @@ -625,6 +624,10 @@ protected override void OnReset()
_requestCount++;

MinResponseDataRate = ServerOptions.Limits.MinResponseDataRate;

// Reset Http1 Features
_currentIHttpMinRequestBodyDataRateFeature = this;
_currentIHttpMinResponseDataRateFeature = this;
}

protected override void OnRequestProcessingEnding()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipelines;
using System.Net;
Expand All @@ -18,23 +16,11 @@

namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
internal partial class HttpProtocol : IHttpRequestFeature,
IHttpRequestBodyDetectionFeature,
IHttpResponseFeature,
IHttpResponseBodyFeature,
IRequestBodyPipeFeature,
IHttpUpgradeFeature,
IHttpConnectionFeature,
IHttpRequestLifetimeFeature,
IHttpRequestIdentifierFeature,
IHttpRequestTrailersFeature,
IHttpBodyControlFeature,
IHttpMaxRequestBodySizeFeature,
IEndpointFeature,
IRouteValuesFeature
internal partial class HttpProtocol
{
// NOTE: When feature interfaces are added to or removed from this HttpProtocol class implementation,
// then the list of `implementedFeatures` in the generated code project MUST also be updated.
// then the list of `implementedFeatures` in the generated code project MUST also be updated first
// and the code generator re-reun, which will change the interface list.
// See also: tools/CodeGenerator/HttpProtocolFeatureCollection.cs

string IHttpRequestFeature.Protocol
Expand Down Expand Up @@ -255,25 +241,6 @@ RouteValueDictionary IRouteValuesFeature.RouteValues

Stream IHttpResponseBodyFeature.Stream => ResponseBody;

protected void ResetHttp1Features()
{
_currentIHttpMinRequestBodyDataRateFeature = this;
_currentIHttpMinResponseDataRateFeature = this;
}

protected void ResetHttp2Features()
{
_currentIHttp2StreamIdFeature = this;
_currentIHttpResponseTrailersFeature = this;
_currentIHttpResetFeature = this;
}

protected void ResetHttp3Features()
{
_currentIHttpResponseTrailersFeature = this;
_currentIHttpResetFeature = this;
}

void IHttpResponseFeature.OnStarting(Func<object, Task> callback, object state)
{
OnStarting(callback, state);
Expand Down
536 changes: 281 additions & 255 deletions src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.Generated.cs

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,11 @@ protected override void OnReset()
_keepAlive = true;
_connectionAborted = false;

ResetHttp2Features();
// Reset Http2 Features
_currentIHttpMinRequestBodyDataRateFeature = this;
_currentIHttp2StreamIdFeature = this;
_currentIHttpResponseTrailersFeature = this;
_currentIHttpResetFeature = this;
}

protected override void OnRequestProcessingEnded()
Expand Down
5 changes: 4 additions & 1 deletion src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,10 @@ public void Tick(DateTimeOffset now)

protected override void OnReset()
{
ResetHttp3Features();
// Reset Http3 Features
_currentIHttpMinRequestBodyDataRateFeature = this;
_currentIHttpResponseTrailersFeature = this;
_currentIHttpResetFeature = this;
}

protected override void ApplicationAbort() => ApplicationAbort(new ConnectionAbortedException(CoreStrings.ConnectionAbortedByApplication), Http3ErrorCode.InternalError);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
using System.Buffers;
using System.IO.Pipelines;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;

using BenchmarkDotNet.Attributes;

using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.Features.Authentication;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Testing;

Expand All @@ -20,45 +21,205 @@ public class HttpProtocolFeatureCollection
{
private readonly IFeatureCollection _collection;

[Benchmark]
[Benchmark(Description = "Get<IHttpRequestFeature>*")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IHttpRequestFeature GetViaTypeOf_First()
public IHttpRequestFeature Get_IHttpRequestFeature()
{
return (IHttpRequestFeature)_collection[typeof(IHttpRequestFeature)];
return _collection.Get<IHttpRequestFeature>();
}

[Benchmark]
[Benchmark(Description = "Get<IHttpResponseFeature>*")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IHttpRequestFeature GetViaGeneric_First()
public IHttpResponseFeature Get_IHttpResponseFeature()
{
return _collection.Get<IHttpRequestFeature>();
return _collection.Get<IHttpResponseFeature>();
}

[Benchmark(Description = "Get<IHttpResponseBodyFeature>*")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IHttpResponseBodyFeature Get_IHttpResponseBodyFeature()
{
return _collection.Get<IHttpResponseBodyFeature>();
}

[Benchmark(Description = "Get<IRouteValuesFeature>*")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IRouteValuesFeature Get_IRouteValuesFeature()
{
return _collection.Get<IRouteValuesFeature>();
}

[Benchmark(Description = "Get<IEndpointFeature>*")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IEndpointFeature Get_IEndpointFeature()
{
return _collection.Get<IEndpointFeature>();
}

[Benchmark(Description = "Get<IServiceProvidersFeature>")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IServiceProvidersFeature Get_IServiceProvidersFeature()
{
return _collection.Get<IServiceProvidersFeature>();
}

[Benchmark(Description = "Get<IItemsFeature>")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IItemsFeature Get_IItemsFeature()
{
return _collection.Get<IItemsFeature>();
}

[Benchmark(Description = "Get<IQueryFeature>")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IQueryFeature Get_IQueryFeature()
{
return _collection.Get<IQueryFeature>();
}

[Benchmark(Description = "Get<IRequestBodyPipeFeature>*")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IRequestBodyPipeFeature Get_IRequestBodyPipeFeature()
{
return _collection.Get<IRequestBodyPipeFeature>();
}

[Benchmark(Description = "Get<IFormFeature>")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IFormFeature Get_IFormFeature()
{
return _collection.Get<IFormFeature>();
}

[Benchmark(Description = "Get<IHttpAuthenticationFeature>")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IHttpAuthenticationFeature Get_IHttpAuthenticationFeature()
{
return _collection.Get<IHttpAuthenticationFeature>();
}

[Benchmark(Description = "Get<IHttpRequestIdentifierFeature>*")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IHttpRequestIdentifierFeature Get_IHttpRequestIdentifierFeature()
{
return _collection.Get<IHttpRequestIdentifierFeature>();
}

[Benchmark(Description = "Get<IHttpConnectionFeature>*")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IHttpConnectionFeature Get_IHttpConnectionFeature()
{
return _collection.Get<IHttpConnectionFeature>();
}

[Benchmark(Description = "Get<ISessionFeature>")]
[MethodImpl(MethodImplOptions.NoInlining)]
public ISessionFeature Get_ISessionFeature()
{
return _collection.Get<ISessionFeature>();
}

[Benchmark]
[Benchmark(Description = "Get<IResponseCookiesFeature>")]
[MethodImpl(MethodImplOptions.NoInlining)]
public object GetViaTypeOf_Custom()
public IResponseCookiesFeature Get_IResponseCookiesFeature()
{
return (IHttpCustomFeature)_collection[typeof(IHttpCustomFeature)];
return _collection.Get<IResponseCookiesFeature>();
}

[Benchmark]
[Benchmark(Description = "Get<IHttpRequestTrailersFeature>*")]
[MethodImpl(MethodImplOptions.NoInlining)]
public object GetViaGeneric_Custom()
public IHttpRequestTrailersFeature Get_IHttpRequestTrailersFeature()
{
return _collection.Get<IHttpCustomFeature>();
return _collection.Get<IHttpRequestTrailersFeature>();
}

[Benchmark(Description = "Get<IHttpResponseTrailersFeature>")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IHttpResponseTrailersFeature Get_IHttpResponseTrailersFeature()
{
return _collection.Get<IHttpResponseTrailersFeature>();
}

[Benchmark]
[Benchmark(Description = "Get<ITlsConnectionFeature>")]
[MethodImpl(MethodImplOptions.NoInlining)]
public object GetViaTypeOf_NotFound()
public ITlsConnectionFeature Get_ITlsConnectionFeature()
{
return (IHttpNotFoundFeature)_collection[typeof(IHttpNotFoundFeature)];
return _collection.Get<ITlsConnectionFeature>();
}

[Benchmark]
[Benchmark(Description = "Get<IHttpUpgradeFeature>*")]
[MethodImpl(MethodImplOptions.NoInlining)]
public object GetViaGeneric_NotFound()
public IHttpUpgradeFeature Get_IHttpUpgradeFeature()
{
return _collection.Get<IHttpUpgradeFeature>();
}

[Benchmark(Description = "Get<IHttpWebSocketFeature>")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IHttpWebSocketFeature Get_IHttpWebSocketFeature()
{
return _collection.Get<IHttpWebSocketFeature>();
}

[Benchmark(Description = "Get<IHttp2StreamIdFeature>")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IHttp2StreamIdFeature Get_IHttp2StreamIdFeature()
{
return _collection.Get<IHttp2StreamIdFeature>();
}

[Benchmark(Description = "Get<IHttpRequestLifetimeFeature>*")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IHttpRequestLifetimeFeature Get_IHttpRequestLifetimeFeature()
{
return _collection.Get<IHttpRequestLifetimeFeature>();
}

[Benchmark(Description = "Get<IHttpMaxRequestBodySizeFeature>*")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IHttpMaxRequestBodySizeFeature Get_IHttpMaxRequestBodySizeFeature()
{
return _collection.Get<IHttpMaxRequestBodySizeFeature>();
}

[Benchmark(Description = "Get<IHttpMinRequestBodyDataRateFeature>*")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IHttpMinRequestBodyDataRateFeature Get_IHttpMinRequestBodyDataRateFeature()
{
return _collection.Get<IHttpMinRequestBodyDataRateFeature>();
}

[Benchmark(Description = "Get<IHttpMinResponseDataRateFeature>*")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IHttpMinResponseDataRateFeature Get_IHttpMinResponseDataRateFeature()
{
return _collection.Get<IHttpMinResponseDataRateFeature>();
}

[Benchmark(Description = "Get<IHttpBodyControlFeature>*")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IHttpBodyControlFeature Get_IHttpBodyControlFeature()
{
return _collection.Get<IHttpBodyControlFeature>();
}

[Benchmark(Description = "Get<IHttpRequestBodyDetectionFeature>*")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IHttpRequestBodyDetectionFeature Get_IHttpRequestBodyDetectionFeature()
{
return _collection.Get<IHttpRequestBodyDetectionFeature>();
}

[Benchmark(Description = "Get<IHttpResetFeature>")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IHttpResetFeature Get_IHttpResetFeature()
{
return _collection.Get<IHttpResetFeature>();
}

[Benchmark(Description = "Get<IHttpNotFoundFeature>")]
[MethodImpl(MethodImplOptions.NoInlining)]
public IHttpNotFoundFeature Get_IHttpNotFoundFeature()
{
return _collection.Get<IHttpNotFoundFeature>();
}
Expand Down Expand Up @@ -89,11 +250,7 @@ public HttpProtocolFeatureCollection()
_collection = http1Connection;
}

private interface IHttpCustomFeature
{
}

private interface IHttpNotFoundFeature
public interface IHttpNotFoundFeature
{
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,11 @@

namespace Microsoft.AspNetCore.Connections
{
internal partial class TransportConnection : IConnectionIdFeature,
IConnectionTransportFeature,
IConnectionItemsFeature,
IMemoryPoolFeature,
IConnectionLifetimeFeature
internal partial class TransportConnection
{
// NOTE: When feature interfaces are added to or removed from this TransportConnection class implementation,
// then the list of `features` in the generated code project MUST also be updated.
// then the list of `features` in the generated code project MUST also be updated first
// and the code generator re-reun, which will change the interface list.
// See also: tools/CodeGenerator/TransportConnectionFeatureCollection.cs

MemoryPool<byte> IMemoryPoolFeature.MemoryPool => MemoryPool;
Expand Down
Loading

0 comments on commit c0d17cf

Please sign in to comment.