Skip to content

Commit 19c9010

Browse files
benaadamsrynowak
authored andcommitted
Read interface IList.Count once rather than per iteration (#9962)
1 parent 3e0afe4 commit 19c9010

File tree

14 files changed

+154
-79
lines changed

14 files changed

+154
-79
lines changed

src/Http/Routing/src/Patterns/RoutePattern.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,12 @@ public RoutePatternParameterPart GetParameter(string name)
139139
throw new ArgumentNullException(nameof(name));
140140
}
141141

142-
for (var i = 0; i < Parameters.Count; i++)
142+
var parameters = Parameters;
143+
// Read interface .Count once rather than per iteration
144+
var parametersCount = parameters.Count;
145+
for (var i = 0; i < parametersCount; i++)
143146
{
144-
var parameter = Parameters[i];
147+
var parameter = parameters[i];
145148
if (string.Equals(parameter.Name, name, StringComparison.OrdinalIgnoreCase))
146149
{
147150
return parameter;

src/Http/Routing/src/Template/TemplateBinder.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -521,16 +521,20 @@ private bool TryBindValuesCore(UriBuildingContext context, RouteValueDictionary
521521
}
522522
}
523523

524-
for (var i = 0; i < _pattern.PathSegments.Count; i++)
524+
var segments = _pattern.PathSegments;
525+
// Read interface .Count once rather than per iteration
526+
var segmentsCount = segments.Count;
527+
for (var i = 0; i < segmentsCount; i++)
525528
{
526529
Debug.Assert(context.BufferState == SegmentState.Beginning);
527530
Debug.Assert(context.UriState == SegmentState.Beginning);
528531

529-
var segment = _pattern.PathSegments[i];
530-
531-
for (var j = 0; j < segment.Parts.Count; j++)
532+
var parts = segments[i].Parts;
533+
// Read interface .Count once rather than per iteration
534+
var partsCount = parts.Count;
535+
for (var j = 0; j < partsCount; j++)
532536
{
533-
var part = segment.Parts[j];
537+
var part = parts[j];
534538

535539
if (part is RoutePatternLiteralPart literalPart)
536540
{
@@ -581,7 +585,7 @@ private bool TryBindValuesCore(UriBuildingContext context, RouteValueDictionary
581585
// for format, so we remove '.' and generate 5.
582586
if (!context.Accept(converted, parameterPart.EncodeSlashes))
583587
{
584-
if (j != 0 && parameterPart.IsOptional && (separatorPart = segment.Parts[j - 1] as RoutePatternSeparatorPart) != null)
588+
if (j != 0 && parameterPart.IsOptional && (separatorPart = parts[j - 1] as RoutePatternSeparatorPart) != null)
585589
{
586590
context.Remove(separatorPart.Content);
587591
}

src/Http/Routing/src/Tree/LinkGenerationDecisionTree.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,20 @@ private void Walk(
113113
{
114114
// Any entries in node.Matches have had all their required values satisfied, so add them
115115
// to the results.
116-
for (var i = 0; i < node.Matches.Count; i++)
116+
var matches = node.Matches;
117+
// Read interface .Count once rather than per iteration
118+
var matchesCount = matches.Count;
119+
for (var i = 0; i < matchesCount; i++)
117120
{
118-
results.Add(new OutboundMatchResult(node.Matches[i], isFallbackPath));
121+
results.Add(new OutboundMatchResult(matches[i], isFallbackPath));
119122
}
120123

121-
for (var i = 0; i < node.Criteria.Count; i++)
124+
var criteria = node.Criteria;
125+
// Read interface .Count once rather than per iteration
126+
var criteriaCount = criteria.Count;
127+
for (var i = 0; i < criteriaCount; i++)
122128
{
123-
var criterion = node.Criteria[i];
129+
var criterion = criteria[i];
124130
var key = criterion.Key;
125131

126132
if (values.TryGetValue(key, out var value))

src/Mvc/Mvc.Core/src/Filters/DefaultFilterProvider.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ public void OnProvidersExecuting(FilterProviderContext context)
2121

2222
if (context.ActionContext.ActionDescriptor.FilterDescriptors != null)
2323
{
24-
// Perf: Avoid allocations
25-
for (var i = 0; i < context.Results.Count; i++)
24+
var results = context.Results;
25+
// Perf: Avoid allocating enumerator and read interface .Count once rather than per iteration
26+
var resultsCount = results.Count;
27+
for (var i = 0; i < resultsCount; i++)
2628
{
27-
ProvideFilter(context, context.Results[i]);
29+
ProvideFilter(context, results[i]);
2830
}
2931
}
3032
}

src/Mvc/Mvc.Razor/src/RazorViewEngine.cs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -249,15 +249,18 @@ private ViewLocationCacheResult LocatePageFromViewLocations(
249249
isMainPage);
250250
Dictionary<string, string> expanderValues = null;
251251

252-
if (_options.ViewLocationExpanders.Count > 0)
252+
var expanders = _options.ViewLocationExpanders;
253+
// Read interface .Count once rather than per iteration
254+
var expandersCount = expanders.Count;
255+
if (expandersCount > 0)
253256
{
254257
expanderValues = new Dictionary<string, string>(StringComparer.Ordinal);
255258
expanderContext.Values = expanderValues;
256259

257260
// Perf: Avoid allocations
258-
for (var i = 0; i < _options.ViewLocationExpanders.Count; i++)
261+
for (var i = 0; i < expandersCount; i++)
259262
{
260-
_options.ViewLocationExpanders[i].PopulateValues(expanderContext);
263+
expanders[i].PopulateValues(expanderContext);
261264
}
262265
}
263266

@@ -350,9 +353,12 @@ private ViewLocationCacheResult OnCacheMiss(
350353
{
351354
var viewLocations = GetViewLocationFormats(expanderContext);
352355

353-
for (var i = 0; i < _options.ViewLocationExpanders.Count; i++)
356+
var expanders = _options.ViewLocationExpanders;
357+
// Read interface .Count once rather than per iteration
358+
var expandersCount = expanders.Count;
359+
for (var i = 0; i < expandersCount; i++)
354360
{
355-
viewLocations = _options.ViewLocationExpanders[i].ExpandViewLocations(expanderContext, viewLocations);
361+
viewLocations = expanders[i].ExpandViewLocations(expanderContext, viewLocations);
356362
}
357363

358364
ViewLocationCacheResult cacheResult = null;
@@ -404,9 +410,12 @@ internal ViewLocationCacheResult CreateCacheResult(
404410
var viewDescriptor = factoryResult.ViewDescriptor;
405411
if (viewDescriptor?.ExpirationTokens != null)
406412
{
407-
for (var i = 0; i < viewDescriptor.ExpirationTokens.Count; i++)
413+
var viewExpirationTokens = viewDescriptor.ExpirationTokens;
414+
// Read interface .Count once rather than per iteration
415+
var viewExpirationTokensCount = viewExpirationTokens.Count;
416+
for (var i = 0; i < viewExpirationTokensCount; i++)
408417
{
409-
expirationTokens.Add(viewDescriptor.ExpirationTokens[i]);
418+
expirationTokens.Add(viewExpirationTokens[i]);
410419
}
411420
}
412421

src/Mvc/Mvc.Razor/src/TagHelpers/UrlResolutionTagHelper.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,12 @@ protected void ProcessUrlAttribute(string attributeName, TagHelperOutput output)
156156
throw new ArgumentNullException(nameof(output));
157157
}
158158

159-
for (var i = 0; i < output.Attributes.Count; i++)
159+
var attributes = output.Attributes;
160+
// Read interface .Count once rather than per iteration
161+
var attributesCount = attributes.Count;
162+
for (var i = 0; i < attributesCount; i++)
160163
{
161-
var attribute = output.Attributes[i];
164+
var attribute = attributes[i];
162165
if (!string.Equals(attribute.Name, attributeName, StringComparison.OrdinalIgnoreCase))
163166
{
164167
continue;
@@ -170,7 +173,7 @@ protected void ProcessUrlAttribute(string attributeName, TagHelperOutput output)
170173
string resolvedUrl;
171174
if (TryResolveUrl(stringValue, resolvedUrl: out resolvedUrl))
172175
{
173-
output.Attributes[i] = new TagHelperAttribute(
176+
attributes[i] = new TagHelperAttribute(
174177
attribute.Name,
175178
resolvedUrl,
176179
attribute.ValueStyle);
@@ -199,15 +202,15 @@ protected void ProcessUrlAttribute(string attributeName, TagHelperOutput output)
199202
IHtmlContent resolvedUrl;
200203
if (TryResolveUrl(stringValue, resolvedUrl: out resolvedUrl))
201204
{
202-
output.Attributes[i] = new TagHelperAttribute(
205+
attributes[i] = new TagHelperAttribute(
203206
attribute.Name,
204207
resolvedUrl,
205208
attribute.ValueStyle);
206209
}
207210
else if (htmlString == null)
208211
{
209212
// Not a ~/ URL. Just avoid re-encoding the attribute value later.
210-
output.Attributes[i] = new TagHelperAttribute(
213+
attributes[i] = new TagHelperAttribute(
211214
attribute.Name,
212215
new HtmlString(stringValue),
213216
attribute.ValueStyle);

src/Mvc/Mvc.TagHelpers/src/AttributeMatcher.cs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,20 @@ public static bool TryDetermineMode<TMode>(
4444
}
4545

4646
var foundResult = false;
47-
result = default(TMode);
47+
result = default;
4848

4949
// Perf: Avoid allocating enumerator
50-
for (var i = 0; i < modeInfos.Count; i++)
50+
var modeInfosCount = modeInfos.Count;
51+
var allAttributes = context.AllAttributes;
52+
// Read interface .Count once rather than per iteration
53+
var allAttributesCount = allAttributes.Count;
54+
for (var i = 0; i < modeInfosCount; i++)
5155
{
5256
var modeInfo = modeInfos[i];
53-
if (!HasMissingAttributes(context, modeInfo.Attributes) &&
57+
var requiredAttributes = modeInfo.Attributes;
58+
// If there are fewer attributes present than required, one or more of them must be missing.
59+
if (allAttributesCount >= requiredAttributes.Length &&
60+
!HasMissingAttributes(allAttributes, requiredAttributes) &&
5461
compare(result, modeInfo.Mode) <= 0)
5562
{
5663
foundResult = true;
@@ -61,19 +68,13 @@ public static bool TryDetermineMode<TMode>(
6168
return foundResult;
6269
}
6370

64-
private static bool HasMissingAttributes(TagHelperContext context, string[] requiredAttributes)
71+
private static bool HasMissingAttributes(ReadOnlyTagHelperAttributeList allAttributes, string[] requiredAttributes)
6572
{
66-
if (context.AllAttributes.Count < requiredAttributes.Length)
67-
{
68-
// If there are fewer attributes present than required, one or more of them must be missing.
69-
return true;
70-
}
71-
7273
// Check for all attribute values
7374
// Perf: Avoid allocating enumerator
7475
for (var i = 0; i < requiredAttributes.Length; i++)
7576
{
76-
if (!context.AllAttributes.TryGetAttribute(requiredAttributes[i], out var attribute))
77+
if (!allAttributes.TryGetAttribute(requiredAttributes[i], out var attribute))
7778
{
7879
// Missing attribute.
7980
return true;

src/Mvc/Mvc.TagHelpers/src/LinkTagHelper.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -381,8 +381,9 @@ private void BuildFallbackBlock(TagHelperAttributeList attributes, TagHelperCont
381381

382382
builder.AppendHtml(", \"");
383383

384-
// Perf: Avoid allocating enumerator
385-
for (var i = 0; i < attributes.Count; i++)
384+
// Perf: Avoid allocating enumerator and read interface .Count once rather than per iteration
385+
var attributesCount = attributes.Count;
386+
for (var i = 0; i < attributesCount; i++)
386387
{
387388
var attribute = attributes[i];
388389
if (string.Equals(attribute.Name, HrefAttributeName, StringComparison.OrdinalIgnoreCase))
@@ -440,8 +441,10 @@ private void AppendFallbackHrefs(TagHelperContent builder, IReadOnlyList<string>
440441
{
441442
builder.AppendHtml("[");
442443
var firstAdded = false;
443-
// Perf: Avoid allocating enumerator
444-
for (var i = 0; i < fallbackHrefs.Count; i++)
444+
445+
// Perf: Avoid allocating enumerator and read interface .Count once rather than per iteration
446+
var fallbackHrefsCount = fallbackHrefs.Count;
447+
for (var i = 0; i < fallbackHrefsCount; i++)
445448
{
446449
if (firstAdded)
447450
{
@@ -498,8 +501,9 @@ private void BuildLinkTag(string href, TagHelperAttributeList attributes, TagHel
498501

499502
var addHref = true;
500503

501-
// Perf: Avoid allocating enumerator
502-
for (var i = 0; i < attributes.Count; i++)
504+
// Perf: Avoid allocating enumerator and read interface .Count once rather than per iteration
505+
var attributesCount = attributes.Count;
506+
for (var i = 0; i < attributesCount; i++)
503507
{
504508
var attribute = attributes[i];
505509

src/Mvc/Mvc.TagHelpers/src/ScriptTagHelper.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,9 @@ private void BuildFallbackBlock(TagHelperAttributeList attributes, TagHelperCont
331331

332332
var addSrc = true;
333333

334-
// Perf: Avoid allocating enumerator
335-
for (var i = 0; i < attributes.Count; i++)
334+
// Perf: Avoid allocating enumerator and read interface .Count once rather than per iteration
335+
var attributesCount = attributes.Count;
336+
for (var i = 0; i < attributesCount; i++)
336337
{
337338
var attribute = attributes[i];
338339
if (!attribute.Name.Equals(SrcAttributeName, StringComparison.OrdinalIgnoreCase))
@@ -434,8 +435,9 @@ private void BuildScriptTag(
434435

435436
var addSrc = true;
436437

437-
// Perf: Avoid allocating enumerator
438-
for (var i = 0; i < attributes.Count; i++)
438+
// Perf: Avoid allocating enumerator and read interface .Count once rather than per iteration
439+
var attributesCount = attributes.Count;
440+
for (var i = 0; i < attributesCount; i++)
439441
{
440442
var attribute = attributes[i];
441443
if (!attribute.Name.Equals(SrcAttributeName, StringComparison.OrdinalIgnoreCase))

src/Mvc/Mvc.TagHelpers/src/TagHelperOutputExtensions.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -321,13 +321,14 @@ private static void CopyHtmlAttribute(
321321
TagHelperOutput tagHelperOutput,
322322
TagHelperContext context)
323323
{
324-
var existingAttribute = context.AllAttributes[allAttributeIndex];
324+
var allAttributes = context.AllAttributes;
325+
var existingAttribute = allAttributes[allAttributeIndex];
325326

326327
// Move backwards through context.AllAttributes from the provided index until we find a familiar attribute
327328
// in tagHelperOutput where we can insert the copied value after the familiar one.
328329
for (var i = allAttributeIndex - 1; i >= 0; i--)
329330
{
330-
var previousName = context.AllAttributes[i].Name;
331+
var previousName = allAttributes[i].Name;
331332
var index = IndexOfFirstMatch(previousName, tagHelperOutput.Attributes);
332333
if (index != -1)
333334
{
@@ -336,11 +337,13 @@ private static void CopyHtmlAttribute(
336337
}
337338
}
338339

340+
// Read interface .Count once rather than per iteration
341+
var allAttributesCount = allAttributes.Count;
339342
// Move forward through context.AllAttributes from the provided index until we find a familiar attribute in
340343
// tagHelperOutput where we can insert the copied value.
341-
for (var i = allAttributeIndex + 1; i < context.AllAttributes.Count; i++)
344+
for (var i = allAttributeIndex + 1; i < allAttributesCount; i++)
342345
{
343-
var nextName = context.AllAttributes[i].Name;
346+
var nextName = allAttributes[i].Name;
344347
var index = IndexOfFirstMatch(nextName, tagHelperOutput.Attributes);
345348
if (index != -1)
346349
{
@@ -355,7 +358,9 @@ private static void CopyHtmlAttribute(
355358

356359
private static int IndexOfFirstMatch(string name, TagHelperAttributeList attributes)
357360
{
358-
for (var i = 0; i < attributes.Count; i++)
361+
// Read interface .Count once rather than per iteration
362+
var attributesCount = attributes.Count;
363+
for (var i = 0; i < attributesCount; i++)
359364
{
360365
if (string.Equals(name, attributes[i].Name, StringComparison.OrdinalIgnoreCase))
361366
{

src/Razor/Razor.Runtime/ref/Microsoft.AspNetCore.Razor.Runtime.netcoreapp3.0.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ public void Reinitialize(string tagName, Microsoft.AspNetCore.Razor.TagHelpers.T
9393
public partial class TagHelperRunner
9494
{
9595
public TagHelperRunner() { }
96-
[System.Diagnostics.DebuggerStepThroughAttribute]
9796
public System.Threading.Tasks.Task RunAsync(Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext executionContext) { throw null; }
9897
}
9998
public partial class TagHelperScopeManager

0 commit comments

Comments
 (0)