Optimize `TagHelperAttributeList` allocations.
- Today `TagHelperAttributeList` and corresponding infrastructure copy themselves too frequently. I've reduced the copying since we own `TagHelperExecutionContext`. - Removed usage of linq inside of `TagHelperAttributeList` to reduce allocations. #599
This commit is contained in:
parent
43aabcb2d6
commit
31a68a0705
|
|
@ -5,7 +5,6 @@ using System;
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.TagHelpers
|
||||
{
|
||||
|
|
@ -50,13 +49,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
protected List<TAttribute> Attributes { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public TAttribute this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return Attributes[index];
|
||||
}
|
||||
}
|
||||
public TAttribute this[int index] => Attributes[index];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first <typeparamref name="TAttribute"/> with <see cref="IReadOnlyTagHelperAttribute.Name"/>
|
||||
|
|
@ -78,18 +71,21 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
return Attributes.FirstOrDefault(attribute => NameEquals(name, attribute));
|
||||
// Perf: Avoid allocating enumerator
|
||||
for (var i = 0; i < Attributes.Count; i++)
|
||||
{
|
||||
if (NameEquals(name, Attributes[i]))
|
||||
{
|
||||
return Attributes[i];
|
||||
}
|
||||
}
|
||||
|
||||
return default(TAttribute);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return Attributes.Count;
|
||||
}
|
||||
}
|
||||
public int Count => Attributes.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a <typeparamref name="TAttribute"/> matching <paramref name="item"/> exists in the
|
||||
|
|
@ -131,7 +127,16 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
return Attributes.Any(attribute => NameEquals(name, attribute));
|
||||
// Perf: Avoid allocating enumerator
|
||||
for (var i = 0; i < Attributes.Count; i++)
|
||||
{
|
||||
if (NameEquals(name, Attributes[i]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -173,7 +178,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
attribute = Attributes.FirstOrDefault(attr => NameEquals(name, attr));
|
||||
attribute = this[name];
|
||||
|
||||
return attribute != null;
|
||||
}
|
||||
|
|
@ -197,9 +202,23 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
attributes = Attributes.Where(attribute => NameEquals(name, attribute));
|
||||
// Perf: Avoid allocating enumerator
|
||||
List<TAttribute> matchedAttributes = null;
|
||||
for (var i = 0; i < Attributes.Count; i++)
|
||||
{
|
||||
if (NameEquals(name, Attributes[i]))
|
||||
{
|
||||
if (matchedAttributes == null)
|
||||
{
|
||||
matchedAttributes = new List<TAttribute>();
|
||||
}
|
||||
|
||||
return attributes.Any();
|
||||
matchedAttributes.Add(Attributes[i]);
|
||||
}
|
||||
}
|
||||
attributes = matchedAttributes ?? Enumerable.Empty<TAttribute>();
|
||||
|
||||
return matchedAttributes != null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Razor.Runtime;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.TagHelpers
|
||||
{
|
||||
|
|
@ -134,6 +133,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
|
||||
var attributeReplaced = false;
|
||||
|
||||
// Perf: Avoid allocating enumerator
|
||||
for (var i = 0; i < Attributes.Count; i++)
|
||||
{
|
||||
if (NameEquals(name, Attributes[i]))
|
||||
|
|
@ -280,7 +280,18 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
return Attributes.RemoveAll(attribute => NameEquals(name, attribute)) > 0;
|
||||
// Perf: Avoid allocating enumerator
|
||||
var removedAtLeastOne = false;
|
||||
for (var i = Attributes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (NameEquals(name, Attributes[i]))
|
||||
{
|
||||
Attributes.RemoveAt(i);
|
||||
removedAtLeastOne = true;
|
||||
}
|
||||
}
|
||||
|
||||
return removedAtLeastOne;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.TagHelpers
|
||||
{
|
||||
|
|
@ -12,6 +11,9 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
/// </summary>
|
||||
public class TagHelperContext
|
||||
{
|
||||
private ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute> _allAttributes;
|
||||
private IEnumerable<IReadOnlyTagHelperAttribute> _allAttributesData;
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="TagHelperContext"/>.
|
||||
/// </summary>
|
||||
|
|
@ -39,8 +41,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
throw new ArgumentNullException(nameof(uniqueId));
|
||||
}
|
||||
|
||||
AllAttributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(
|
||||
allAttributes.Select(attribute => new TagHelperAttribute(attribute.Name, attribute.Value)));
|
||||
_allAttributesData = allAttributes;
|
||||
Items = items;
|
||||
UniqueId = uniqueId;
|
||||
}
|
||||
|
|
@ -48,7 +49,18 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
/// <summary>
|
||||
/// Every attribute associated with the current HTML element.
|
||||
/// </summary>
|
||||
public ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute> AllAttributes { get; }
|
||||
public ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute> AllAttributes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_allAttributes == null)
|
||||
{
|
||||
_allAttributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(_allAttributesData);
|
||||
}
|
||||
|
||||
return _allAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of items used to communicate with other <see cref="ITagHelper"/>s.
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
}
|
||||
|
||||
TagName = tagName;
|
||||
Attributes = new TagHelperAttributeList(attributes);
|
||||
Attributes = attributes;
|
||||
_getChildContentAsync = getChildContentAsync;
|
||||
}
|
||||
|
||||
|
|
@ -243,8 +243,10 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
writer.Write('<');
|
||||
writer.Write(TagName);
|
||||
|
||||
foreach (var attribute in Attributes)
|
||||
// Perf: Avoid allocating enumerator
|
||||
for (var i = 0; i < Attributes.Count; i++)
|
||||
{
|
||||
var attribute = Attributes[i];
|
||||
writer.Write(' ');
|
||||
writer.Write(attribute.Name);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue