Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ChildSyntaxList.ItemInternal optimization #73650

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ public override void DefaultVisit(SyntaxNode node)
{
var childCnt = node.ChildNodesAndTokens().Count;
int i = 0;
var slotData = new ChildSyntaxList.SlotData(node);

do
{
var child = ChildSyntaxList.ItemInternal((CSharpSyntaxNode)node, i);
var child = ChildSyntaxList.ItemInternal((CSharpSyntaxNode)node, i, ref slotData);
i++;

var asNode = child.AsNode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ public struct Enumerator
private SyntaxNode? _node;
private int _count;
private int _childIndex;
private SlotData _slotData;

internal Enumerator(SyntaxNode node, int count)
{
_node = node;
_count = count;
_childIndex = -1;
_slotData = new SlotData(node);
}

// PERF: Initialize an Enumerator directly from a SyntaxNode without going
Expand All @@ -33,6 +35,7 @@ internal void InitializeFrom(SyntaxNode node)
_node = node;
_count = CountNodes(node.Green);
_childIndex = -1;
_slotData = new SlotData(node);
}

/// <summary>Advances the enumerator to the next element of the <see cref="ChildSyntaxList" />.</summary>
Expand All @@ -58,7 +61,7 @@ public SyntaxNodeOrToken Current
get
{
Debug.Assert(_node is object);
return ItemInternal(_node, _childIndex);
return ItemInternal(_node, _childIndex, ref _slotData);
}
}

Expand All @@ -76,15 +79,15 @@ internal bool TryMoveNextAndGetCurrent(out SyntaxNodeOrToken current)
return false;
}

current = ItemInternal(_node, _childIndex);
current = ItemInternal(_node, _childIndex, ref _slotData);
return true;
}

internal SyntaxNode? TryMoveNextAndGetCurrentAsNode()
{
while (MoveNext())
{
var nodeValue = ItemInternalAsNode(_node, _childIndex);
var nodeValue = ItemInternalAsNode(_node, _childIndex, ref _slotData);
if (nodeValue != null)
{
return nodeValue;
Expand Down
73 changes: 66 additions & 7 deletions src/Compilers/Core/Portable/Syntax/ChildSyntaxList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,60 @@ private static int Occupancy(GreenNode green)
return green.IsList ? green.SlotCount : 1;
}

internal readonly struct SlotData
{
/// <summary>
/// The green node slot index at which to start the search
/// </summary>
public readonly int SlotIndex;

/// <summary>
/// Indicates the total number of occupants in preceding slots
/// </summary>
public readonly int PrecedingOccupantSlotCount;

/// <summary>
/// Indicates the node start position plus any prior slot full widths
/// </summary>
public readonly int PositionAtSlotIndex;

public SlotData(SyntaxNode node)
: this(slotIndex: 0, precedingOccupantSlotCount: 0, node.Position)
{
}

public SlotData(int slotIndex, int precedingOccupantSlotCount, int positionAtSlotIndex)
{
SlotIndex = slotIndex;
PrecedingOccupantSlotCount = precedingOccupantSlotCount;
PositionAtSlotIndex = positionAtSlotIndex;
}
}

internal static SyntaxNodeOrToken ItemInternal(SyntaxNode node, int index)
{
var slotData = new SlotData(node);

return ItemInternal(node, index, ref slotData);
}

/// <summary>
/// internal indexer that does not verify index.
/// Used when caller has already ensured that index is within bounds.
/// </summary>
internal static SyntaxNodeOrToken ItemInternal(SyntaxNode node, int index)
internal static SyntaxNodeOrToken ItemInternal(SyntaxNode node, int index, ref SlotData slotData)
{
GreenNode? greenChild;
var green = node.Green;
var idx = index;
var slotIndex = 0;
var position = node.Position;

// slotData may contain information that allows us to start the loop below using data
// calculated during a previous call. As index represents the offset into all children of
// node, idx represents the offset requested relative to the given slot index.
var idx = index - slotData.PrecedingOccupantSlotCount;
var slotIndex = slotData.SlotIndex;
var position = slotData.PositionAtSlotIndex;

Debug.Assert(idx >= 0);

// find a slot that contains the node or its parent list (if node is in a list)
// we will be skipping whole slots here so we will not loop for long
Expand Down Expand Up @@ -122,6 +165,12 @@ internal static SyntaxNodeOrToken ItemInternal(SyntaxNode node, int index)
slotIndex++;
}

if (slotIndex != slotData.SlotIndex)
{
// (index - idx) represents the number of occupants prior to this new slotIndex
slotData = new SlotData(slotIndex, index - idx, position);
}

// get node that represents this slot
var red = node.GetNodeSlot(slotIndex);
if (!greenChild.IsList)
Expand Down Expand Up @@ -250,12 +299,15 @@ internal static SyntaxNodeOrToken ChildThatContainsPosition(SyntaxNode node, int
/// internal indexer that does not verify index.
/// Used when caller has already ensured that index is within bounds.
/// </summary>
internal static SyntaxNode? ItemInternalAsNode(SyntaxNode node, int index)
internal static SyntaxNode? ItemInternalAsNode(SyntaxNode node, int index, ref SlotData slotData)
{
GreenNode? greenChild;
var green = node.Green;
var idx = index;
var slotIndex = 0;
var idx = index - slotData.PrecedingOccupantSlotCount;
var slotIndex = slotData.SlotIndex;
var position = slotData.PositionAtSlotIndex;

Debug.Assert(idx >= 0);

// find a slot that contains the node or its parent list (if node is in a list)
// we will be skipping whole slots here so we will not loop for long
Expand All @@ -277,11 +329,18 @@ internal static SyntaxNodeOrToken ChildThatContainsPosition(SyntaxNode node, int
}

idx -= currentOccupancy;
position += greenChild.FullWidth;
}

slotIndex++;
}

if (slotIndex != slotData.SlotIndex)
{
// (index - idx) represents the number of occupants prior to this new slotIndex
slotData = new SlotData(slotIndex, index - idx, position);
}

// get node that represents this slot
var red = node.GetNodeSlot(slotIndex);
if (greenChild.IsList && red != null)
Expand Down
Loading