Fix perf issue in LineTrackingStringbuffer.ScanLines.
I noticed several hundred ms spent in this method from a customer profile. Primarilly, the method was doing a linear scan of all lines trying to find one that contained the requested position. I changed this to a binary search, but kept/improved the optimization around checking next/previous lines before instigating the search.
Note, there was also a bug where the old code did:
else if (absoluteIndex > _currentLine.Index && _currentLine.Index + 1 < _lines.Count)
but it should have been coparing absoluteIndex with _currentLine.Start
\n\nCommit migrated from 32a0f28708
This commit is contained in:
parent
7eca4ab2ec
commit
069b409dd4
|
|
@ -82,25 +82,51 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
|||
{
|
||||
TextLine selected = null;
|
||||
|
||||
if (_currentLine != null)
|
||||
{
|
||||
if (_currentLine.Contains(absoluteIndex))
|
||||
{
|
||||
// This index is on the last read line
|
||||
selected = _currentLine;
|
||||
}
|
||||
else if (absoluteIndex > _currentLine.Index && _currentLine.Index + 1 < _lines.Count)
|
||||
{
|
||||
// This index is ahead of the last read line
|
||||
selected = ScanLines(absoluteIndex, _currentLine.Index);
|
||||
}
|
||||
}
|
||||
|
||||
// Have we found a line yet?
|
||||
if (selected == null)
|
||||
if (_currentLine == null)
|
||||
{
|
||||
// Scan from line 0
|
||||
selected = ScanLines(absoluteIndex, 0);
|
||||
selected = ScanLines(absoluteIndex, 0, _lines.Count);
|
||||
}
|
||||
else if (absoluteIndex >= _currentLine.End)
|
||||
{
|
||||
if (_currentLine.Index + 1 < _lines.Count)
|
||||
{
|
||||
// This index is after the last read line
|
||||
var nextLine = _lines[_currentLine.Index + 1];
|
||||
|
||||
// Optimization to not search if it's the common case where the line after _currentLine is being requested.
|
||||
if (nextLine.Contains(absoluteIndex))
|
||||
{
|
||||
selected = nextLine;
|
||||
}
|
||||
else
|
||||
{
|
||||
selected = ScanLines(absoluteIndex, _currentLine.Index, _lines.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (absoluteIndex < _currentLine.Start)
|
||||
{
|
||||
if (_currentLine.Index > 0)
|
||||
{
|
||||
// This index is before the last read line
|
||||
var prevLine = _lines[_currentLine.Index - 1];
|
||||
|
||||
// Optimization to not search if it's the common case where the line before _currentLine is being requested.
|
||||
if (prevLine.Contains(absoluteIndex))
|
||||
{
|
||||
selected = prevLine;
|
||||
}
|
||||
else
|
||||
{
|
||||
selected = ScanLines(absoluteIndex, 0, _currentLine.Index);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This index is on the last read line
|
||||
selected = _currentLine;
|
||||
}
|
||||
|
||||
Debug.Assert(selected == null || selected.Contains(absoluteIndex));
|
||||
|
|
@ -108,18 +134,31 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
|||
return selected;
|
||||
}
|
||||
|
||||
private TextLine ScanLines(int absoluteIndex, int startPos)
|
||||
private TextLine ScanLines(int absoluteIndex, int startLineIndex, int endLineIndex)
|
||||
{
|
||||
for (int i = 0; i < _lines.Count; i++)
|
||||
{
|
||||
var idx = (i + startPos) % _lines.Count;
|
||||
Debug.Assert(idx >= 0 && idx < _lines.Count);
|
||||
// binary search for the line containing absoluteIndex
|
||||
var lowIndex = startLineIndex;
|
||||
var highIndex = _lines.Count;
|
||||
|
||||
if (_lines[idx].Contains(absoluteIndex))
|
||||
while (lowIndex != highIndex)
|
||||
{
|
||||
var midIndex = (lowIndex + highIndex) / 2;
|
||||
var midLine = _lines[midIndex];
|
||||
|
||||
if (absoluteIndex >= midLine.End)
|
||||
{
|
||||
return _lines[idx];
|
||||
lowIndex = midIndex + 1;
|
||||
}
|
||||
else if (absoluteIndex < midLine.Start)
|
||||
{
|
||||
highIndex = midIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
return midLine;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue