Avoid allocating the CandidateSet when there's a single match (#9622)
- Add fast path for 0 route values, and policies - Remove RVD allocation in the fast path
This commit is contained in:
parent
418f3d82dd
commit
d62d33c531
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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;
|
||||
|
|
@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
/// </summary>
|
||||
public RouteValueDictionary RouteValues
|
||||
{
|
||||
get => _routeValues;
|
||||
get => _routeValues ?? (_routeValues = new RouteValueDictionary());
|
||||
set
|
||||
{
|
||||
_routeValues = value;
|
||||
|
|
@ -67,4 +67,4 @@ namespace Microsoft.AspNetCore.Routing
|
|||
set => throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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;
|
||||
|
|
@ -16,6 +16,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
private readonly EndpointSelector _selector;
|
||||
private readonly DfaState[] _states;
|
||||
private readonly int _maxSegmentCount;
|
||||
private readonly bool _isDefaultEndpointSelector;
|
||||
|
||||
public DfaMatcher(ILogger<DfaMatcher> logger, EndpointSelector selector, DfaState[] states, int maxSegmentCount)
|
||||
{
|
||||
|
|
@ -23,6 +24,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
_selector = selector;
|
||||
_states = states;
|
||||
_maxSegmentCount = maxSegmentCount;
|
||||
_isDefaultEndpointSelector = selector is DefaultEndpointSelector;
|
||||
}
|
||||
|
||||
public sealed override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context)
|
||||
|
|
@ -53,7 +55,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
// FindCandidateSet will process the DFA and return a candidate set. This does
|
||||
// some preliminary matching of the URL (mostly the literal segments).
|
||||
var (candidates, policies) = FindCandidateSet(httpContext, path, segments);
|
||||
if (candidates.Length == 0)
|
||||
var candidateCount = candidates.Length;
|
||||
if (candidateCount == 0)
|
||||
{
|
||||
if (log)
|
||||
{
|
||||
|
|
@ -68,6 +71,23 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
Logger.CandidatesFound(_logger, path, candidates);
|
||||
}
|
||||
|
||||
var policyCount = policies.Length;
|
||||
|
||||
// This is a fast path for single candidate, 0 policies and default selector
|
||||
if (candidateCount == 1 && policyCount == 0 && _isDefaultEndpointSelector)
|
||||
{
|
||||
ref var candidate = ref candidates[0];
|
||||
|
||||
// Just strict path matching
|
||||
if (candidate.Flags == Candidate.CandidateFlags.None)
|
||||
{
|
||||
context.Endpoint = candidate.Endpoint;
|
||||
|
||||
// We're done
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we have a candidate set, defined as a list of endpoints in
|
||||
// priority order.
|
||||
//
|
||||
|
|
@ -83,7 +103,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
// `candidateSet` is the mutable state that we pass to the EndpointSelector.
|
||||
var candidateSet = new CandidateSet(candidates);
|
||||
|
||||
for (var i = 0; i < candidates.Length; i++)
|
||||
for (var i = 0; i < candidateCount; i++)
|
||||
{
|
||||
// PERF: using ref here to avoid copying around big structs.
|
||||
//
|
||||
|
|
@ -165,7 +185,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
}
|
||||
}
|
||||
|
||||
if (policies.Length == 0)
|
||||
if (policyCount == 0)
|
||||
{
|
||||
// Perf: avoid a state machine if there are no polices
|
||||
return _selector.SelectAsync(httpContext, context, candidateSet);
|
||||
|
|
|
|||
Loading…
Reference in New Issue