Merge pull request #22877 from dotnet/dev/toddgrun/MorePerfOptimizations

Dev/toddgrun/more perf optimizations
This commit is contained in:
Todd Grunke 2020-06-12 13:25:31 -07:00 committed by GitHub
commit 073cd0aa40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 92 additions and 77 deletions

View File

@ -554,13 +554,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return Transition(CSharpTokenizerState.Data, EndToken(SyntaxKind.StringLiteral)); return Transition(CSharpTokenizerState.Data, EndToken(SyntaxKind.StringLiteral));
} }
private StateResult QuotedCharacterLiteral() => QuotedLiteral('\'', SyntaxKind.CharacterLiteral); private StateResult QuotedCharacterLiteral() => QuotedLiteral('\'', IsEndQuotedCharacterLiteral, SyntaxKind.CharacterLiteral);
private StateResult QuotedStringLiteral() => QuotedLiteral('\"', SyntaxKind.StringLiteral); private StateResult QuotedStringLiteral() => QuotedLiteral('\"', IsEndQuotedStringLiteral, SyntaxKind.StringLiteral);
private StateResult QuotedLiteral(char quote, SyntaxKind literalType) private Func<char, bool> IsEndQuotedCharacterLiteral = (c) => c == '\\' || c == '\'' || ParserHelpers.IsNewLine(c);
private Func<char, bool> IsEndQuotedStringLiteral = (c) => c == '\\' || c == '\"' || ParserHelpers.IsNewLine(c);
private StateResult QuotedLiteral(char quote, Func<char, bool> isEndQuotedLiteral, SyntaxKind literalType)
{ {
TakeUntil(c => c == '\\' || c == quote || ParserHelpers.IsNewLine(c)); TakeUntil(isEndQuotedLiteral);
if (CurrentCharacter == '\\') if (CurrentCharacter == '\\')
{ {
TakeCurrent(); // Take the '\' TakeCurrent(); // Take the '\'

View File

@ -13,7 +13,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private readonly IList<TextLine> _lines; private readonly IList<TextLine> _lines;
private readonly string _filePath; private readonly string _filePath;
private TextLine _currentLine; private TextLine _currentLine;
private TextLine _endLine;
public LineTrackingStringBuffer(string content, string filePath) public LineTrackingStringBuffer(string content, string filePath)
: this(content.ToCharArray(), filePath) : this(content.ToCharArray(), filePath)
@ -22,17 +21,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public LineTrackingStringBuffer(char[] content, string filePath) public LineTrackingStringBuffer(char[] content, string filePath)
{ {
_endLine = new TextLine(0, 0); _lines = new List<TextLine>();
_lines = new List<TextLine>() { _endLine };
Append(content); BuildTextLines(content);
_filePath = filePath; _filePath = filePath;
} }
public int Length public int Length
{ {
get { return _endLine.End; } get { return _lines[_lines.Count - 1].End; }
} }
public SourceLocation EndLocation public SourceLocation EndLocation
@ -43,7 +41,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public CharacterReference CharAt(int absoluteIndex) public CharacterReference CharAt(int absoluteIndex)
{ {
var line = FindLine(absoluteIndex); var line = FindLine(absoluteIndex);
if (line == null) if (line.IsDefault)
{ {
throw new ArgumentOutOfRangeException(nameof(absoluteIndex)); throw new ArgumentOutOfRangeException(nameof(absoluteIndex));
} }
@ -51,38 +49,38 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return new CharacterReference(line.Content[idx], new SourceLocation(_filePath, absoluteIndex, line.Index, idx)); return new CharacterReference(line.Content[idx], new SourceLocation(_filePath, absoluteIndex, line.Index, idx));
} }
private void Append(char[] content) private void BuildTextLines(char[] content)
{ {
string lineText;
var lineStart = 0;
for (int i = 0; i < content.Length; i++) for (int i = 0; i < content.Length; i++)
{ {
AppendCore(content[i]); if (ParserHelpers.IsNewLine(content[i]))
// \r on it's own: Start a new line, otherwise wait for \n
// Other Newline: Start a new line
if ((content[i] == '\r' && (i + 1 == content.Length || content[i + 1] != '\n')) || (content[i] != '\r' && ParserHelpers.IsNewLine(content[i])))
{ {
PushNewLine(); // \r on it's own: Start a new line, otherwise wait for \n
// Other Newline: Start a new line
if (content[i] == '\r' && i + 1 < content.Length && content[i + 1] == '\n')
{
i++;
}
lineText = new string(content, lineStart, (i - lineStart) + 1); // +1 to include the current char
_lines.Add(new TextLine(lineStart, _lines.Count, lineText));
lineStart = i + 1;
} }
} }
}
private void PushNewLine() lineText = new string(content, lineStart, content.Length - lineStart); // no +1 as content.Length points past the last char already
{ _lines.Add(new TextLine(lineStart, _lines.Count, lineText));
_endLine = new TextLine(_endLine.End, _endLine.Index + 1);
_lines.Add(_endLine);
}
private void AppendCore(char chr)
{
Debug.Assert(_lines.Count > 0);
_lines[_lines.Count - 1].Content.Append(chr);
} }
private TextLine FindLine(int absoluteIndex) private TextLine FindLine(int absoluteIndex)
{ {
TextLine selected = null; TextLine selected;
if (_currentLine == null) if (_currentLine.IsDefault)
{ {
// Scan from line 0 // Scan from line 0
selected = ScanLines(absoluteIndex, 0, _lines.Count); selected = ScanLines(absoluteIndex, 0, _lines.Count);
@ -104,6 +102,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
selected = ScanLines(absoluteIndex, _currentLine.Index, _lines.Count); selected = ScanLines(absoluteIndex, _currentLine.Index, _lines.Count);
} }
} }
else
{
selected = default;
}
} }
else if (absoluteIndex < _currentLine.Start) else if (absoluteIndex < _currentLine.Start)
{ {
@ -122,6 +124,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
selected = ScanLines(absoluteIndex, 0, _currentLine.Index); selected = ScanLines(absoluteIndex, 0, _currentLine.Index);
} }
} }
else
{
selected = default;
}
} }
else else
{ {
@ -129,7 +135,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
selected = _currentLine; selected = _currentLine;
} }
Debug.Assert(selected == null || selected.Contains(absoluteIndex)); Debug.Assert(selected.IsDefault || selected.Contains(absoluteIndex));
_currentLine = selected; _currentLine = selected;
return selected; return selected;
} }
@ -159,7 +165,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
} }
} }
return null; return default;
} }
internal struct CharacterReference internal struct CharacterReference
@ -175,28 +181,26 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public SourceLocation Location { get; } public SourceLocation Location { get; }
} }
private class TextLine private struct TextLine
{ {
private StringBuilder _content = new StringBuilder(); public TextLine(int start, int index, string content)
public TextLine(int start, int index)
{ {
Start = start; Start = start;
Index = index; Index = index;
Content = content;
} }
public StringBuilder Content public string Content { get; }
{
get { return _content; } public bool IsDefault => Content == null;
}
public int Length public int Length
{ {
get { return Content.Length; } get { return Content.Length; }
} }
public int Start { get; set; } public int Start { get; }
public int Index { get; set; } public int Index { get; }
public int End public int End
{ {

View File

@ -16,25 +16,25 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private readonly TokenizerView<TTokenizer> _tokenizer; private readonly TokenizerView<TTokenizer> _tokenizer;
private SyntaxListBuilder<SyntaxToken>? _tokenBuilder; private SyntaxListBuilder<SyntaxToken>? _tokenBuilder;
// Following four high traffic methods cached as using method groups would cause allocation on every invocation. // Following four high traffic methods cached as using method groups would cause allocation on every invocation.
protected static readonly Func<SyntaxToken, bool> IsSpacingToken = (token) => protected static readonly Func<SyntaxToken, bool> IsSpacingToken = (token) =>
{ {
return token.Kind == SyntaxKind.Whitespace; return token.Kind == SyntaxKind.Whitespace;
}; };
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLines = (token) => protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLines = (token) =>
{ {
return IsSpacingToken(token) || token.Kind == SyntaxKind.NewLine; return IsSpacingToken(token) || token.Kind == SyntaxKind.NewLine;
}; };
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingComments = (token) => protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingComments = (token) =>
{ {
return IsSpacingToken(token) || token.Kind == SyntaxKind.CSharpComment; return IsSpacingToken(token) || token.Kind == SyntaxKind.CSharpComment;
}; };
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLinesAndComments = (token) => protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLinesAndComments = (token) =>
{ {
return IsSpacingTokenIncludingNewLines(token) || token.Kind == SyntaxKind.CSharpComment; return IsSpacingTokenIncludingNewLines(token) || token.Kind == SyntaxKind.CSharpComment;
}; };
protected TokenizerBackedParser(LanguageCharacteristics<TTokenizer> language, ParserContext context) protected TokenizerBackedParser(LanguageCharacteristics<TTokenizer> language, ParserContext context)
@ -224,7 +224,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected internal bool NextIs(SyntaxKind type) protected internal bool NextIs(SyntaxKind type)
{ {
return NextIs(token => token != null && type == token.Kind); // Duplicated logic with NextIs(Func...) to prevent allocation
var cur = CurrentToken;
var result = false;
if (NextToken())
{
result = (type == CurrentToken.Kind);
PutCurrentBack();
}
PutBack(cur);
EnsureCurrent();
return result;
} }
protected internal bool NextIs(params SyntaxKind[] types) protected internal bool NextIs(params SyntaxKind[] types)
@ -235,21 +247,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected internal bool NextIs(Func<SyntaxToken, bool> condition) protected internal bool NextIs(Func<SyntaxToken, bool> condition)
{ {
var cur = CurrentToken; var cur = CurrentToken;
var result = false;
if (NextToken()) if (NextToken())
{ {
var result = condition(CurrentToken); result = condition(CurrentToken);
PutCurrentBack(); PutCurrentBack();
PutBack(cur);
EnsureCurrent();
return result;
}
else
{
PutBack(cur);
EnsureCurrent();
} }
return false; PutBack(cur);
EnsureCurrent();
return result;
} }
protected internal bool Was(SyntaxKind type) protected internal bool Was(SyntaxKind type)
@ -289,11 +297,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected internal IReadOnlyList<SyntaxToken> ReadWhile(Func<SyntaxToken, bool> condition) protected internal IReadOnlyList<SyntaxToken> ReadWhile(Func<SyntaxToken, bool> condition)
{ {
if (!EnsureCurrent() || !condition(CurrentToken)) if (!EnsureCurrent() || !condition(CurrentToken))
{ {
return Array.Empty<SyntaxToken>(); return Array.Empty<SyntaxToken>();
} }
var result = new List<SyntaxToken>(); var result = new List<SyntaxToken>();
do do
{ {