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) protected override bool CanRewrite(Block block)
{ {
var gen = block.ChunkGenerator as AttributeBlockChunkGenerator; 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) 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(), 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. // for identifying TagHelperDescriptors and rebuilding ParserResults.
tagHelperDescriptors: Enumerable.Empty<TagHelperDescriptor>(), tagHelperDescriptors: Enumerable.Empty<TagHelperDescriptor>(),
errorSink: _errorSink); errorSink: _errorSink);

View File

@ -14,9 +14,11 @@ namespace Microsoft.AspNet.Razor.Parser
public virtual void VisitBlock(Block block) public virtual void VisitBlock(Block block)
{ {
VisitStartBlock(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); VisitEndBlock(block);
} }

View File

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

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
} }
public BlockType? Type { get; set; } 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 IParentChunkGenerator ChunkGenerator { get; set; }
public virtual Block Build() public virtual Block Build()

View File

@ -302,8 +302,19 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
private IEnumerable<string> GetAttributeNames(Block tagBlock) private IEnumerable<string> GetAttributeNames(Block tagBlock)
{ {
// Need to calculate how many children we should take that represent the attributes. // Need to calculate how many children we should take that represent the attributes.
var childrenOffset = IsPartialTag(tagBlock) ? 1 : 2; var childrenOffset = IsPartialTag(tagBlock) ? 0 : 1;
var attributeChildren = tagBlock.Children.Skip(1).Take(tagBlock.Children.Count() - childrenOffset); 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>(); var attributeNames = new List<string>();
foreach (var child in attributeChildren) foreach (var child in attributeChildren)
@ -637,9 +648,9 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
private static string GetTagName(Block tagBlock) 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; return null;
} }

View File

