Prevent unneeded allocations of `Block` children enumerators.

#635
This commit is contained in:
N. Taylor Mullen 2016-01-15 12:27:21 -08:00
parent 315d79ff2b
commit 1ce9180a3e
8 changed files with 66 additions and 62 deletions

View File

@ -20,7 +20,21 @@ namespace Microsoft.AspNet.Razor.Parser
protected override bool CanRewrite(Block block)
{
var gen = block.ChunkGenerator as AttributeBlockChunkGenerator;
return gen != null && block.Children.Any() && block.Children.All(IsLiteralAttributeValue);
if (gen != null && block.Children.Count > 0)
{
// Perf: Avoid allocating an enumerator.
for (var i = 0; i < block.Children.Count; i++)
{
if (!IsLiteralAttributeValue(block.Children[i]))
{
return false;
}
}
return true;
}
return false;
}
protected override SyntaxTreeNode RewriteBlock(BlockBuilder parent, Block block)

View File

@ -253,7 +253,7 @@ namespace Microsoft.AspNet.Razor.Parser
}
return new ParserResults(_blockStack.Pop().Build(),
// TagHelperDescriptors are not found by default. The RazorParser is responsible
// TagHelperDescriptors are not found by default. The RazorParser is responsible
// for identifying TagHelperDescriptors and rebuilding ParserResults.
tagHelperDescriptors: Enumerable.Empty<TagHelperDescriptor>(),
errorSink: _errorSink);

View File

