From 6a4a9544a14ac272841a4cc4845de0319b66b6c8 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Thu, 14 Jan 2016 14:54:47 -0800 Subject: [PATCH] Eliminate extra List copies Changing to IReadOnlyList since we always want to support indexing. Replacing ToArray() with non-linq when needed, and with a static EmptyArray when not needed. Eliminates 50mb of list copies. --- .../Parser/CSharpLanguageCharacteristics.cs | 2 +- .../Parser/HtmlLanguageCharacteristics.cs | 2 +- .../Parser/LanguageCharacteristics.cs | 4 ++-- src/Microsoft.AspNet.Razor/RazorError.cs | 2 ++ .../Tokenizer/CSharpTokenizer.cs | 2 +- src/Microsoft.AspNet.Razor/Tokenizer/HtmlTokenizer.cs | 2 +- .../Tokenizer/Symbols/CSharpSymbol.cs | 8 ++++---- .../Tokenizer/Symbols/HtmlSymbol.cs | 8 ++++---- .../Tokenizer/Symbols/SymbolBase.cs | 4 ++-- src/Microsoft.AspNet.Razor/Tokenizer/Tokenizer.cs | 11 +++++++++-- .../Tokenizer/TokenizerLookaheadTest.cs | 2 +- 11 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/Microsoft.AspNet.Razor/Parser/CSharpLanguageCharacteristics.cs b/src/Microsoft.AspNet.Razor/Parser/CSharpLanguageCharacteristics.cs index 7234836202..e65210643c 100644 --- a/src/Microsoft.AspNet.Razor/Parser/CSharpLanguageCharacteristics.cs +++ b/src/Microsoft.AspNet.Razor/Parser/CSharpLanguageCharacteristics.cs @@ -80,7 +80,7 @@ namespace Microsoft.AspNet.Razor.Parser return new CSharpTokenizer(source); } - protected override CSharpSymbol CreateSymbol(SourceLocation location, string content, CSharpSymbolType type, IEnumerable errors) + protected override CSharpSymbol CreateSymbol(SourceLocation location, string content, CSharpSymbolType type, IReadOnlyList errors) { return new CSharpSymbol(location, content, type, errors); } diff --git a/src/Microsoft.AspNet.Razor/Parser/HtmlLanguageCharacteristics.cs b/src/Microsoft.AspNet.Razor/Parser/HtmlLanguageCharacteristics.cs index dfb1c5eed1..e3b5d5b90d 100644 --- a/src/Microsoft.AspNet.Razor/Parser/HtmlLanguageCharacteristics.cs +++ b/src/Microsoft.AspNet.Razor/Parser/HtmlLanguageCharacteristics.cs @@ -129,7 +129,7 @@ namespace Microsoft.AspNet.Razor.Parser } } - protected override HtmlSymbol CreateSymbol(SourceLocation location, string content, HtmlSymbolType type, IEnumerable errors) + protected override HtmlSymbol CreateSymbol(SourceLocation location, string content, HtmlSymbolType type, IReadOnlyList errors) { return new HtmlSymbol(location, content, type, errors); } diff --git a/src/Microsoft.AspNet.Razor/Parser/LanguageCharacteristics.cs b/src/Microsoft.AspNet.Razor/Parser/LanguageCharacteristics.cs index fb2bbba8e6..1ff17e16af 100644 --- a/src/Microsoft.AspNet.Razor/Parser/LanguageCharacteristics.cs +++ b/src/Microsoft.AspNet.Razor/Parser/LanguageCharacteristics.cs @@ -91,7 +91,7 @@ namespace Microsoft.AspNet.Razor.Parser public virtual Tuple SplitSymbol(TSymbol symbol, int splitAt, TSymbolType leftType) { - var left = CreateSymbol(symbol.Start, symbol.Content.Substring(0, splitAt), leftType, Enumerable.Empty()); + var left = CreateSymbol(symbol.Start, symbol.Content.Substring(0, splitAt), leftType, RazorError.EmptyArray); TSymbol right = null; if (splitAt < symbol.Content.Length) { @@ -107,6 +107,6 @@ namespace Microsoft.AspNet.Razor.Parser return type == KnownSymbolType.Unknown || !Equals(GetKnownSymbolType(type), GetKnownSymbolType(KnownSymbolType.Unknown)); } - protected abstract TSymbol CreateSymbol(SourceLocation location, string content, TSymbolType type, IEnumerable errors); + protected abstract TSymbol CreateSymbol(SourceLocation location, string content, TSymbolType type, IReadOnlyList errors); } } diff --git a/src/Microsoft.AspNet.Razor/RazorError.cs b/src/Microsoft.AspNet.Razor/RazorError.cs index d7abc3d4db..e1d2af0ae9 100644 --- a/src/Microsoft.AspNet.Razor/RazorError.cs +++ b/src/Microsoft.AspNet.Razor/RazorError.cs @@ -9,6 +9,8 @@ namespace Microsoft.AspNet.Razor { public class RazorError : IEquatable { + internal static readonly RazorError[] EmptyArray = new RazorError[0]; + internal const int DefaultErrorLength = 1; /// diff --git a/src/Microsoft.AspNet.Razor/Tokenizer/CSharpTokenizer.cs b/src/Microsoft.AspNet.Razor/Tokenizer/CSharpTokenizer.cs index bfc191b5de..3ae99e5f0d 100644 --- a/src/Microsoft.AspNet.Razor/Tokenizer/CSharpTokenizer.cs +++ b/src/Microsoft.AspNet.Razor/Tokenizer/CSharpTokenizer.cs @@ -72,7 +72,7 @@ namespace Microsoft.AspNet.Razor.Tokenizer get { return CSharpSymbolType.RazorCommentStar; } } - protected override CSharpSymbol CreateSymbol(SourceLocation start, string content, CSharpSymbolType type, IEnumerable errors) + protected override CSharpSymbol CreateSymbol(SourceLocation start, string content, CSharpSymbolType type, IReadOnlyList errors) { return new CSharpSymbol(start, content, type, errors); } diff --git a/src/Microsoft.AspNet.Razor/Tokenizer/HtmlTokenizer.cs b/src/Microsoft.AspNet.Razor/Tokenizer/HtmlTokenizer.cs index 74a3d784df..f91afa475e 100644 --- a/src/Microsoft.AspNet.Razor/Tokenizer/HtmlTokenizer.cs +++ b/src/Microsoft.AspNet.Razor/Tokenizer/HtmlTokenizer.cs @@ -59,7 +59,7 @@ namespace Microsoft.AspNet.Razor.Tokenizer } } - protected override HtmlSymbol CreateSymbol(SourceLocation start, string content, HtmlSymbolType type, IEnumerable errors) + protected override HtmlSymbol CreateSymbol(SourceLocation start, string content, HtmlSymbolType type, IReadOnlyList errors) { return new HtmlSymbol(start, content, type, errors); } diff --git a/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/CSharpSymbol.cs b/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/CSharpSymbol.cs index 4cc9acd2c6..b0b936ed4b 100644 --- a/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/CSharpSymbol.cs +++ b/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/CSharpSymbol.cs @@ -11,7 +11,7 @@ namespace Microsoft.AspNet.Razor.Tokenizer.Symbols { // Helper constructor public CSharpSymbol(int offset, int line, int column, string content, CSharpSymbolType type) - : this(new SourceLocation(offset, line, column), content, type, Enumerable.Empty()) + : this(new SourceLocation(offset, line, column), content, type, RazorError.EmptyArray) { if (content == null) { @@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Razor.Tokenizer.Symbols } public CSharpSymbol(SourceLocation start, string content, CSharpSymbolType type) - : this(start, content, type, Enumerable.Empty()) + : this(start, content, type, RazorError.EmptyArray) { if (content == null) { @@ -34,7 +34,7 @@ namespace Microsoft.AspNet.Razor.Tokenizer.Symbols int column, string content, CSharpSymbolType type, - IEnumerable errors) + IReadOnlyList errors) : base(new SourceLocation(offset, line, column), content, type, errors) { if (content == null) @@ -47,7 +47,7 @@ namespace Microsoft.AspNet.Razor.Tokenizer.Symbols SourceLocation start, string content, CSharpSymbolType type, - IEnumerable errors) + IReadOnlyList errors) : base(start, content, type, errors) { if (content == null) diff --git a/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/HtmlSymbol.cs b/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/HtmlSymbol.cs index e7b4c7e881..d34c6bda66 100644 --- a/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/HtmlSymbol.cs +++ b/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/HtmlSymbol.cs @@ -11,7 +11,7 @@ namespace Microsoft.AspNet.Razor.Tokenizer.Symbols { // Helper constructor public HtmlSymbol(int offset, int line, int column, string content, HtmlSymbolType type) - : this(new SourceLocation(offset, line, column), content, type, Enumerable.Empty()) + : this(new SourceLocation(offset, line, column), content, type, RazorError.EmptyArray) { if (content == null) { @@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Razor.Tokenizer.Symbols } public HtmlSymbol(SourceLocation start, string content, HtmlSymbolType type) - : base(start, content, type, Enumerable.Empty()) + : base(start, content, type, RazorError.EmptyArray) { if (content == null) { @@ -34,7 +34,7 @@ namespace Microsoft.AspNet.Razor.Tokenizer.Symbols int column, string content, HtmlSymbolType type, - IEnumerable errors) + IReadOnlyList errors) : base(new SourceLocation(offset, line, column), content, type, errors) { if (content == null) @@ -47,7 +47,7 @@ namespace Microsoft.AspNet.Razor.Tokenizer.Symbols SourceLocation start, string content, HtmlSymbolType type, - IEnumerable errors) + IReadOnlyList errors) : base(start, content, type, errors) { if (content == null) diff --git a/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/SymbolBase.cs b/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/SymbolBase.cs index 76cb0664fb..70760014c1 100644 --- a/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/SymbolBase.cs +++ b/src/Microsoft.AspNet.Razor/Tokenizer/Symbols/SymbolBase.cs @@ -15,7 +15,7 @@ namespace Microsoft.AspNet.Razor.Tokenizer.Symbols SourceLocation start, string content, TType type, - IEnumerable errors) + IReadOnlyList errors) { if (content == null) { @@ -32,7 +32,7 @@ namespace Microsoft.AspNet.Razor.Tokenizer.Symbols public string Content { get; } - public IEnumerable Errors { get; } + public IReadOnlyList Errors { get; } public TType Type { get; } diff --git a/src/Microsoft.AspNet.Razor/Tokenizer/Tokenizer.cs b/src/Microsoft.AspNet.Razor/Tokenizer/Tokenizer.cs index 460f17d662..9b23e855a9 100644 --- a/src/Microsoft.AspNet.Razor/Tokenizer/Tokenizer.cs +++ b/src/Microsoft.AspNet.Razor/Tokenizer/Tokenizer.cs @@ -90,7 +90,7 @@ namespace Microsoft.AspNet.Razor.Tokenizer CurrentState = StartState; } - protected abstract TSymbol CreateSymbol(SourceLocation start, string content, TSymbolType type, IEnumerable errors); + protected abstract TSymbol CreateSymbol(SourceLocation start, string content, TSymbolType type, IReadOnlyList errors); protected TSymbol Single(TSymbolType type) { @@ -115,7 +115,14 @@ namespace Microsoft.AspNet.Razor.Tokenizer TSymbol sym = null; if (HaveContent) { - sym = CreateSymbol(start, Buffer.ToString(), type, CurrentErrors.ToArray()); + // Perf: Don't allocate a new errors array unless necessary. + var errors = CurrentErrors.Count == 0 ? RazorError.EmptyArray : new RazorError[CurrentErrors.Count]; + for (var i = 0; i < CurrentErrors.Count; i++) + { + errors[i] = CurrentErrors[i]; + } + + sym = CreateSymbol(start, Buffer.ToString(), type, errors); } StartSymbol(); return sym; diff --git a/test/Microsoft.AspNet.Razor.Test/Tokenizer/TokenizerLookaheadTest.cs b/test/Microsoft.AspNet.Razor.Test/Tokenizer/TokenizerLookaheadTest.cs index abdf02f942..a3292f7e7e 100644 --- a/test/Microsoft.AspNet.Razor.Test/Tokenizer/TokenizerLookaheadTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Tokenizer/TokenizerLookaheadTest.cs @@ -138,7 +138,7 @@ namespace Microsoft.AspNet.Razor.Test.Tokenizer SourceLocation start, string content, CSharpSymbolType type, - IEnumerable errors) + IReadOnlyList errors) { throw new NotImplementedException(); }