Software Development

Code review challenge – The concurrent dictionary refactoring – answer

Here is the full method that we refactored:

 public void ReturnMemory(byte* pointer)
 {
     var memoryDataForPointer = GetMemoryDataForPointer(pointer);
     _freeSegments.AddOrUpdate(memoryDataForPointer.SizeInBytes, x =>
     {
         var newQueue = new ConcurrentStack<AllocatedMemoryData>();
         newQueue.Push(memoryDataForPointer);
         return newQueue;
     }, (x, queue) =>
     {
         queue.Push(memoryDataForPointer);
         return queue;
     });
 }

And here is the allocation map for this method:

public unsafe void ReturnMemory(byte* pointer)
{
    <>c__DisplayClass9_0 CS$<>8__locals0 = new <>c__DisplayClass9_0();
    CS$<>8__locals0.memoryDataForPointer = this.GetMemoryDataForPointer(pointer);
    this._freeSegments.AddOrUpdate(CS$<>8__locals0.memoryDataForPointer.SizeInBytes, 
          new Func<int, ConcurrentStack<AllocatedMemoryData>>(CS$<>8__locals0.<ReturnMemory>b__0), 
          new Func<int, ConcurrentStack<AllocatedMemoryData>, ConcurrentStack<AllocatedMemoryData>>(CS$<>8__locals0.<ReturnMemory>b__1));
}

As you can see, we are actually allocating three objects here. One is the captured variables class generated by the compiler (<>c__DisplayClass9_0) and two delegate instances. We do this regardless of if we need to add or update.

The refactored code looks like this:

 public void ReturnMemory(byte* pointer)
 {
     var memoryDataForPointer = GetMemoryDataForPointer(pointer);
     var q = _freeSegments.GetOrAdd(memoryDataForPointer.SizeInBytes, size => new ConcurrentStack<AllocatedMemoryData>());
     q.Push(memoryDataForPointer);
 }

And what actually gets called is:

public unsafe void ReturnMemory(byte* pointer)
{
    Interlocked.Increment(ref this._returnMemoryCalls);
    AllocatedMemoryData memoryDataForPointer = this.GetMemoryDataForPointer(pointer);
    if(<<c.<<9__9_0 == null)
    {
        <<c.<<9__9_0 = new Func<int, ConcurrentStack<AllocatedMemoryData<<(this.<ReturnMemory<b__9_0);
    }
    this._freeSegments.GetOrAdd(memoryDataForPointer.SizeInBytes, <<c.<<9__9_0).Push(memoryDataForPointer);
}

The field (<>c.<>9__9_0) is actually a static field, so it is only allocated once. Now we have a zero allocation method.

Related Articles

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button