Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A particular usage of StrongBox

Tags:

roslyn

I am looking at the Roslyn September 2012 CTP with Reflector, and I noticed the following depth-first traversal of the syntax tree:

private IEnumerable<CommonSyntaxNode> DescendantNodesOnly(TextSpan span,
    Func<CommonSyntaxNode, bool> descendIntoChildren, bool includeSelf)
{
    if (includeSelf && IsInSpan(span, FullSpan))
    {
        yield return this;
    }

    if ((descendIntoChildren != null) && !descendIntoChildren(this))
    {
        yield break;
    }

    var queue = new Queue<StrongBox<IEnumerator<CommonSyntaxNode>>>();
    var stack = new Stack<StrongBox<IEnumerator<CommonSyntaxNode>>>();
    stack.Push(new StrongBox<IEnumerator<CommonSyntaxNode>>(ChildNodes().GetEnumerator()));
    while (stack.Count > 0)
    {
        var enumerator = stack.Peek();
        StrongBox<IEnumerator<CommonSyntaxNode>> childEnumerator;
        if (enumerator.Value.MoveNext())
        {
            var current = enumerator.Value.Current;
            if (IsInSpan(span, current.FullSpan))
            {
                yield return current;

                if ((descendIntoChildren == null) || descendIntoChildren(current))
                {
                    childEnumerator = queue.Count == 0
                        ? new StrongBox<IEnumerator<CommonSyntaxNode>>()
                        : queue.Dequeue();
                    childEnumerator.Value = current.ChildNodes().GetEnumerator();
                    stack.Push(childEnumerator);
                }
            }
        }
        else
        {
            childEnumerator = stack.Pop();
            childEnumerator.Value = null;
            queue.Enqueue(childEnumerator);
        }
    }
}

I am guessing that the queue is to ease the runtime from allocating and deallocating so many instances of IEnumerator<CommonSyntaxNode>.

However, I am not sure why IEnumerator<CommonSyntaxNode> is wrapped in StrongBox<>. What sort of performance and safety trade-offs are involved in wrapping IEnumerator<CommonSyntaxNode>, which is usually a value type, inside the reference type StrongBox<>?

like image 313
cubetwo1729 Avatar asked Jan 21 '14 16:01

cubetwo1729


2 Answers

CommonSyntaxNode is an abstact class which contains alot of value types and can be inherited into a big object.

IEnumerator<CommonSyntaxNode> contains only a reference to a CommonSyntaxNode so it seems like the size of the CommonSyntaxNode won't affect the Enumerator size since it's only a reference, but:

since IEnumerator<T>'s method MoveNext() uses yield return; each iteration in the Enumerator will cause the method to save it's state untill the next iteration.

since the whole method state is heavy enough and it might contain CommonSyntaxNode's properties in order to do the MoveNext() logic, than the whole IEnumerator<CommonSyntaxNode> might be pretty heavy on memory.

using StrongBox<> causes the Queue or Stack hold only a small reference object (the StrongBox<> instead of the potentially heavy on memory IEnumerator<CommonSyntaxNode> - therefore - the GC is cleaning the Queue or Stack's contained IEnumerator<CommonSyntaxNode> from the memory potentially faster - reducing the application total memory consumption.

note that CommonSyntaxNode's enumerator is a struct which working with it directly means deeply copying the whole struct, it's a small struct so it's not really heavy but still...

like image 120
Tamir Vered Avatar answered Nov 18 '22 15:11

Tamir Vered


The advantage of the StrongBox<T> is once an item is dequeued, the StrongBox clears out it's internal content - so the GC can then collect the instance of T being held by StrongBox and the Queue<T> ends up holding just an instance of StrongBox (instead of an instance of T).

like image 3
nchaud Avatar answered Nov 18 '22 15:11

nchaud