-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Change BulkMoveWithWriteBarrier to be GC suspension friendly #27642
Conversation
Micro-benchmark results:
|
The managed part seems sound to me, but I'm not familiar enough with QCalls and GC cooperative modes to comment on the native components. |
I would expect latencies in object The byte |
Could be interesting to measure impact on |
src/vm/comutilnative.cpp
Outdated
@@ -714,13 +714,75 @@ void QCALLTYPE Buffer::Clear(void *dst, size_t length) | |||
memset(dst, 0, length); | |||
} | |||
|
|||
#define BULK_MOVE_WITH_WRITE_BARRIER_BLOCK_SIZE 0x8000 | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it could be simpler if “chunking” happens on the managed side. Then it would be regular fcalls without polling and frames. (move itself does not trigger or throw)
Bulk w barrier move would just need to assert it is not asked to move too much at once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be simpler, but less efficient. It can either add a new frame that does the chunking (throughput overhead - for small block sizes in particular); or aggressive inline the chunking into every callsite to avoid the frame (code side overhead). Which tradeoff would you pick if the chunking happens on the managed side?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking of something like:
ManagedHelperProbablyInlineable()
{
// get refs, byte count and whether need to copy backwards
nint count = . . .;
if (count < CHUNK_SIZE)
{
NativeHelper(ref src, ref dst, count, backwards);
}
else
{
ManagedChunkingHelperDoNotInline(mt, ref src, ref dst, count, backwards);
}
}
ManagedChunkingHelperDoNotInline(MethodTable *mt, ref byte src, ref byte dst, nint count, bool backwards)
{
int elementSize = . . . ;
while(count)
{
int chunk = Align(min(count, chunkSize), elementSize);
NativeHelper(src, dst, chunk , backwards);
count -= chunk;
src = . . .;
dst = . . .;
}
}
The In the baseline, the pause time time is 12ms waiting for threads to suspend and 21ms GC doing work. With this change, it is just 21ms GC doing work. I have picked this example specifically to show that this change helps, but copying large arrays of object references has still less than ideal performance characteristics. |
The sample copies null references. And even if they were objects, they'd be old. I did not realize we update cards unconditionally. Yes, that introduces a lot of roots to scan and explains the remaining pause. Checking for copying null pointers is not too hard, but may not be a typical enough scenario. Checking whether writes are cross-generational (even in a conservative way) is not an easy thing to fix right now. Especially in server GC. |
Pushed update with this change. @VSadov How does the new version look? |
Looks nice!! |
Yes. |
@@ -37,8 +39,53 @@ internal static unsafe void _ZeroMemory(ref byte b, nuint byteLength) | |||
[DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] | |||
private static extern unsafe void __ZeroMemory(void* b, nuint byteLength); | |||
|
|||
// The maximum block size to for __BulkMoveWithWriteBarrier FCall. This is required to avoid GC starvation. | |||
private const uint BulkMoveWithWriteBarrierChunk = 0x10000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
65K - not too big?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
65k should be less than 50 microseconds. I can make it smaller.
/azp run coreclr-ci |
Azure Pipelines successfully started running 1 pipeline(s). |
…otnet#27642)" This reverts commit 5e1ef69.
…riendly (dotnet#27642)" (dotnet#27758)" This reverts commit b06f8a7.
No description provided.