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
This commit is contained in:
Todd Grunke 2020-06-04 16:56:50 -07:00 committed by GitHub
parent c329dc555a
commit da52d6b636
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 30 additions and 35 deletions

View File

@ -16,27 +16,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
'@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'', '*'
});
// Following four high traffic methods cached as using method groups would cause allocation on every invocation.
protected static readonly Func<SyntaxToken, bool> IsSpacingToken = (token) =>
{
return token.Kind == SyntaxKind.Whitespace;
};
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLines = (token) =>
{
return IsSpacingToken(token) || token.Kind == SyntaxKind.NewLine;
};
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingComments = (token) =>
{
return IsSpacingToken(token) || token.Kind == SyntaxKind.CSharpComment;
};
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLinesAndComments = (token) =>
{
return IsSpacingTokenIncludingNewLines(token) || token.Kind == SyntaxKind.CSharpComment;
};
private static readonly Func<SyntaxToken, bool> IsValidStatementSpacingToken =
IsSpacingTokenIncludingNewLinesAndComments;

View File

@ -128,7 +128,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return null;
}
AcceptWhile(IsSpacingToken(includeNewLines: true));
AcceptWhile(IsSpacingTokenIncludingNewLines);
builder.Add(OutputAsMarkupLiteral());
if (At(SyntaxKind.OpenAngle))
@ -683,7 +683,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
var bookmark = CurrentStart.AbsoluteIndex;
// Skip whitespace
ReadWhile(IsSpacingToken(includeNewLines: true));
ReadWhile(IsSpacingTokenIncludingNewLines);
// Open Angle
if (At(SyntaxKind.OpenAngle) && NextIs(SyntaxKind.ForwardSlash))
@ -754,7 +754,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
SyntaxToken forwardSlashToken = null;
SyntaxToken closeAngleToken = null;
AcceptWhile(IsSpacingToken(includeNewLines: false));
AcceptWhile(IsSpacingToken);
miscAttributeContentBuilder.Add(OutputAsMarkupLiteral());
if (At(SyntaxKind.CloseAngle) ||
@ -1442,7 +1442,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private void ParseDoubleTransition(in SyntaxListBuilder<RazorSyntaxNode> builder)
{
AcceptWhile(IsSpacingToken(includeNewLines: true));
AcceptWhile(IsSpacingTokenIncludingNewLines);
builder.Add(OutputAsMarkupLiteral());
// First transition
@ -1463,7 +1463,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// We don't want to write it to output.
Context.NullGenerateWhitespaceAndNewLine = false;
SpanContext.ChunkGenerator = SpanChunkGenerator.Null;
AcceptWhile(IsSpacingToken(includeNewLines: false));
AcceptWhile(IsSpacingToken);
if (At(SyntaxKind.NewLine))
{
AcceptAndMoveNext();
@ -1549,7 +1549,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// We don't want to write it to output.
Context.NullGenerateWhitespaceAndNewLine = false;
SpanContext.ChunkGenerator = SpanChunkGenerator.Null;
AcceptWhile(IsSpacingToken(includeNewLines: false));
AcceptWhile(IsSpacingToken);
if (At(SyntaxKind.NewLine))
{
AcceptAndMoveNext();
@ -1596,7 +1596,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
(At(SyntaxKind.NewLine) ||
(At(SyntaxKind.Whitespace) && NextIs(SyntaxKind.NewLine))))
{
AcceptWhile(IsSpacingToken(includeNewLines: false));
AcceptWhile(IsSpacingToken);
AcceptAndMoveNext();
SpanContext.ChunkGenerator = SpanChunkGenerator.Null;
builder.Add(OutputAsMarkupEphemeralLiteral());
@ -1611,7 +1611,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// We don't want to write it to output.
Context.NullGenerateWhitespaceAndNewLine = false;
SpanContext.ChunkGenerator = SpanChunkGenerator.Null;
AcceptWhile(IsSpacingToken(includeNewLines: false));
AcceptWhile(IsSpacingToken);
if (At(SyntaxKind.NewLine))
{
AcceptAndMoveNext();
@ -1620,7 +1620,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
builder.Add(OutputAsMarkupEphemeralLiteral());
}
AcceptWhile(IsSpacingToken(includeNewLines: true));
AcceptWhile(IsSpacingTokenIncludingNewLines);
}
private bool ScriptTagExpectsHtml(MarkupStartTagSyntax tagBlock)
@ -2111,11 +2111,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return false;
}
protected static Func<SyntaxToken, bool> IsSpacingToken(bool includeNewLines)
{
return token => token.Kind == SyntaxKind.Whitespace || (includeNewLines && token.Kind == SyntaxKind.NewLine);
}
internal static bool IsHyphen(SyntaxToken token)
{
return token.Kind == SyntaxKind.Text && token.Content == "-";

View File

@ -16,6 +16,27 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private readonly TokenizerView<TTokenizer> _tokenizer;
private SyntaxListBuilder<SyntaxToken>? _tokenBuilder;
// Following four high traffic methods cached as using method groups would cause allocation on every invocation.
protected static readonly Func<SyntaxToken, bool> IsSpacingToken = (token) =>
{
return token.Kind == SyntaxKind.Whitespace;
};
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLines = (token) =>
{
return IsSpacingToken(token) || token.Kind == SyntaxKind.NewLine;
};
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingComments = (token) =>
{
return IsSpacingToken(token) || token.Kind == SyntaxKind.CSharpComment;
};
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLinesAndComments = (token) =>
{
return IsSpacingTokenIncludingNewLines(token) || token.Kind == SyntaxKind.CSharpComment;
};
protected TokenizerBackedParser(LanguageCharacteristics<TTokenizer> language, ParserContext context)
: base(context)
{