Small perf improvement in TokenizerBackedParser.Accept and ReadWhile. (dotnet/aspnetcore-tooling#1882)

In the razor perf typing test, Accept was showing 27 ms allocating enumerators. Additionally, modified ReadWhile to only allocate if it would return a non-empty collection (and to not use the complexity introduced by using yield enumerators)\n\nCommit migrated from 27a14af36a
This commit is contained in:
Todd Grunke 2020-05-11 10:28:31 -07:00 committed by GitHub
parent 485924edd2
commit a70de6b67b
3 changed files with 24 additions and 26 deletions

View File

@ -893,7 +893,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
} }
} }
protected bool TryParseDirective(in SyntaxListBuilder<RazorSyntaxNode> builder, IEnumerable<SyntaxToken> whitespace, CSharpTransitionSyntax transition, string directive) protected bool TryParseDirective(in SyntaxListBuilder<RazorSyntaxNode> builder, IReadOnlyList<SyntaxToken> whitespace, CSharpTransitionSyntax transition, string directive)
{ {
if (_directiveParserMap.TryGetValue(directive, out var handler)) if (_directiveParserMap.TryGetValue(directive, out var handler))
{ {
@ -1679,7 +1679,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
} }
} }
private bool TryParseKeyword(in SyntaxListBuilder<RazorSyntaxNode> builder, IEnumerable<SyntaxToken> whitespace, CSharpTransitionSyntax transition) private bool TryParseKeyword(in SyntaxListBuilder<RazorSyntaxNode> builder, IReadOnlyList<SyntaxToken> whitespace, CSharpTransitionSyntax transition)
{ {
var result = CSharpTokenizer.GetTokenKeyword(CurrentToken); var result = CSharpTokenizer.GetTokenKeyword(CurrentToken);
Debug.Assert(CurrentToken.Kind == SyntaxKind.Keyword && result.HasValue); Debug.Assert(CurrentToken.Kind == SyntaxKind.Keyword && result.HasValue);
@ -2387,7 +2387,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
} }
} }
private IEnumerable<SyntaxToken> SkipToNextImportantToken(in SyntaxListBuilder<RazorSyntaxNode> builder) private IReadOnlyList<SyntaxToken> SkipToNextImportantToken(in SyntaxListBuilder<RazorSyntaxNode> builder)
{ {
while (!EndOfFile) while (!EndOfFile)
{ {
@ -2406,7 +2406,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return whitespace; return whitespace;
} }
} }
return Enumerable.Empty<SyntaxToken>(); return Array.Empty<SyntaxToken>();
} }
private void DefaultSpanContextConfig(SpanContextBuilder spanContext) private void DefaultSpanContextConfig(SpanContextBuilder spanContext)

View File

@ -1085,9 +1085,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
} }
} }
private bool TryParseAttributeName(out IEnumerable<SyntaxToken> nameTokens) private bool TryParseAttributeName(out IReadOnlyList<SyntaxToken> nameTokens)
{ {
nameTokens = Enumerable.Empty<SyntaxToken>(); nameTokens = Array.Empty<SyntaxToken>();
// //
// We are currently here <input |name="..." /> // We are currently here <input |name="..." />
// If we encounter a transition (@) here, it can be parsed as CSharp or Markup depending on the feature flag. // If we encounter a transition (@) here, it can be parsed as CSharp or Markup depending on the feature flag.

View File

@ -266,14 +266,22 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return true; return true;
} }
protected internal IEnumerable<SyntaxToken> ReadWhile(params SyntaxKind[] types) protected internal IReadOnlyList<SyntaxToken> ReadWhile(Func<SyntaxToken, bool> condition)
{ {
return ReadWhile(token => types.Any(expected => expected == token.Kind)); if (!EnsureCurrent() || !condition(CurrentToken))
} {
return Array.Empty<SyntaxToken>();
}
var result = new List<SyntaxToken>();
do
{
result.Add(CurrentToken);
NextToken();
}
while (EnsureCurrent() && condition(CurrentToken));
protected internal IEnumerable<SyntaxToken> ReadWhile(Func<SyntaxToken, bool> condition) return result;
{
return ReadWhileLazy(condition).ToList();
} }
protected bool AtIdentifier(bool allowKeywords) protected bool AtIdentifier(bool allowKeywords)
@ -283,17 +291,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
(allowKeywords && Language.IsKeyword(CurrentToken))); (allowKeywords && Language.IsKeyword(CurrentToken)));
} }
// Don't open this to sub classes because it's lazy but it looks eager.
// You have to advance the Enumerable to read the next characters.
internal IEnumerable<SyntaxToken> ReadWhileLazy(Func<SyntaxToken, bool> condition)
{
while (EnsureCurrent() && condition(CurrentToken))
{
yield return CurrentToken;
NextToken();
}
}
protected RazorCommentBlockSyntax ParseRazorComment() protected RazorCommentBlockSyntax ParseRazorComment()
{ {
if (!Language.KnowsTokenType(KnownTokenType.CommentStart) || if (!Language.KnowsTokenType(KnownTokenType.CommentStart) ||
@ -430,13 +427,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected internal void AcceptWhile(Func<SyntaxToken, bool> condition) protected internal void AcceptWhile(Func<SyntaxToken, bool> condition)
{ {
Accept(ReadWhileLazy(condition)); Accept(ReadWhile(condition));
} }
protected internal void Accept(IEnumerable<SyntaxToken> tokens) protected internal void Accept(IReadOnlyList<SyntaxToken> tokens)
{ {
foreach (var token in tokens) for(int i = 0; i < tokens.Count; i++)
{ {
var token = tokens[i];
Accept(token); Accept(token);
} }
} }