Skip to content

Commit da52d6b

Browse files
authored
Move IsSpacingToken perf optimizations down to base class (#22555)
This gives the HtmlMarkupParser the same non-allocating behavior for it's IsSpacing* method invocations. Profile from following feedback ticket showed ~100ms time spent doing these allocations: https://developercommunity.visualstudio.com/content/problem/1006207/vs-2019-constant-freezing-please-wait-for-an-edito.html?childToView=1065769
1 parent c329dc5 commit da52d6b

File tree

3 files changed

+30
-35
lines changed

3 files changed

+30
-35
lines changed

src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/CSharpCodeParser.cs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,6 @@ internal class CSharpCodeParser : TokenizerBackedParser<CSharpTokenizer>
1616
'@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'', '*'
1717
});
1818

19-
// Following four high traffic methods cached as using method groups would cause allocation on every invocation.
20-
protected static readonly Func<SyntaxToken, bool> IsSpacingToken = (token) =>
21-
{
22-
return token.Kind == SyntaxKind.Whitespace;
23-
};
24-
25-
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLines = (token) =>
26-
{
27-
return IsSpacingToken(token) || token.Kind == SyntaxKind.NewLine;
28-
};
29-
30-
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingComments = (token) =>
31-
{
32-
return IsSpacingToken(token) || token.Kind == SyntaxKind.CSharpComment;
33-
};
34-
35-
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLinesAndComments = (token) =>
36-
{
37-
return IsSpacingTokenIncludingNewLines(token) || token.Kind == SyntaxKind.CSharpComment;
38-
};
39-
4019
private static readonly Func<SyntaxToken, bool> IsValidStatementSpacingToken =
4120
IsSpacingTokenIncludingNewLinesAndComments;
4221

src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/HtmlMarkupParser.cs

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public MarkupBlockSyntax ParseBlock()
128128
return null;
129129
}
130130

131-
AcceptWhile(IsSpacingToken(includeNewLines: true));
131+
AcceptWhile(IsSpacingTokenIncludingNewLines);
132132
builder.Add(OutputAsMarkupLiteral());
133133

134134
if (At(SyntaxKind.OpenAngle))
@@ -683,7 +683,7 @@ private MarkupStartTagSyntax ParseStartTag(
683683
var bookmark = CurrentStart.AbsoluteIndex;
684684

685685
// Skip whitespace
686-
ReadWhile(IsSpacingToken(includeNewLines: true));
686+
ReadWhile(IsSpacingTokenIncludingNewLines);
687687

688688
// Open Angle
689689
if (At(SyntaxKind.OpenAngle) && NextIs(SyntaxKind.ForwardSlash))
@@ -754,7 +754,7 @@ private MarkupStartTagSyntax ParseStartTextTag(SyntaxToken openAngleToken, out M
754754
SyntaxToken forwardSlashToken = null;
755755
SyntaxToken closeAngleToken = null;
756756

757-
AcceptWhile(IsSpacingToken(includeNewLines: false));
757+
AcceptWhile(IsSpacingToken);
758758
miscAttributeContentBuilder.Add(OutputAsMarkupLiteral());
759759

760760
if (At(SyntaxKind.CloseAngle) ||
@@ -1442,7 +1442,7 @@ private bool ParseCData(in SyntaxListBuilder<RazorSyntaxNode> builder)
14421442

14431443
private void ParseDoubleTransition(in SyntaxListBuilder<RazorSyntaxNode> builder)
14441444
{
1445-
AcceptWhile(IsSpacingToken(includeNewLines: true));
1445+
AcceptWhile(IsSpacingTokenIncludingNewLines);
14461446
builder.Add(OutputAsMarkupLiteral());
14471447

14481448
// First transition
@@ -1463,7 +1463,7 @@ private void ParseCodeTransition(in SyntaxListBuilder<RazorSyntaxNode> builder)
14631463
// We don't want to write it to output.
14641464
Context.NullGenerateWhitespaceAndNewLine = false;
14651465
SpanContext.ChunkGenerator = SpanChunkGenerator.Null;
1466-
AcceptWhile(IsSpacingToken(includeNewLines: false));
1466+
AcceptWhile(IsSpacingToken);
14671467
if (At(SyntaxKind.NewLine))
14681468
{
14691469
AcceptAndMoveNext();
@@ -1549,7 +1549,7 @@ private void ParseRazorCommentWithLeadingAndTrailingWhitespace(in SyntaxListBuil
15491549
// We don't want to write it to output.
15501550
Context.NullGenerateWhitespaceAndNewLine = false;
15511551
SpanContext.ChunkGenerator = SpanChunkGenerator.Null;
1552-
AcceptWhile(IsSpacingToken(includeNewLines: false));
1552+
AcceptWhile(IsSpacingToken);
15531553
if (At(SyntaxKind.NewLine))
15541554
{
15551555
AcceptAndMoveNext();
@@ -1596,7 +1596,7 @@ private void ParseRazorCommentWithLeadingAndTrailingWhitespace(in SyntaxListBuil
15961596
(At(SyntaxKind.NewLine) ||
15971597
(At(SyntaxKind.Whitespace) && NextIs(SyntaxKind.NewLine))))
15981598
{
1599-
AcceptWhile(IsSpacingToken(includeNewLines: false));
1599+
AcceptWhile(IsSpacingToken);
16001600
AcceptAndMoveNext();
16011601
SpanContext.ChunkGenerator = SpanChunkGenerator.Null;
16021602
builder.Add(OutputAsMarkupEphemeralLiteral());
@@ -1611,7 +1611,7 @@ private void ParseMisc(in SyntaxListBuilder<RazorSyntaxNode> builder)
16111611
// We don't want to write it to output.
16121612
Context.NullGenerateWhitespaceAndNewLine = false;
16131613
SpanContext.ChunkGenerator = SpanChunkGenerator.Null;
1614-
AcceptWhile(IsSpacingToken(includeNewLines: false));
1614+
AcceptWhile(IsSpacingToken);
16151615
if (At(SyntaxKind.NewLine))
16161616
{
16171617
AcceptAndMoveNext();
@@ -1620,7 +1620,7 @@ private void ParseMisc(in SyntaxListBuilder<RazorSyntaxNode> builder)
16201620
builder.Add(OutputAsMarkupEphemeralLiteral());
16211621
}
16221622

1623-
AcceptWhile(IsSpacingToken(includeNewLines: true));
1623+
AcceptWhile(IsSpacingTokenIncludingNewLines);
16241624
}
16251625

16261626
private bool ScriptTagExpectsHtml(MarkupStartTagSyntax tagBlock)
@@ -2111,11 +2111,6 @@ internal static bool IsCommentContentEndingInvalid(IEnumerable<SyntaxToken> sequ
21112111
return false;
21122112
}
21132113

2114-
protected static Func<SyntaxToken, bool> IsSpacingToken(bool includeNewLines)
2115-
{
2116-
return token => token.Kind == SyntaxKind.Whitespace || (includeNewLines && token.Kind == SyntaxKind.NewLine);
2117-
}
2118-
21192114
internal static bool IsHyphen(SyntaxToken token)
21202115
{
21212116
return token.Kind == SyntaxKind.Text && token.Content == "-";

src/Razor/Microsoft.AspNetCore.Razor.Language/src/Legacy/TokenizerBackedParser.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,27 @@ internal abstract class TokenizerBackedParser<TTokenizer> : ParserBase
1616
private readonly TokenizerView<TTokenizer> _tokenizer;
1717
private SyntaxListBuilder<SyntaxToken>? _tokenBuilder;
1818

19+
// Following four high traffic methods cached as using method groups would cause allocation on every invocation.
20+
protected static readonly Func<SyntaxToken, bool> IsSpacingToken = (token) =>
21+
{
22+
return token.Kind == SyntaxKind.Whitespace;
23+
};
24+
25+
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLines = (token) =>
26+
{
27+
return IsSpacingToken(token) || token.Kind == SyntaxKind.NewLine;
28+
};
29+
30+
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingComments = (token) =>
31+
{
32+
return IsSpacingToken(token) || token.Kind == SyntaxKind.CSharpComment;
33+
};
34+
35+
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLinesAndComments = (token) =>
36+
{
37+
return IsSpacingTokenIncludingNewLines(token) || token.Kind == SyntaxKind.CSharpComment;
38+
};
39+
1940
protected TokenizerBackedParser(LanguageCharacteristics<TTokenizer> language, ParserContext context)
2041
: base(context)
2142
{

0 commit comments

Comments
 (0)