Commit 20d4d676 authored by Ben Gamari's avatar Ben Gamari 🐢 Committed by Marge Bot

nonmoving: Don't traverse filled segment list in pause

The non-moving collector would previously walk the entire filled segment
list during the preparatory pause. However, this is far more work than
is strictly necessary. We can rather get away with merely collecting the
allocators' filled segment list heads and process the lists themselves
during the concurrent phase. This can significantly reduce the maximum
gen1 GC pause time in programs with high rates of long-lived allocations.
parent 7c3e39a9
Pipeline #16702 canceled with stages
......@@ -727,25 +727,10 @@ static void nonmovingPrepareMark(void)
nonmovingSegmentInfo(seg)->next_free_snap = seg->next_free;
}
// Update filled segments' snapshot pointers and move to sweep_list
uint32_t n_filled = 0;
struct NonmovingSegment *const filled = alloca->filled;
// Save the filled segments for later processing during the concurrent
// mark phase.
alloca->saved_filled = alloca->filled;
alloca->filled = NULL;
if (filled) {
struct NonmovingSegment *seg = filled;
while (true) {
// Set snapshot
nonmovingSegmentInfo(seg)->next_free_snap = seg->next_free;
n_filled++;
if (seg->link)
seg = seg->link;
else
break;
}
// add filled segments to sweep_list
seg->link = nonmovingHeap.sweep_list;
nonmovingHeap.sweep_list = filled;
}
// N.B. It's not necessary to update snapshot pointers of active segments;
// they were set after they were swept and haven't seen any allocation
......@@ -969,6 +954,28 @@ static void nonmovingMark_(MarkQueue *mark_queue, StgWeak **dead_weaks, StgTSO *
debugTrace(DEBUG_nonmoving_gc, "Starting mark...");
stat_startNonmovingGc();
// Walk the list of filled segments that we collected during preparation,
// updated their snapshot pointers and move them to the sweep list.
for (int alloca_idx = 0; alloca_idx < NONMOVING_ALLOCA_CNT; ++alloca_idx) {
struct NonmovingSegment *filled = nonmovingHeap.allocators[alloca_idx]->saved_filled;
uint32_t n_filled = 0;
if (filled) {
struct NonmovingSegment *seg = filled;
while (true) {
// Set snapshot
nonmovingSegmentInfo(seg)->next_free_snap = seg->next_free;
n_filled++;
if (seg->link)
seg = seg->link;
else
break;
}
// add filled segments to sweep_list
seg->link = nonmovingHeap.sweep_list;
nonmovingHeap.sweep_list = filled;
}
}
// Do concurrent marking; most of the heap will get marked here.
nonmovingMarkThreadsWeaks(mark_queue);
......
......@@ -62,6 +62,7 @@ struct NonmovingSegment {
// A non-moving allocator for a particular block size
struct NonmovingAllocator {
struct NonmovingSegment *filled;
struct NonmovingSegment *saved_filled;
struct NonmovingSegment *active;
// indexed by capability number
struct NonmovingSegment *current[];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment