diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/Frame.Generated.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/Frame.Generated.cs index 181e54d667..28b0be9f78 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/Frame.Generated.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/Frame.Generated.cs @@ -63,7 +63,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http _currentIHttpSendFileFeature = null; } - private object FastFeatureGet(Type key) + internal object FastFeatureGet(Type key) { if (key == IHttpRequestFeatureType) { @@ -132,7 +132,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http return ExtraFeatureGet(key); } - private void FastFeatureSet(Type key, object feature) + internal void FastFeatureSet(Type key, object feature) { _featureRevision++; diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/FrameFeatureCollection.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/FrameFeatureCollection.cs new file mode 100644 index 0000000000..4115934774 --- /dev/null +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/FrameFeatureCollection.cs @@ -0,0 +1,107 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using BenchmarkDotNet.Attributes; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Server.Kestrel.Core; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; + +namespace Microsoft.AspNetCore.Server.Kestrel.Performance +{ + [Config(typeof(CoreConfig))] + public class FrameFeatureCollection + { + private readonly Frame _frame; + private IFeatureCollection _collection; + + [Benchmark(Baseline = true)] + public IHttpRequestFeature GetFirstViaFastFeature() + { + return (IHttpRequestFeature)GetFastFeature(typeof(IHttpRequestFeature)); + } + + [Benchmark] + public IHttpRequestFeature GetFirstViaType() + { + return (IHttpRequestFeature)Get(typeof(IHttpRequestFeature)); + } + + [Benchmark] + public IHttpRequestFeature GetFirstViaExtension() + { + return _collection.GetType(); + } + + [Benchmark] + public IHttpRequestFeature GetFirstViaGeneric() + { + return _collection.Get(); + } + + [Benchmark] + public IHttpSendFileFeature GetLastViaFastFeature() + { + return (IHttpSendFileFeature)GetFastFeature(typeof(IHttpSendFileFeature)); + } + + [Benchmark] + public IHttpSendFileFeature GetLastViaType() + { + return (IHttpSendFileFeature)Get(typeof(IHttpSendFileFeature)); + } + + [Benchmark] + public IHttpSendFileFeature GetLastViaExtension() + { + return _collection.GetType(); + } + + [Benchmark] + public IHttpSendFileFeature GetLastViaGeneric() + { + return _collection.Get (); + } + + private object Get(Type type) + { + return _collection[type]; + } + + private object GetFastFeature(Type type) + { + return _frame.FastFeatureGet(type); + } + + public FrameFeatureCollection() + { + var serviceContext = new ServiceContext + { + HttpParserFactory = _ => NullParser.Instance, + ServerOptions = new KestrelServerOptions() + }; + var frameContext = new FrameContext + { + ServiceContext = serviceContext, + ConnectionInformation = new MockConnectionInformation() + }; + + _frame = new Frame(application: null, frameContext: frameContext); + } + + [Setup] + public void Setup() + { + _collection = _frame; + } + + } + public static class IFeatureCollectionExtensions + { + public static T GetType(this IFeatureCollection collection) + { + return (T)collection[typeof(T)]; + } + } +} diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/FrameParsingOverheadBenchmark.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/FrameParsingOverheadBenchmark.cs index 1e05f8ea99..ac7f6869bf 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/FrameParsingOverheadBenchmark.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/FrameParsingOverheadBenchmark.cs @@ -1,12 +1,10 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Text; using BenchmarkDotNet.Attributes; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; -using Microsoft.AspNetCore.Server.Kestrel.Internal.System; using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines; namespace Microsoft.AspNetCore.Server.Kestrel.Performance @@ -100,52 +98,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance ErrorUtilities.ThrowInvalidRequestHeaders(); } } - - private class NullParser : IHttpParser - { - private readonly byte[] _startLine = Encoding.ASCII.GetBytes("GET /plaintext HTTP/1.1\r\n"); - private readonly byte[] _target = Encoding.ASCII.GetBytes("/plaintext"); - private readonly byte[] _hostHeaderName = Encoding.ASCII.GetBytes("Host"); - private readonly byte[] _hostHeaderValue = Encoding.ASCII.GetBytes("www.example.com"); - private readonly byte[] _acceptHeaderName = Encoding.ASCII.GetBytes("Accept"); - private readonly byte[] _acceptHeaderValue = Encoding.ASCII.GetBytes("text/plain,text/html;q=0.9,application/xhtml+xml;q=0.9,application/xml;q=0.8,*/*;q=0.7\r\n\r\n"); - private readonly byte[] _connectionHeaderName = Encoding.ASCII.GetBytes("Connection"); - private readonly byte[] _connectionHeaderValue = Encoding.ASCII.GetBytes("keep-alive"); - - public static readonly NullParser Instance = new NullParser(); - - public bool ParseHeaders(IHttpHeadersHandler handler, ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined, out int consumedBytes) - { - handler.OnHeader(new Span(_hostHeaderName), new Span(_hostHeaderValue)); - handler.OnHeader(new Span(_acceptHeaderName), new Span(_acceptHeaderValue)); - handler.OnHeader(new Span(_connectionHeaderName), new Span(_connectionHeaderValue)); - - consumedBytes = 0; - consumed = buffer.Start; - examined = buffer.End; - - return true; - } - - public bool ParseRequestLine(IHttpRequestLineHandler handler, ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined) - { - handler.OnStartLine(HttpMethod.Get, - HttpVersion.Http11, - new Span(_target), - new Span(_target), - Span.Empty, - Span.Empty, - false); - - consumed = buffer.Start; - examined = buffer.End; - - return true; - } - - public void Reset() - { - } - } } } diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Mocks/NullParser.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Mocks/NullParser.cs new file mode 100644 index 0000000000..d27bfc1f2b --- /dev/null +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Mocks/NullParser.cs @@ -0,0 +1,57 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Text; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; +using Microsoft.AspNetCore.Server.Kestrel.Internal.System; +using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines; + +namespace Microsoft.AspNetCore.Server.Kestrel.Performance +{ + public class NullParser : IHttpParser + { + private readonly byte[] _startLine = Encoding.ASCII.GetBytes("GET /plaintext HTTP/1.1\r\n"); + private readonly byte[] _target = Encoding.ASCII.GetBytes("/plaintext"); + private readonly byte[] _hostHeaderName = Encoding.ASCII.GetBytes("Host"); + private readonly byte[] _hostHeaderValue = Encoding.ASCII.GetBytes("www.example.com"); + private readonly byte[] _acceptHeaderName = Encoding.ASCII.GetBytes("Accept"); + private readonly byte[] _acceptHeaderValue = Encoding.ASCII.GetBytes("text/plain,text/html;q=0.9,application/xhtml+xml;q=0.9,application/xml;q=0.8,*/*;q=0.7\r\n\r\n"); + private readonly byte[] _connectionHeaderName = Encoding.ASCII.GetBytes("Connection"); + private readonly byte[] _connectionHeaderValue = Encoding.ASCII.GetBytes("keep-alive"); + + public static readonly NullParser Instance = new NullParser(); + + public bool ParseHeaders(IHttpHeadersHandler handler, ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined, out int consumedBytes) + { + handler.OnHeader(new Span(_hostHeaderName), new Span(_hostHeaderValue)); + handler.OnHeader(new Span(_acceptHeaderName), new Span(_acceptHeaderValue)); + handler.OnHeader(new Span(_connectionHeaderName), new Span(_connectionHeaderValue)); + + consumedBytes = 0; + consumed = buffer.Start; + examined = buffer.End; + + return true; + } + + public bool ParseRequestLine(IHttpRequestLineHandler handler, ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined) + { + handler.OnStartLine(HttpMethod.Get, + HttpVersion.Http11, + new Span(_target), + new Span(_target), + Span.Empty, + Span.Empty, + false); + + consumed = buffer.Start; + examined = buffer.End; + + return true; + } + + public void Reset() + { + } + } +} diff --git a/tools/CodeGenerator/FrameFeatureCollection.cs b/tools/CodeGenerator/FrameFeatureCollection.cs index e487a36a94..01b9c1cfa9 100644 --- a/tools/CodeGenerator/FrameFeatureCollection.cs +++ b/tools/CodeGenerator/FrameFeatureCollection.cs @@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http _current{feature.Name} = null;")} }} - private object FastFeatureGet(Type key) + internal object FastFeatureGet(Type key) {{{Each(allFeatures, feature => $@" if (key == {feature.Name}Type) {{ @@ -96,7 +96,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http return ExtraFeatureGet(key); }} - private void FastFeatureSet(Type key, object feature) + internal void FastFeatureSet(Type key, object feature) {{ _featureRevision++; {Each(allFeatures, feature => $@"