Minor performance improvement for DfaMatcherBuilder (#854)
This commit is contained in:
parent
bcbf2a1a68
commit
bc482cd2b0
|
|
@ -2,14 +2,25 @@
|
|||
// 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.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing.TestObjects;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing.Matching
|
||||
{
|
||||
public partial class MatcherBuilderMultipleEntryBenchmark : EndpointRoutingBenchmarkBase
|
||||
{
|
||||
private IServiceProvider _services;
|
||||
private List<MatcherPolicy> _policies;
|
||||
private ILoggerFactory _loggerFactory;
|
||||
private DefaultEndpointSelector _selector;
|
||||
private DefaultParameterPolicyFactory _parameterPolicyFactory;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
|
|
@ -28,6 +39,26 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
Endpoints[8] = CreateEndpoint("/v2/account/{id}", "POST");
|
||||
Endpoints[9] = CreateEndpoint("/v2/account/{id}", "UPDATE");
|
||||
|
||||
// Define an unordered mixture of policies that implement INodeBuilderPolicy,
|
||||
// IEndpointComparerPolicy and/or IEndpointSelectorPolicy
|
||||
_policies = new List<MatcherPolicy>()
|
||||
{
|
||||
CreateNodeBuilderPolicy(4),
|
||||
CreateUberPolicy(2),
|
||||
CreateNodeBuilderPolicy(3),
|
||||
CreateEndpointComparerPolicy(5),
|
||||
CreateNodeBuilderPolicy(1),
|
||||
CreateEndpointSelectorPolicy(9),
|
||||
CreateEndpointComparerPolicy(7),
|
||||
CreateNodeBuilderPolicy(6),
|
||||
CreateEndpointSelectorPolicy(10),
|
||||
CreateUberPolicy(12),
|
||||
CreateEndpointComparerPolicy(11)
|
||||
};
|
||||
_loggerFactory = NullLoggerFactory.Instance;
|
||||
_selector = new DefaultEndpointSelector();
|
||||
_parameterPolicyFactory = new DefaultParameterPolicyFactory(Options.Create(new RouteOptions()), new TestServiceProvider());
|
||||
|
||||
_services = CreateServices();
|
||||
}
|
||||
|
||||
|
|
@ -46,5 +77,132 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
var builder = _services.GetRequiredService<DfaMatcherBuilder>();
|
||||
SetupMatcher(builder);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Constructor_Policies()
|
||||
{
|
||||
new DfaMatcherBuilder(_loggerFactory, _parameterPolicyFactory, _selector, _policies);
|
||||
}
|
||||
|
||||
private static MatcherPolicy CreateNodeBuilderPolicy(int order)
|
||||
{
|
||||
return new TestNodeBuilderPolicy(order);
|
||||
}
|
||||
private static MatcherPolicy CreateEndpointComparerPolicy(int order)
|
||||
{
|
||||
return new TestEndpointComparerPolicy(order);
|
||||
}
|
||||
|
||||
private static MatcherPolicy CreateEndpointSelectorPolicy(int order)
|
||||
{
|
||||
return new TestEndpointSelectorPolicy(order);
|
||||
}
|
||||
|
||||
private static MatcherPolicy CreateUberPolicy(int order)
|
||||
{
|
||||
return new TestUberPolicy(order);
|
||||
}
|
||||
|
||||
private class TestUberPolicy : TestMatcherPolicyBase, INodeBuilderPolicy, IEndpointComparerPolicy
|
||||
{
|
||||
public TestUberPolicy(int order) : base(order)
|
||||
{
|
||||
}
|
||||
|
||||
public IComparer<Endpoint> Comparer => new TestEndpointComparer();
|
||||
|
||||
public bool AppliesToEndpoints(IReadOnlyList<Endpoint> endpoints)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public PolicyJumpTable BuildJumpTable(int exitDestination, IReadOnlyList<PolicyJumpTableEdge> edges)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IReadOnlyList<PolicyNodeEdge> GetEdges(IReadOnlyList<Endpoint> endpoints)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class TestNodeBuilderPolicy : TestMatcherPolicyBase, INodeBuilderPolicy
|
||||
{
|
||||
public TestNodeBuilderPolicy(int order) : base(order)
|
||||
{
|
||||
}
|
||||
|
||||
public bool AppliesToEndpoints(IReadOnlyList<Endpoint> endpoints)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public PolicyJumpTable BuildJumpTable(int exitDestination, IReadOnlyList<PolicyJumpTableEdge> edges)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IReadOnlyList<PolicyNodeEdge> GetEdges(IReadOnlyList<Endpoint> endpoints)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class TestEndpointComparerPolicy : TestMatcherPolicyBase, IEndpointComparerPolicy
|
||||
{
|
||||
public TestEndpointComparerPolicy(int order) : base(order)
|
||||
{
|
||||
}
|
||||
|
||||
public IComparer<Endpoint> Comparer => new TestEndpointComparer();
|
||||
|
||||
public bool AppliesToEndpoints(IReadOnlyList<Endpoint> endpoints)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public Task ApplyAsync(HttpContext httpContext, EndpointSelectorContext context, CandidateSet candidates)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class TestEndpointSelectorPolicy : TestMatcherPolicyBase, IEndpointSelectorPolicy
|
||||
{
|
||||
public TestEndpointSelectorPolicy(int order) : base(order)
|
||||
{
|
||||
}
|
||||
|
||||
public bool AppliesToEndpoints(IReadOnlyList<Endpoint> endpoints)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public Task ApplyAsync(HttpContext httpContext, EndpointSelectorContext context, CandidateSet candidates)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class TestMatcherPolicyBase : MatcherPolicy
|
||||
{
|
||||
private int _order;
|
||||
|
||||
protected TestMatcherPolicyBase(int order)
|
||||
{
|
||||
_order = order;
|
||||
}
|
||||
|
||||
public override int Order { get { return _order; } }
|
||||
}
|
||||
|
||||
private class TestEndpointComparer : IComparer<Endpoint>
|
||||
{
|
||||
public int Compare(Endpoint x, Endpoint y)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly ParameterPolicyFactory _parameterPolicyFactory;
|
||||
private readonly EndpointSelector _selector;
|
||||
private readonly MatcherPolicy[] _policies;
|
||||
private readonly IEndpointSelectorPolicy[] _endpointSelectorPolicies;
|
||||
private readonly INodeBuilderPolicy[] _nodeBuilders;
|
||||
private readonly EndpointComparer _comparer;
|
||||
|
||||
|
|
@ -39,11 +39,11 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
_loggerFactory = loggerFactory;
|
||||
_parameterPolicyFactory = parameterPolicyFactory;
|
||||
_selector = selector;
|
||||
_policies = policies.OrderBy(p => p.Order).ToArray();
|
||||
|
||||
// Taking care to use _policies, which has been sorted.
|
||||
_nodeBuilders = _policies.OfType<INodeBuilderPolicy>().ToArray();
|
||||
_comparer = new EndpointComparer(_policies.OfType<IEndpointComparerPolicy>().ToArray());
|
||||
var (nodeBuilderPolicies, endpointComparerPolicies, endpointSelectorPolicies) = ExtractPolicies(policies.OrderBy(p => p.Order));
|
||||
_endpointSelectorPolicies = endpointSelectorPolicies;
|
||||
_nodeBuilders = nodeBuilderPolicies;
|
||||
_comparer = new EndpointComparer(endpointComparerPolicies);
|
||||
|
||||
_assignments = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
_slots = new List<KeyValuePair<string, object>>();
|
||||
|
|
@ -383,10 +383,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
List<IEndpointSelectorPolicy> endpointSelectorPolicies = null;
|
||||
if (node.Matches?.Count > 0)
|
||||
{
|
||||
for (var i = 0; i < _policies.Length; i++)
|
||||
for (var i = 0; i < _endpointSelectorPolicies.Length; i++)
|
||||
{
|
||||
if (_policies[i] is IEndpointSelectorPolicy endpointSelectorPolicy &&
|
||||
endpointSelectorPolicy.AppliesToEndpoints(node.Matches))
|
||||
var endpointSelectorPolicy = _endpointSelectorPolicies[i];
|
||||
if (endpointSelectorPolicy.AppliesToEndpoints(node.Matches))
|
||||
{
|
||||
if (endpointSelectorPolicies == null)
|
||||
{
|
||||
|
|
@ -465,16 +465,16 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
// internal for tests
|
||||
internal Candidate CreateCandidate(Endpoint endpoint, int score)
|
||||
{
|
||||
_assignments.Clear();
|
||||
_slots.Clear();
|
||||
_captures.Clear();
|
||||
_complexSegments.Clear();
|
||||
_constraints.Clear();
|
||||
|
||||
(string parameterName, int segmentIndex, int slotIndex) catchAll = default;
|
||||
|
||||
if (endpoint is RouteEndpoint routeEndpoint)
|
||||
{
|
||||
_assignments.Clear();
|
||||
_slots.Clear();
|
||||
_captures.Clear();
|
||||
_complexSegments.Clear();
|
||||
_constraints.Clear();
|
||||
|
||||
foreach (var kvp in routeEndpoint.RoutePattern.Defaults)
|
||||
{
|
||||
_assignments.Add(kvp.Key, _assignments.Count);
|
||||
|
|
@ -539,16 +539,27 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Candidate(
|
||||
endpoint,
|
||||
score,
|
||||
_slots.ToArray(),
|
||||
_captures.ToArray(),
|
||||
catchAll,
|
||||
_complexSegments.ToArray(),
|
||||
_constraints.ToArray());
|
||||
return new Candidate(
|
||||
endpoint,
|
||||
score,
|
||||
_slots.ToArray(),
|
||||
_captures.ToArray(),
|
||||
catchAll,
|
||||
_complexSegments.ToArray(),
|
||||
_constraints.ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Candidate(
|
||||
endpoint,
|
||||
score,
|
||||
Array.Empty<KeyValuePair<string, object>>(),
|
||||
Array.Empty<(string parameterName, int segmentIndex, int slotIndex)>(),
|
||||
catchAll,
|
||||
Array.Empty<(RoutePatternPathSegment pathSegment, int segmentIndex)>(),
|
||||
Array.Empty<KeyValuePair<string, IRouteConstraint>>());
|
||||
}
|
||||
}
|
||||
|
||||
private int[] GetGroupLengths(DfaNode node)
|
||||
|
|
@ -682,5 +693,32 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
work = nextWork;
|
||||
}
|
||||
}
|
||||
|
||||
private static (INodeBuilderPolicy[] nodeBuilderPolicies, IEndpointComparerPolicy[] endpointComparerPolicies, IEndpointSelectorPolicy[] endpointSelectorPolicies) ExtractPolicies(IEnumerable<MatcherPolicy> policies)
|
||||
{
|
||||
var nodeBuilderPolicies = new List<INodeBuilderPolicy>();
|
||||
var endpointComparerPolicies = new List<IEndpointComparerPolicy>();
|
||||
var endpointSelectorPolicies = new List<IEndpointSelectorPolicy>();
|
||||
|
||||
foreach (var policy in policies)
|
||||
{
|
||||
if (policy is INodeBuilderPolicy nodeBuilderPolicy)
|
||||
{
|
||||
nodeBuilderPolicies.Add(nodeBuilderPolicy);
|
||||
}
|
||||
|
||||
if (policy is IEndpointComparerPolicy endpointComparerPolicy)
|
||||
{
|
||||
endpointComparerPolicies.Add(endpointComparerPolicy);
|
||||
}
|
||||
|
||||
if (policy is IEndpointSelectorPolicy endpointSelectorPolicy)
|
||||
{
|
||||
endpointSelectorPolicies.Add(endpointSelectorPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
return (nodeBuilderPolicies.ToArray(), endpointComparerPolicies.ToArray(), endpointSelectorPolicies.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue