Skip to content

Commit 43eaabb

Browse files
committed
Use mark_at_start() in GC full collection.
This provides a pretty significant performance increase for full collections.
1 parent 5ec03cf commit 43eaabb

1 file changed

Lines changed: 33 additions & 12 deletions

File tree

Python/gc.c

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,7 +1485,6 @@ completed_scavenge(GCState *gcstate)
14851485
gc_list_merge(&gcstate->old[visited].head, &gcstate->old[not_visited].head);
14861486
gc_list_set_space(&gcstate->old[not_visited].head, not_visited);
14871487
}
1488-
assert(gc_list_is_empty(&gcstate->old[visited].head));
14891488
gcstate->work_to_do = 0;
14901489
gcstate->phase = GC_PHASE_MARK;
14911490
}
@@ -1709,6 +1708,7 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
17091708

17101709
if (gc_list_is_empty(not_visited)) {
17111710
completed_scavenge(gcstate);
1711+
assert(gc_list_is_empty(&gcstate->old[visited].head));
17121712
}
17131713
validate_spaces(gcstate);
17141714
}
@@ -1720,21 +1720,42 @@ gc_collect_full(PyThreadState *tstate,
17201720
GC_STAT_ADD(2, collections, 1);
17211721
GCState *gcstate = &tstate->interp->gc;
17221722
validate_spaces(gcstate);
1723-
PyGC_Head *young = &gcstate->young.head;
1724-
PyGC_Head *pending = &gcstate->old[gcstate->visited_space^1].head;
1723+
PyGC_Head *not_visited = &gcstate->old[gcstate->visited_space^1].head;
17251724
PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head;
1726-
untrack_tuples(young);
1727-
/* merge all generations into visited */
1728-
gc_list_merge(young, pending);
1729-
gc_list_validate_space(pending, 1-gcstate->visited_space);
1730-
gc_list_set_space(pending, gcstate->visited_space);
1731-
gcstate->young.count = 0;
1732-
gc_list_merge(pending, visited);
1725+
untrack_tuples(&gcstate->young.head);
1726+
1727+
// Move objects that are reachable from known roots into
1728+
// the "visited" set.
1729+
gc_list_set_space(visited, 1-gcstate->visited_space);
1730+
gc_list_merge(visited, not_visited);
1731+
Py_ssize_t objects_marked = mark_at_start(tstate);
1732+
GC_STAT_ADD(2, objects_transitively_reachable, objects_marked);
1733+
stats->candidates += objects_marked;
17331734
validate_spaces(gcstate);
17341735

1735-
gc_collect_region(tstate, visited, visited,
1736-
stats);
1736+
// Prepare increment set, it contains "young+not_visited". The "visited"
1737+
// set contains objects known to not be garbage and so they will be
1738+
// excluded from the gc_collect_region() operation.
1739+
PyGC_Head increment;
1740+
gc_list_init(&increment);
1741+
gc_list_set_space(&gcstate->young.head, gcstate->visited_space);
1742+
gc_list_merge(&gcstate->young.head, &increment);
1743+
gc_list_validate_space(&increment, gcstate->visited_space);
1744+
gc_list_set_space(not_visited, gcstate->visited_space);
1745+
gc_list_merge(not_visited, &increment);
1746+
validate_list(&increment, collecting_clear_unreachable_clear);
1747+
gc_list_validate_space(&increment, gcstate->visited_space);
1748+
1749+
// Find cyclic garbage by comparing edge counts with reference counts.
1750+
// Objects that are not garbage will be in "survivors".
1751+
PyGC_Head survivors;
1752+
gc_list_init(&survivors);
1753+
gc_collect_region(tstate, &increment, &survivors, stats);
1754+
gc_list_merge(&survivors, visited);
1755+
assert(gc_list_is_empty(&increment));
1756+
assert(gc_list_is_empty(not_visited));
17371757
validate_spaces(gcstate);
1758+
17381759
gcstate->young.count = 0;
17391760
gcstate->old[0].count = 0;
17401761
gcstate->old[1].count = 0;

0 commit comments

Comments
 (0)