From 1c9a54aeb8fc5bf0d888cd4545a699f39ed22bdb Mon Sep 17 00:00:00 2001 From: mnltejaswini Date: Wed, 24 Feb 2016 17:25:32 -0800 Subject: [PATCH] [Perf] Reduce allocations in Link generation code paths --- .../Internal/LinkGenerationDecisionTree.cs | 14 ++++++++++---- .../RouteConstraintMatcher.cs | 2 +- .../Template/TemplateBinder.cs | 9 +++++++-- .../Tree/TreeRouter.cs | 9 +++++++-- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.AspNetCore.Routing/Internal/LinkGenerationDecisionTree.cs b/src/Microsoft.AspNetCore.Routing/Internal/LinkGenerationDecisionTree.cs index 6cd1b66b1f..b22be9d196 100644 --- a/src/Microsoft.AspNetCore.Routing/Internal/LinkGenerationDecisionTree.cs +++ b/src/Microsoft.AspNetCore.Routing/Internal/LinkGenerationDecisionTree.cs @@ -22,10 +22,16 @@ namespace Microsoft.AspNetCore.Routing.Internal public IList GetMatches(VirtualPathContext context) { - var results = new List(); - Walk(results, context, _root, isFallbackPath: false); - results.Sort(LinkGenerationMatchComparer.Instance); - return results; + // Perf: Avoid allocation for List if there aren't any Matches or Criteria + if (_root.Matches.Count > 0 || _root.Criteria.Count > 0) + { + var results = new List(); + Walk(results, context, _root, isFallbackPath: false); + results.Sort(LinkGenerationMatchComparer.Instance); + return results; + } + + return null; } // We need to recursively walk the decision tree based on the provided route data diff --git a/src/Microsoft.AspNetCore.Routing/RouteConstraintMatcher.cs b/src/Microsoft.AspNetCore.Routing/RouteConstraintMatcher.cs index eb29d0fbd5..8601d85581 100644 --- a/src/Microsoft.AspNetCore.Routing/RouteConstraintMatcher.cs +++ b/src/Microsoft.AspNetCore.Routing/RouteConstraintMatcher.cs @@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.Routing throw new ArgumentNullException(nameof(logger)); } - if (constraints == null) + if (constraints == null || constraints.Count == 0) { return true; } diff --git a/src/Microsoft.AspNetCore.Routing/Template/TemplateBinder.cs b/src/Microsoft.AspNetCore.Routing/Template/TemplateBinder.cs index 809a7a6913..73fa07b348 100644 --- a/src/Microsoft.AspNetCore.Routing/Template/TemplateBinder.cs +++ b/src/Microsoft.AspNetCore.Routing/Template/TemplateBinder.cs @@ -171,7 +171,8 @@ namespace Microsoft.AspNetCore.Routing.Template // Add any ambient values that don't match parameters - they need to be visible to constraints // but they will ignored by link generation. - var combinedValues = new RouteValueDictionary(context.AcceptedValues); + // Perf: Lazily allocate RouteValueDictionary only when needed as in most cases combined is same as accepted values + RouteValueDictionary combinedValues = null; if (ambientValues != null) { foreach (var kvp in ambientValues) @@ -181,6 +182,10 @@ namespace Microsoft.AspNetCore.Routing.Template var parameter = GetParameter(kvp.Key); if (parameter == null && !context.AcceptedValues.ContainsKey(kvp.Key)) { + if (combinedValues == null) + { + combinedValues = new RouteValueDictionary(context.AcceptedValues); + } combinedValues.Add(kvp.Key, kvp.Value); } } @@ -190,7 +195,7 @@ namespace Microsoft.AspNetCore.Routing.Template return new TemplateValuesResult() { AcceptedValues = context.AcceptedValues, - CombinedValues = combinedValues, + CombinedValues = combinedValues ?? context.AcceptedValues }; } diff --git a/src/Microsoft.AspNetCore.Routing/Tree/TreeRouter.cs b/src/Microsoft.AspNetCore.Routing/Tree/TreeRouter.cs index ae35f8e5d8..451171c248 100644 --- a/src/Microsoft.AspNetCore.Routing/Tree/TreeRouter.cs +++ b/src/Microsoft.AspNetCore.Routing/Tree/TreeRouter.cs @@ -138,9 +138,14 @@ namespace Microsoft.AspNetCore.Routing.Tree // order. We just need to iterate them and use the first one that can generate a link. var matches = _linkGenerationTree.GetMatches(context); - foreach (var match in matches) + if (matches == null) { - var path = GenerateVirtualPath(context, match.Entry); + return null; + } + + for (var i = 0; i < matches.Count; i++) + { + var path = GenerateVirtualPath(context, matches[i].Entry); if (path != null) { return path;