@ -15,13 +15,13 @@ namespace Microsoft.AspNet.Razor.Test.Framework
{ {
private const BlockType ThisBlockType = BlockType.Statement; private const BlockType ThisBlockType = BlockType.Statement;
public StatementBlock(IParentChunkGenerator chunkGenerator, IEnumerable<SyntaxTreeNode> children) public StatementBlock(IParentChunkGenerator chunkGenerator, IReadOnlyList<SyntaxTreeNode> children)
: base(ThisBlockType, children, chunkGenerator) : base(ThisBlockType, children, chunkGenerator)
{ {
} }
public StatementBlock(IParentChunkGenerator chunkGenerator, params SyntaxTreeNode[] children) 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) : this(ParentChunkGenerator.Null, children)
{ {
} }
public StatementBlock(IEnumerable<SyntaxTreeNode> children)
: this(ParentChunkGenerator.Null, children)
{
}
} }
public class DirectiveBlock : Block public class DirectiveBlock : Block
{ {
private const BlockType ThisBlockType = BlockType.Directive; private const BlockType ThisBlockType = BlockType.Directive;
public DirectiveBlock(IParentChunkGenerator chunkGenerator, IEnumerable<SyntaxTreeNode> children) public DirectiveBlock(IParentChunkGenerator chunkGenerator, IReadOnlyList<SyntaxTreeNode> children)
: base(ThisBlockType, children, chunkGenerator) : base(ThisBlockType, children, chunkGenerator)
{ {
} }
public DirectiveBlock(IParentChunkGenerator chunkGenerator, params SyntaxTreeNode[] children) 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) : this(ParentChunkGenerator.Null, children)
{ {
} }
public DirectiveBlock(IEnumerable<SyntaxTreeNode> children)
: this(ParentChunkGenerator.Null, children)
{
}
} }
public class FunctionsBlock : Block public class FunctionsBlock : Block
{ {
private const BlockType ThisBlockType = BlockType.Functions; private const BlockType ThisBlockType = BlockType.Functions;
public FunctionsBlock(IParentChunkGenerator chunkGenerator, IEnumerable<SyntaxTreeNode> children) public FunctionsBlock(IParentChunkGenerator chunkGenerator, IReadOnlyList<SyntaxTreeNode> children)
: base(ThisBlockType, children, chunkGenerator) : base(ThisBlockType, children, chunkGenerator)
{ {
} }
public FunctionsBlock(IParentChunkGenerator chunkGenerator, params SyntaxTreeNode[] children) 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) : this(ParentChunkGenerator.Null, children)
{ {
} }
public FunctionsBlock(IEnumerable<SyntaxTreeNode> children)
: this(ParentChunkGenerator.Null, children)
{
}
} }
public class ExpressionBlock : Block public class ExpressionBlock : Block
{ {
private const BlockType ThisBlockType = BlockType.Expression; private const BlockType ThisBlockType = BlockType.Expression;
public ExpressionBlock(IParentChunkGenerator chunkGenerator, IEnumerable<SyntaxTreeNode> children) public ExpressionBlock(IParentChunkGenerator chunkGenerator, IReadOnlyList<SyntaxTreeNode> children)
: base(ThisBlockType, children, chunkGenerator) : base(ThisBlockType, children, chunkGenerator)
{ {
} }
public ExpressionBlock(IParentChunkGenerator chunkGenerator, params SyntaxTreeNode[] children) 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) : this(new ExpressionChunkGenerator(), children)
{ {
} }
public ExpressionBlock(IEnumerable<SyntaxTreeNode> children)
: this(new ExpressionChunkGenerator(), children)
{
}
} }
public class MarkupTagBlock : Block public class MarkupTagBlock : Block
@ -128,18 +108,18 @@ namespace Microsoft.AspNet.Razor.Test.Framework
public MarkupBlock( public MarkupBlock(
BlockType blockType, BlockType blockType,
IParentChunkGenerator chunkGenerator, IParentChunkGenerator chunkGenerator,
IEnumerable<SyntaxTreeNode> children) IReadOnlyList<SyntaxTreeNode> children)
: base(blockType, children, chunkGenerator) : base(blockType, children, chunkGenerator)
{ {
} }
public MarkupBlock(IParentChunkGenerator chunkGenerator, IEnumerable<SyntaxTreeNode> children) public MarkupBlock(IParentChunkGenerator chunkGenerator, IReadOnlyList<SyntaxTreeNode> children)
: this(ThisBlockType, chunkGenerator, children) : this(ThisBlockType, chunkGenerator, children)
{ {
} }
public MarkupBlock(IParentChunkGenerator chunkGenerator, params SyntaxTreeNode[] 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) : this(ParentChunkGenerator.Null, children)
{ {
} }
public MarkupBlock(IEnumerable<SyntaxTreeNode> children)
: this(ParentChunkGenerator.Null, children)
{
}
} }
public class MarkupTagHelperBlock : TagHelperBlock public class MarkupTagHelperBlock : TagHelperBlock
@ -221,13 +196,13 @@ namespace Microsoft.AspNet.Razor.Test.Framework
{ {
private const BlockType ThisBlockType = BlockType.Section; private const BlockType ThisBlockType = BlockType.Section;
public SectionBlock(IParentChunkGenerator chunkGenerator, IEnumerable<SyntaxTreeNode> children) public SectionBlock(IParentChunkGenerator chunkGenerator, IReadOnlyList<SyntaxTreeNode> children)
: base(ThisBlockType, children, chunkGenerator) : base(ThisBlockType, children, chunkGenerator)
{ {
} }
public SectionBlock(IParentChunkGenerator chunkGenerator, params SyntaxTreeNode[] children) 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) : this(ParentChunkGenerator.Null, children)
{ {
} }
@ -246,13 +221,13 @@ namespace Microsoft.AspNet.Razor.Test.Framework
{ {
private const BlockType ThisBlockType = BlockType.Template; private const BlockType ThisBlockType = BlockType.Template;
public TemplateBlock(IParentChunkGenerator chunkGenerator, IEnumerable<SyntaxTreeNode> children) public TemplateBlock(IParentChunkGenerator chunkGenerator, IReadOnlyList<SyntaxTreeNode> children)
: base(ThisBlockType, children, chunkGenerator) : base(ThisBlockType, children, chunkGenerator)
{ {
} }
public TemplateBlock(IParentChunkGenerator chunkGenerator, params SyntaxTreeNode[] children) 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) : this(new TemplateBlockChunkGenerator(), children)
{ {
} }
@ -271,13 +246,13 @@ namespace Microsoft.AspNet.Razor.Test.Framework
{ {
private const BlockType ThisBlockType = BlockType.Comment; private const BlockType ThisBlockType = BlockType.Comment;
public CommentBlock(IParentChunkGenerator chunkGenerator, IEnumerable<SyntaxTreeNode> children) public CommentBlock(IParentChunkGenerator chunkGenerator, IReadOnlyList<SyntaxTreeNode> children)
: base(ThisBlockType, children, chunkGenerator) : base(ThisBlockType, children, chunkGenerator)
{ {
} }
public CommentBlock(IParentChunkGenerator chunkGenerator, params SyntaxTreeNode[] children) 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) : this(new RazorCommentChunkGenerator(), children)
{ {
} }

View File

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