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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.Extensions.Internal;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Razor.TagHelpers
|
namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
{
|
{
|
||||||
|
|
@ -50,13 +49,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
protected List<TAttribute> Attributes { get; }
|
protected List<TAttribute> Attributes { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public TAttribute this[int index]
|
public TAttribute this[int index] => Attributes[index];
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Attributes[index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the first <typeparamref name="TAttribute"/> with <see cref="IReadOnlyTagHelperAttribute.Name"/>
|
/// 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));
|
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 />
|
/// <inheritdoc />
|
||||||
public int Count
|
public int Count => Attributes.Count;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Attributes.Count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether a <typeparamref name="TAttribute"/> matching <paramref name="item"/> exists in the
|
/// 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));
|
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>
|
/// <summary>
|
||||||
|
|
@ -173,7 +178,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
throw new ArgumentNullException(nameof(name));
|
throw new ArgumentNullException(nameof(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
attribute = Attributes.FirstOrDefault(attr => NameEquals(name, attr));
|
attribute = this[name];
|
||||||
|
|
||||||
return attribute != null;
|
return attribute != null;
|
||||||
}
|
}
|
||||||
|
|
@ -197,9 +202,23 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
throw new ArgumentNullException(nameof(name));
|
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 />
|
/// <inheritdoc />
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNet.Razor.Runtime;
|
using Microsoft.AspNet.Razor.Runtime;
|
||||||
using Microsoft.Extensions.Internal;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Razor.TagHelpers
|
namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
{
|
{
|
||||||
|
|
@ -134,6 +133,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
|
|
||||||
var attributeReplaced = false;
|
var attributeReplaced = false;
|
||||||
|
|
||||||
|
// Perf: Avoid allocating enumerator
|
||||||
for (var i = 0; i < Attributes.Count; i++)
|
for (var i = 0; i < Attributes.Count; i++)
|
||||||
{
|
{
|
||||||
if (NameEquals(name, Attributes[i]))
|
if (NameEquals(name, Attributes[i]))
|
||||||
|
|
@ -280,7 +280,18 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
throw new ArgumentNullException(nameof(name));
|
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 />
|
/// <inheritdoc />
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Razor.TagHelpers
|
namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
{
|
{
|
||||||
|
|
@ -12,6 +11,9 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TagHelperContext
|
public class TagHelperContext
|
||||||
{
|
{
|
||||||
|
private ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute> _allAttributes;
|
||||||
|
private IEnumerable<IReadOnlyTagHelperAttribute> _allAttributesData;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Instantiates a new <see cref="TagHelperContext"/>.
|
/// Instantiates a new <see cref="TagHelperContext"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -39,8 +41,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
throw new ArgumentNullException(nameof(uniqueId));
|
throw new ArgumentNullException(nameof(uniqueId));
|
||||||
}
|
}
|
||||||
|
|
||||||
AllAttributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(
|
_allAttributesData = allAttributes;
|
||||||
allAttributes.Select(attribute => new TagHelperAttribute(attribute.Name, attribute.Value)));
|
|
||||||
Items = items;
|
Items = items;
|
||||||
UniqueId = uniqueId;
|
UniqueId = uniqueId;
|
||||||
}
|
}
|
||||||
|
|
@ -48,7 +49,18 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Every attribute associated with the current HTML element.
|
/// Every attribute associated with the current HTML element.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute> AllAttributes { get; }
|
public ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute> AllAttributes
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_allAttributes == null)
|
||||||
|
{
|
||||||
|
_allAttributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(_allAttributesData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _allAttributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the collection of items used to communicate with other <see cref="ITagHelper"/>s.
|
/// 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;
|
TagName = tagName;
|
||||||
Attributes = new TagHelperAttributeList(attributes);
|
Attributes = attributes;
|
||||||
_getChildContentAsync = getChildContentAsync;
|
_getChildContentAsync = getChildContentAsync;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -243,8 +243,10 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
||||||
writer.Write('<');
|
writer.Write('<');
|
||||||
writer.Write(TagName);
|
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(' ');
|
||||||
writer.Write(attribute.Name);
|
writer.Write(attribute.Name);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue