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.
Reference: | Code review challenge – The concurrent dictionary refactoring – answer from our NCG partner Oren Eini at the Ayende @ Rahien blog. |