Optimize allocations of List<ISymbol> and related

This commit is contained in:
Ryan Nowak 2016-01-14 16:57:53 -08:00
parent ffdf5d2827
commit 95ea4cc06f
3 changed files with 45 additions and 13 deletions

View File

@ -16,6 +16,7 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
public class Span : SyntaxTreeNode public class Span : SyntaxTreeNode
{ {
private static readonly int TypeHashCode = typeof(Span).GetHashCode(); private static readonly int TypeHashCode = typeof(Span).GetHashCode();
private string _content;
private SourceLocation _start; private SourceLocation _start;
public Span(SpanBuilder builder) public Span(SpanBuilder builder)
@ -24,7 +25,7 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
} }
public SpanKind Kind { get; protected set; } public SpanKind Kind { get; protected set; }
public IEnumerable<ISymbol> Symbols { get; protected set; } public IReadOnlyList<ISymbol> Symbols { get; protected set; }
// Allow test code to re-link spans // Allow test code to re-link spans
public Span Previous { get; protected internal set; } public Span Previous { get; protected internal set; }
@ -48,7 +49,25 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
get { return _start; } get { return _start; }
} }
public string Content { get; private set; } public string Content
{
get
{
if (_content == null)
{
var builder = new StringBuilder();
for (var i = 0; i < Symbols.Count; i++)
{
var symbol = Symbols[i];
builder.Append(symbol.Content);
}
_content = builder.ToString();
}
return _content;
}
}
public void Change(Action<SpanBuilder> changes) public void Change(Action<SpanBuilder> changes)
{ {
@ -59,19 +78,15 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
public void ReplaceWith(SpanBuilder builder) public void ReplaceWith(SpanBuilder builder)
{ {
Debug.Assert(!builder.Symbols.Any() || builder.Symbols.All(s => s != null));
Kind = builder.Kind; Kind = builder.Kind;
Symbols = builder.Symbols; Symbols = builder.Symbols;
EditHandler = builder.EditHandler; EditHandler = builder.EditHandler;
ChunkGenerator = builder.ChunkGenerator ?? SpanChunkGenerator.Null; ChunkGenerator = builder.ChunkGenerator ?? SpanChunkGenerator.Null;
_start = builder.Start; _start = builder.Start;
_content = null;
// Since we took references to the values in SpanBuilder, clear its references out // Since we took references to the values in SpanBuilder, clear its references out
builder.Reset(); builder.Reset();
// Calculate other properties
Content = Symbols.Aggregate(new StringBuilder(), (sb, sym) => sb.Append(sym.Content), sb => sb.ToString());
} }
/// <summary> /// <summary>

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using Microsoft.AspNet.Razor.Editor; using Microsoft.AspNet.Razor.Editor;
using Microsoft.AspNet.Razor.Chunks.Generators; using Microsoft.AspNet.Razor.Chunks.Generators;
@ -13,7 +12,7 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
{ {
public class SpanBuilder public class SpanBuilder
{ {
private IList<ISymbol> _symbols = new List<ISymbol>(); private List<ISymbol> _symbols;
private SourceLocationTracker _tracker = new SourceLocationTracker(); private SourceLocationTracker _tracker = new SourceLocationTracker();
public SpanBuilder(Span original) public SpanBuilder(Span original)
@ -31,11 +30,20 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
} }
public SourceLocation Start { get; set; } public SourceLocation Start { get; set; }
public SpanKind Kind { get; set; } public SpanKind Kind { get; set; }
public ReadOnlyCollection<ISymbol> Symbols public IReadOnlyList<ISymbol> Symbols
{ {
get { return new ReadOnlyCollection<ISymbol>(_symbols); } get
{
if (_symbols == null)
{
_symbols = new List<ISymbol>();
}
return _symbols;
}
} }
public SpanEditHandler EditHandler { get; set; } public SpanEditHandler EditHandler { get; set; }
@ -43,7 +51,10 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
public void Reset() public void Reset()
{ {
_symbols = new List<ISymbol>(); // Need to potentially allocate a new list because Span.ReplaceWith takes ownership
// of the original list.
_symbols = null;
EditHandler = SpanEditHandler.CreateDefault(s => Enumerable.Empty<ISymbol>()); EditHandler = SpanEditHandler.CreateDefault(s => Enumerable.Empty<ISymbol>());
ChunkGenerator = SpanChunkGenerator.Null; ChunkGenerator = SpanChunkGenerator.Null;
Start = SourceLocation.Zero; Start = SourceLocation.Zero;
@ -67,6 +78,11 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
return; return;
} }
if (_symbols == null)
{
_symbols = new List<ISymbol>();
}
if (_symbols.Count == 0) if (_symbols.Count == 0)
{ {
Start = symbol.Start; Start = symbol.Start;

View File

@ -647,8 +647,9 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
EnsureTagBlock(tagBlock); EnsureTagBlock(tagBlock);
var childSpan = (Span)tagBlock.Children.First(); var childSpan = (Span)tagBlock.Children.First();
// We grab the symbol that could be forward slash // We grab the symbol that could be forward slash
var relevantSymbol = (HtmlSymbol)childSpan.Symbols.Take(2).Last(); var relevantSymbol = (HtmlSymbol)childSpan.Symbols[childSpan.Symbols.Count == 1 ? 0 : 1];
return relevantSymbol.Type == HtmlSymbolType.ForwardSlash; return relevantSymbol.Type == HtmlSymbolType.ForwardSlash;
} }