Skip to content

Commit 2fc58a5

Browse files
committed
Reduce TextDiffer allocations, including LOH
TextDiffer.ComputeDiff shows as 1.3% of allocations in the razor speedometer scrolling and typing test. There are two issues: 1) The IntArray class was using too large a page size when used in conjunction with System.Buffers.ArrayPool. It was indicating it wanted an 80KB buffer, and ArrayPool allocates it's pool entries on powers of 2, so it was giving back an 128KB array, which goes to the LOH. The fix is to request a 64 KB buffer. 2) The IntArrays were being constructed over unnecessarilly large ranges. If we instead prime the pump by detemining an initial changed extent, we can request significantly less allocated space during the typing scenario (reduces the requested array size from the buffer size to just the changed range, tyipcally only a couple characters) Reducing the size of the vf/vr arrays did require a redirection to the SourceEqual method in the derived classes, as they don't know of that concept.
1 parent c60ff69 commit 2fc58a5

File tree

3 files changed

+54
-19
lines changed

3 files changed

+54
-19
lines changed

src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TextDifferencing/TextDiffer.DiffEdit.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ protected readonly struct DiffEdit
1414
public int? NewTextPosition { get; }
1515
public int Length { get; }
1616

17-
private DiffEdit(DiffEditKind kind, int position, int? newTextPosition, int length)
17+
public DiffEdit(DiffEditKind kind, int position, int? newTextPosition, int length)
1818
{
1919
Kind = kind;
2020
Position = position;

src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TextDifferencing/TextDiffer.IntArray.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public void Deconstruct(out int[] array, out int start, out int length)
2828
=> (array, start, length) = (Array, Start, Length);
2929
}
3030

31-
private const int PageSize = 1024 * 80 / sizeof(int);
31+
private const int PageSize = 1024 * 64 / sizeof(int);
3232

3333
private Page _page;
3434
private readonly Page[] _pages;

src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/TextDifferencing/TextDiffer.cs

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,38 +18,53 @@ internal abstract partial class TextDiffer
1818
protected abstract int OldSourceLength { get; }
1919
protected abstract int NewSourceLength { get; }
2020

21+
private int _oldSourceOffset;
22+
private int _newSourceOffset;
23+
2124
protected abstract bool SourceEqual(int oldSourceIndex, int newSourceIndex);
2225

2326
protected List<DiffEdit> ComputeDiff()
2427
{
2528
var edits = new List<DiffEdit>(capacity: 4);
2629
var builder = new DiffEditBuilder(edits);
2730

31+
var lowA = 0;
32+
var highA = OldSourceLength;
33+
var lowB = 0;
34+
var highB = NewSourceLength;
35+
36+
// Determine the extent of the changes in both texts, as this will allow us
37+
// to limit the amount of memory needed in the vf/vr arrays. By doing this though,
38+
// we will need to adjust SourceEqual requests to use an appropriate offset.
39+
FindChangeExtent(ref lowA, ref highA, ref lowB, ref highB);
40+
41+
_oldSourceOffset = lowA;
42+
_newSourceOffset = lowB;
43+
44+
var oldSourceLength = highA - lowA;
45+
var newSourceLength = highB - lowB;
46+
2847
// Initialize the vectors to use for forward and reverse searches.
29-
var max = NewSourceLength + OldSourceLength;
48+
var max = newSourceLength + oldSourceLength;
3049
using var vf = new IntArray((2 * max) + 1);
3150
using var vr = new IntArray((2 * max) + 1);
3251

33-
ComputeDiffRecursive(builder, 0, OldSourceLength, 0, NewSourceLength, vf, vr);
52+
ComputeDiffRecursive(builder, 0, oldSourceLength, 0, newSourceLength, vf, vr);
53+
54+
// Update the resultant edits with the appropriate offsets
55+
for (var i = 0; i < edits.Count; i++)
56+
{
57+
var edit = edits[i];
58+
59+
edits[i] = new DiffEdit(edit.Kind, _oldSourceOffset + edit.Position, _newSourceOffset + edit.NewTextPosition, edit.Length);
60+
}
3461

3562
return edits;
3663
}
3764

3865
private void ComputeDiffRecursive(DiffEditBuilder edits, int lowA, int highA, int lowB, int highB, IntArray vf, IntArray vr)
3966
{
40-
while (lowA < highA && lowB < highB && SourceEqual(lowA, lowB))
41-
{
42-
// Skip equal text at the start.
43-
lowA++;
44-
lowB++;
45-
}
46-
47-
while (lowA < highA && lowB < highB && SourceEqual(highA - 1, highB - 1))
48-
{
49-
// Skip equal text at the end.
50-
highA--;
51-
highB--;
52-
}
67+
FindChangeExtent(ref lowA, ref highA, ref lowB, ref highB);
5368

5469
if (lowA == highA)
5570
{
@@ -82,6 +97,23 @@ private void ComputeDiffRecursive(DiffEditBuilder edits, int lowA, int highA, in
8297
}
8398
}
8499

100+
private void FindChangeExtent(ref int lowA, ref int highA, ref int lowB, ref int highB)
101+
{
102+
while (lowA < highA && lowB < highB && SourceEqualUsingOffset(lowA, lowB))
103+
{
104+
// Skip equal text at the start.
105+
lowA++;
106+
lowB++;
107+
}
108+
109+
while (lowA < highA && lowB < highB && SourceEqualUsingOffset(highA - 1, highB - 1))
110+
{
111+
// Skip equal text at the end.
112+
highA--;
113+
highB--;
114+
}
115+
}
116+
85117
private (int, int) FindMiddleSnake(int lowA, int highA, int lowB, int highB, IntArray vf, IntArray vr)
86118
{
87119
var n = highA - lowA;
@@ -126,7 +158,7 @@ private void ComputeDiffRecursive(DiffEditBuilder edits, int lowA, int highA, in
126158
var y = x - k;
127159

128160
// Traverse diagonal if possible.
129-
while (x < highA && y < highB && SourceEqual(x, y))
161+
while (x < highA && y < highB && SourceEqualUsingOffset(x, y))
130162
{
131163
x++;
132164
y++;
@@ -169,7 +201,7 @@ private void ComputeDiffRecursive(DiffEditBuilder edits, int lowA, int highA, in
169201
var y = x - k;
170202

171203
// Traverse diagonal if possible.
172-
while (x > lowA && y > lowB && SourceEqual(x - 1, y - 1))
204+
while (x > lowA && y > lowB && SourceEqualUsingOffset(x - 1, y - 1))
173205
{
174206
x--;
175207
y--;
@@ -195,4 +227,7 @@ private void ComputeDiffRecursive(DiffEditBuilder edits, int lowA, int highA, in
195227

196228
throw Assumes.NotReachable();
197229
}
230+
231+
private bool SourceEqualUsingOffset(int oldSourceIndex, int newSourceIndex)
232+
=> SourceEqual(oldSourceIndex + _sourceOffsetA, newSourceIndex + _newSourceOffset);
198233
}

0 commit comments

Comments
 (0)