Skip to content

Commit a70de6b

Browse files
authored
Small perf improvement in TokenizerBackedParser.Accept and ReadWhile. (dotnet/razor#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 dotnet/razor@27a14af
1 parent 485924e commit a70de6b

File tree

3 files changed

+25
-27
lines changed

3 files changed

+25
-27
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,7 @@ private void ParseTemplate(in SyntaxListBuilder<RazorSyntaxNode> builder)
893893
}
894894
}
895895

896-
protected bool TryParseDirective(in SyntaxListBuilder<RazorSyntaxNode> builder, IEnumerable<SyntaxToken> whitespace, CSharpTransitionSyntax transition, string directive)
896+
protected bool TryParseDirective(in SyntaxListBuilder<RazorSyntaxNode> builder, IReadOnlyList<SyntaxToken> whitespace, CSharpTransitionSyntax transition, string directive)
897897
{
898898
if (_directiveParserMap.TryGetValue(directive, out var handler))
899899
{
@@ -1679,7 +1679,7 @@ private void ParseDirectiveBlock(in SyntaxListBuilder<RazorSyntaxNode> builder,
16791679
}
16801680
}
16811681

1682-
private bool TryParseKeyword(in SyntaxListBuilder<RazorSyntaxNode> builder, IEnumerable<SyntaxToken> whitespace, CSharpTransitionSyntax transition)
1682+
private bool TryParseKeyword(in SyntaxListBuilder<RazorSyntaxNode> builder, IReadOnlyList<SyntaxToken> whitespace, CSharpTransitionSyntax transition)
16831683
{
16841684
var result = CSharpTokenizer.GetTokenKeyword(CurrentToken);
16851685
Debug.Assert(CurrentToken.Kind == SyntaxKind.Keyword && result.HasValue);
@@ -2387,7 +2387,7 @@ protected void CompleteBlock(bool insertMarkerIfNecessary, bool captureWhitespac
23872387
}
23882388
}
23892389

2390-
private IEnumerable<SyntaxToken> SkipToNextImportantToken(in SyntaxListBuilder<RazorSyntaxNode> builder)
2390+
private IReadOnlyList<SyntaxToken> SkipToNextImportantToken(in SyntaxListBuilder<RazorSyntaxNode> builder)
23912391
{
23922392
while (!EndOfFile)
23932393
{
@@ -2406,7 +2406,7 @@ private IEnumerable<SyntaxToken> SkipToNextImportantToken(in SyntaxListBuilder<R
24062406
return whitespace;
24072407
}
24082408
}
2409-
return Enumerable.Empty<SyntaxToken>();
2409+
return Array.Empty<SyntaxToken>();
24102410
}
24112411

24122412
private void DefaultSpanContextConfig(SpanContextBuilder spanContext)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,9 +1085,9 @@ private void ParseAttribute(in SyntaxListBuilder<RazorSyntaxNode> builder)
10851085
}
10861086
}
10871087

1088-
private bool TryParseAttributeName(out IEnumerable<SyntaxToken> nameTokens)
1088+
private bool TryParseAttributeName(out IReadOnlyList<SyntaxToken> nameTokens)
10891089
{
1090-
nameTokens = Enumerable.Empty<SyntaxToken>();
1090+
nameTokens = Array.Empty<SyntaxToken>();
10911091
//
10921092
// We are currently here <input |name="..." />
10931093
// If we encounter a transition (@) here, it can be parsed as CSharp or Markup depending on the feature flag.

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

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -266,14 +266,22 @@ protected bool EnsureCurrent()
266266
return true;
267267
}
268268

269-
protected internal IEnumerable<SyntaxToken> ReadWhile(params SyntaxKind[] types)
270-
{
271-
return ReadWhile(token => types.Any(expected => expected == token.Kind));
272-
}
269+
protected internal IReadOnlyList<SyntaxToken> ReadWhile(Func<SyntaxToken, bool> condition)
270+
{
271+
if (!EnsureCurrent() || !condition(CurrentToken))
272+
{
273+
return Array.Empty<SyntaxToken>();
274+
}
275+
276+
var result = new List<SyntaxToken>();
277+
do
278+
{
279+
result.Add(CurrentToken);
280+
NextToken();
281+
}
282+
while (EnsureCurrent() && condition(CurrentToken));
273283

274-
protected internal IEnumerable<SyntaxToken> ReadWhile(Func<SyntaxToken, bool> condition)
275-
{
276-
return ReadWhileLazy(condition).ToList();
284+
return result;
277285
}
278286

279287
protected bool AtIdentifier(bool allowKeywords)
@@ -283,17 +291,6 @@ protected bool AtIdentifier(bool allowKeywords)
283291
(allowKeywords && Language.IsKeyword(CurrentToken)));
284292
}
285293

286-
// Don't open this to sub classes because it's lazy but it looks eager.
287-
// You have to advance the Enumerable to read the next characters.
288-
internal IEnumerable<SyntaxToken> ReadWhileLazy(Func<SyntaxToken, bool> condition)
289-
{
290-
while (EnsureCurrent() && condition(CurrentToken))
291-
{
292-
yield return CurrentToken;
293-
NextToken();
294-
}
295-
}
296-
297294
protected RazorCommentBlockSyntax ParseRazorComment()
298295
{
299296
if (!Language.KnowsTokenType(KnownTokenType.CommentStart) ||
@@ -430,13 +427,14 @@ protected internal void AcceptUntil(params SyntaxKind[] types)
430427

431428
protected internal void AcceptWhile(Func<SyntaxToken, bool> condition)
432429
{
433-
Accept(ReadWhileLazy(condition));
430+
Accept(ReadWhile(condition));
434431
}
435432

436-
protected internal void Accept(IEnumerable<SyntaxToken> tokens)
433+
protected internal void Accept(IReadOnlyList<SyntaxToken> tokens)
437434
{
438-
foreach (var token in tokens)
435+
for(int i = 0; i < tokens.Count; i++)
439436
{
437+
var token = tokens[i];
440438
Accept(token);
441439
}
442440
}

0 commit comments

Comments
 (0)