@ -14,9 +14,11 @@ namespace Microsoft.AspNet.Razor.Parser
public virtual void VisitBlock(Block block)
{
VisitStartBlock(block);
foreach (SyntaxTreeNode node in block.Children)
// Perf: Avoid allocating an enumerator.
for (var i = 0; i < block.Children.Count; i++)
{
node.Accept(this);
block.Children[i].Accept(this);
}
VisitEndBlock(block);
}

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
source.Reset();
}
protected Block(BlockType? type, IEnumerable<SyntaxTreeNode> contents, IParentChunkGenerator generator)
protected Block(BlockType? type, IReadOnlyList<SyntaxTreeNode> contents, IParentChunkGenerator generator)
{
if (type == null)
{
@ -30,14 +30,15 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
Children = contents;
ChunkGenerator = generator;
foreach (SyntaxTreeNode node in Children)
// Perf: Avoid allocating an enumerator.
for (var i = 0; i < Children.Count; i++)
{
node.Parent = this;
Children[i].Parent = this;
}
}
// A Test constructor
internal Block(BlockType type, IEnumerable<SyntaxTreeNode> contents, IParentChunkGenerator generator)
internal Block(BlockType type, IReadOnlyList<SyntaxTreeNode> contents, IParentChunkGenerator generator)
{
Type = type;
ChunkGenerator = generator;
@ -46,7 +47,7 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
public BlockType Type { get; }
public IEnumerable<SyntaxTreeNode> Children { get; }
public IReadOnlyList<SyntaxTreeNode> Children { get; }
public IParentChunkGenerator ChunkGenerator { get; }
@ -133,9 +134,10 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
public virtual IEnumerable<Span> Flatten()
{
// Create an enumerable that flattens the tree for use by syntax highlighters, etc.
foreach (SyntaxTreeNode element in Children)
// Perf: Avoid allocating an enumerator.
for (var i = 0; i < Children.Count; i++)
{
var element = Children[i];
var span = element as Span;
if (span != null)
{

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
}
public BlockType? Type { get; set; }
public IList<SyntaxTreeNode> Children { get; private set; }
public List<SyntaxTreeNode> Children { get; private set; }
public IParentChunkGenerator ChunkGenerator { get; set; }
public virtual Block Build()

View File

@ -302,8 +302,19 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
private IEnumerable<string> GetAttributeNames(Block tagBlock)
{
// Need to calculate how many children we should take that represent the attributes.
var childrenOffset = IsPartialTag(tagBlock) ? 1 : 2;
var attributeChildren = tagBlock.Children.Skip(1).Take(tagBlock.Children.Count() - childrenOffset);
var childrenOffset = IsPartialTag(tagBlock) ? 0 : 1;
var childCount = tagBlock.Children.Count - childrenOffset;
if (childCount <= 1)
{
return Enumerable.Empty<string>();
}
var attributeChildren = new List<SyntaxTreeNode>(childCount - 1);
for (var i = 1; i < childCount; i++)
{
attributeChildren.Add(tagBlock.Children[i]);
}
var attributeNames = new List<string>();
foreach (var child in attributeChildren)
@ -637,9 +648,9 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
private static string GetTagName(Block tagBlock)
{
var child = tagBlock.Children.First();
var child = tagBlock.Children[0];
if (tagBlock.Type != BlockType.Tag || !tagBlock.Children.Any() || !(child is Span))
if (tagBlock.Type != BlockType.Tag || tagBlock.Children.Count == 0|| !(child is Span))
{
return null;
}

View File

@ -15,13 +15,13 @@ namespace Microsoft.AspNet.Razor.Test.Framework
{
private const BlockType ThisBlockType = BlockType.Statement;
public StatementBlock(IParentChunkGenerator chunkGenerator, IEnumerable<SyntaxTreeNode> children)
public StatementBlock(IParentChunkGenerator chunkGenerator, IReadOnlyList<SyntaxTreeNode> children)
: base(ThisBlockType, children, chunkGenerator)
{
}
public StatementBlock(IParentChunkGenerator chunkGenerator, params SyntaxTreeNode[] children)
: this(chunkGenerator, (IEnumerable<SyntaxTreeNode>)children)
: this(chunkGenerator, (IReadOnlyList<SyntaxTreeNode>)children)
{
}
@ -29,24 +29,19 @@ namespace Microsoft.AspNet.Razor.Test.Framework
: this(ParentChunkGenerator.Null, children)
{
}
public StatementBlock(IEnumerable<SyntaxTreeNode> children)
: this(ParentChunkGenerator.Null, children)
{
}
}
public class DirectiveBlock : Block
{
private const BlockType ThisBlockType = BlockType.Directive;
public DirectiveBlock(IParentChunkGenerator chunkGenerator, IEnumerable<SyntaxTreeNode> children)
public DirectiveBlock(IParentChunkGenerator chunkGenerator, IReadOnlyList<SyntaxTreeNode> children)
: base(ThisBlockType, children, chunkGenerator)
{
}
public DirectiveBlock(IParentChunkGenerator chunkGenerator, params SyntaxTreeNode[] children)
: this(chunkGenerator, (IEnumerable<SyntaxTreeNode>)children)
: this(chunkGenerator, (IReadOnlyList<SyntaxTreeNode>)children)
{
}
@ -54,24 +49,19 @@ namespace Microsoft.AspNet.Razor.Test.Framework
: this(ParentChunkGenerator.Null, children)
{
}
public DirectiveBlock(IEnumerable<SyntaxTreeNode> children)
: this(ParentChunkGenerator.Null, children)
{
}
}
public class FunctionsBlock : Block
{
private const BlockType ThisBlockType = BlockType.Functions;
public FunctionsBlock(IParentChunkGenerator chunkGenerator, IEnumerable<SyntaxTreeNode> children)
public FunctionsBlock(IParentChunkGenerator chunkGenerator, IReadOnlyList<SyntaxTreeNode> children)
: base(ThisBlockType, children, chunkGenerator)
{
}
public FunctionsBlock(IParentChunkGenerator chunkGenerator, params SyntaxTreeNode[] children)
: this(chunkGenerator, (IEnumerable<SyntaxTreeNode>)children)
: this(chunkGenerator, (IReadOnlyList<SyntaxTreeNode>)children)
{
}
@ -79,24 +69,19 @@ namespace Microsoft.AspNet.Razor.Test.Framework
: this(ParentChunkGenerator.Null, children)
{
}
public FunctionsBlock(IEnumerable<SyntaxTreeNode> children)
: this(ParentChunkGenerator.Null, children)
{
}
}
public class ExpressionBlock : Block
{
private const BlockType ThisBlockType = BlockType.Expression;
public ExpressionBlock(IParentChunkGenerator chunkGenerator, IEnumerable<SyntaxTreeNode> children)
public ExpressionBlock(IParentChunkGenerator chunkGenerator, IReadOnlyList<SyntaxTreeNode> children)
: base(ThisBlockType, children, chunkGenerator)
{
}
public ExpressionBlock(IParentChunkGenerator chunkGenerator, params SyntaxTreeNode[] children)
: this(chunkGenerator, (IEnumerable<SyntaxTreeNode>)children)
: this(chunkGenerator, (IReadOnlyList<SyntaxTreeNode>)children)
{
}
@ -104,11 +89,6 @@ namespace Microsoft.AspNet.Razor.Test.Framework
: this(new ExpressionChunkGenerator(), children)
{
}
public ExpressionBlock(IEnumerable<SyntaxTreeNode> children)
: this(new ExpressionChunkGenerator(), children)
{
}
}
public class MarkupTagBlock : Block
@ -128,18 +108,18 @@ namespace Microsoft.AspNet.Razor.Test.Framework
public MarkupBlock(
BlockType blockType,
IParentChunkGenerator chunkGenerator,
IEnumerable<SyntaxTreeNode> children)
IReadOnlyList<SyntaxTreeNode> children)
: base(blockType, children, chunkGenerator)
{
}
public MarkupBlock(IParentChunkGenerator chunkGenerator, IEnumerable<SyntaxTreeNode> children)
public MarkupBlock(IParentChunkGenerator chunkGenerator, IReadOnlyList<SyntaxTreeNode> children)
: this(ThisBlockType, chunkGenerator, children)
{
}
public MarkupBlock(IParentChunkGenerator chunkGenerator, params SyntaxTreeNode[] children)
: this(chunkGenerator, (IEnumerable<SyntaxTreeNode>)children)
: this(chunkGenerator, (IReadOnlyList<SyntaxTreeNode>)children)
{
}
@ -147,11 +127,6 @@ namespace Microsoft.AspNet.Razor.Test.Framework
: this(ParentChunkGenerator.Null, children)
{
}
public MarkupBlock(IEnumerable<SyntaxTreeNode> children)
: this(ParentChunkGenerator.Null, children)
{
}
}
public class MarkupTagHelperBlock : TagHelperBlock
@ -221,13 +196,13 @@ namespace Microsoft.AspNet.Razor.Test.Framework
{
private const BlockType ThisBlockType = BlockType.Section;
public SectionBlock(IParentChunkGenerator chunkGenerator, IEnumerable<SyntaxTreeNode> children)
public SectionBlock(IParentChunkGenerator chunkGenerator, IReadOnlyList<SyntaxTreeNode> children)
: base(ThisBlockType, children, chunkGenerator)
{
}
public SectionBlock(IParentChunkGenerator chunkGenerator, params SyntaxTreeNode[] children)
: this(chunkGenerator, (IEnumerable<SyntaxTreeNode>)children)
: this(chunkGenerator, (IReadOnlyList<SyntaxTreeNode>)children)
{
}
@ -236,7 +211,7 @@ namespace Microsoft.AspNet.Razor.Test.Framework
{
}
public SectionBlock(IEnumerable<SyntaxTreeNode> children)
public SectionBlock(IReadOnlyList<SyntaxTreeNode> children)
: this(ParentChunkGenerator.Null, children)
{
}
@ -246,13 +221,13 @@ namespace Microsoft.AspNet.Razor.Test.Framework
{
private const BlockType ThisBlockType = BlockType.Template;
public TemplateBlock(IParentChunkGenerator chunkGenerator, IEnumerable<SyntaxTreeNode> children)
public TemplateBlock(IParentChunkGenerator chunkGenerator, IReadOnlyList<SyntaxTreeNode> children)
: base(ThisBlockType, children, chunkGenerator)
{
}
public TemplateBlock(IParentChunkGenerator chunkGenerator, params SyntaxTreeNode[] children)
: this(chunkGenerator, (IEnumerable<SyntaxTreeNode>)children)
: this(chunkGenerator, (IReadOnlyList<SyntaxTreeNode>)children)
{
}
@ -261,7 +236,7 @@ namespace Microsoft.AspNet.Razor.Test.Framework
{
}
public TemplateBlock(IEnumerable<SyntaxTreeNode> children)
public TemplateBlock(IReadOnlyList<SyntaxTreeNode> children)
: this(new TemplateBlockChunkGenerator(), children)
{
}
@ -271,13 +246,13 @@ namespace Microsoft.AspNet.Razor.Test.Framework
{
private const BlockType ThisBlockType = BlockType.Comment;
public CommentBlock(IParentChunkGenerator chunkGenerator, IEnumerable<SyntaxTreeNode> children)
public CommentBlock(IParentChunkGenerator chunkGenerator, IReadOnlyList<SyntaxTreeNode> children)
: base(ThisBlockType, children, chunkGenerator)
{
}
public CommentBlock(IParentChunkGenerator chunkGenerator, params SyntaxTreeNode[] children)
: this(chunkGenerator, (IEnumerable<SyntaxTreeNode>)children)
: this(chunkGenerator, (IReadOnlyList<SyntaxTreeNode>)children)
{
}
@ -286,7 +261,7 @@ namespace Microsoft.AspNet.Razor.Test.Framework
{
}
public CommentBlock(IEnumerable<SyntaxTreeNode> children)
public CommentBlock(IReadOnlyList<SyntaxTreeNode> children)
: this(new RazorCommentChunkGenerator(), children)
{
}

View File

@ -531,7 +531,7 @@ namespace Microsoft.AspNet.Razor.Test.Framework
private class IgnoreOutputBlock : Block
{
public IgnoreOutputBlock() : base(BlockType.Template, Enumerable.Empty<SyntaxTreeNode>(), null) { }
public IgnoreOutputBlock() : base(BlockType.Template, new SyntaxTreeNode[0], null) { }
}
}
}