Skip to content

Commit 615a96e

Browse files
committed
Pre-scan sys.modules
1 parent d451ace commit 615a96e

File tree

3 files changed

+32
-4
lines changed

3 files changed

+32
-4
lines changed

Include/internal/pycore_gc.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,8 @@ struct _gc_runtime_state {
328328
Py_ssize_t heap_size;
329329
Py_ssize_t work_to_do;
330330
/* Which of the old spaces is the visited space */
331-
int visited_space;
331+
uint8_t visited_space;
332+
uint8_t scan_reachable;
332333

333334
#ifdef Py_GIL_DISABLED
334335
/* This is the number of objects that survived the last full

Lib/test/test_gc.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,6 +1115,7 @@ def make_ll(depth):
11151115
olds.append(newhead)
11161116
if len(olds) == 20:
11171117
live = _testinternalcapi.get_heap_size()
1118+
print(i, live, baseline_live)
11181119
self.assertLess(live, baseline_live*2)
11191120
del olds[:]
11201121
if not enabled:

Python/gc.c

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,8 +1287,8 @@ gc_list_set_space(PyGC_Head *list, int space)
12871287
*/
12881288

12891289
/* Multiply by 5, so that the default incremental threshold of 10
1290-
* scans objects at the same rate as the young generation */
1291-
#define SCAN_RATE_MULTIPLIER 10
1290+
* scans objects at half the rate of the young generation */
1291+
#define SCAN_RATE_MULTIPLIER 5
12921292

12931293
static void
12941294
add_stats(GCState *gcstate, int gen, struct gc_collection_stats *stats)
@@ -1428,8 +1428,30 @@ completed_cycle(GCState *gcstate)
14281428
gc = next;
14291429
}
14301430
gcstate->work_to_do = 0;
1431+
gcstate->scan_reachable = 1;
14311432
}
14321433

1434+
1435+
static void
1436+
gc_mark_reachable(PyThreadState *tstate)
1437+
{
1438+
GCState *gcstate = &tstate->interp->gc;
1439+
PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head;
1440+
PyObject *sysdict = tstate->interp->sysdict;
1441+
PyObject *sysmod = PyDict_GetItemString(sysdict, "modules");
1442+
if (sysmod == NULL) {
1443+
return;
1444+
}
1445+
PyGC_Head reachable;
1446+
gc_list_init(&reachable);
1447+
PyGC_Head *gc = _Py_AS_GC(sysmod);
1448+
gc_list_move(gc, &reachable);
1449+
gc_set_old_space(gc, gcstate->visited_space);
1450+
gcstate->work_to_do -= expand_region_transitively_reachable(&reachable, gc, gcstate);
1451+
gc_list_merge(&reachable, visited);
1452+
}
1453+
1454+
14331455
static void
14341456
gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
14351457
{
@@ -1439,6 +1461,10 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
14391461
PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head;
14401462
PyGC_Head increment;
14411463
gc_list_init(&increment);
1464+
if (gcstate->scan_reachable) {
1465+
gc_mark_reachable(tstate);
1466+
gcstate->scan_reachable = 0;
1467+
}
14421468
Py_ssize_t scale_factor = gcstate->old[0].threshold;
14431469
if (scale_factor < 1) {
14441470
scale_factor = 1;
@@ -1461,7 +1487,7 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
14611487
gc_list_validate_space(&survivors, gcstate->visited_space);
14621488
gc_list_merge(&survivors, visited);
14631489
assert(gc_list_is_empty(&increment));
1464-
Py_ssize_t delta = (gcstate->heap_size - gcstate->prior_heap_size)*2;
1490+
Py_ssize_t delta = (gcstate->heap_size - gcstate->prior_heap_size)*3;
14651491
delta += gcstate->young.threshold * SCAN_RATE_MULTIPLIER / scale_factor;
14661492
if (delta > 0) {
14671493
gcstate->work_to_do += delta;

0 commit comments

Comments
 (0)