@@ -182,6 +182,7 @@ _PyGC_Init(PyInterpreterState *interp)
182182 return _PyStatus_NO_MEMORY ();
183183 }
184184 gcstate -> heap_size = 0 ;
185+ gcstate -> prior_heap_size = 0 ;
185186
186187 return _PyStatus_OK ();
187188}
@@ -1278,19 +1279,16 @@ gc_list_set_space(PyGC_Head *list, int space)
12781279 * the incremental collector must progress through the old
12791280 * space faster than objects are added to the old space.
12801281 *
1281- * Each young or incremental collection adds a number of
1282- * objects, S (for survivors) to the old space, and
1283- * incremental collectors scan I objects from the old space.
1284- * I > S must be true. We also want I > S * N to be where
1285- * N > 1. Higher values of N mean that the old space is
1286- * scanned more rapidly.
1287- * The default incremental threshold of 10 translates to
1288- * N == 1.4 (1 + 4/threshold)
1282+ * To do this we maintain a prior heap size, so the
1283+ * change in heap size can easily be computed.
1284+ *
1285+ * Each increment scans twice the delta (if increasing)
1286+ * plus half the size of the young generation.
12891287 */
12901288
1291- /* Divide by 10 , so that the default incremental threshold of 10
1292- * scans objects at 1% of the heap size */
1293- #define SCAN_RATE_DIVISOR 10
1289+ /* Multiply by 5 , so that the default incremental threshold of 10
1290+ * scans objects at half the rate of the young generation */
1291+ #define SCAN_RATE_MULTIPLIER 5
12941292
12951293static void
12961294add_stats (GCState * gcstate , int gen , struct gc_collection_stats * stats )
@@ -1344,7 +1342,6 @@ gc_collect_young(PyThreadState *tstate,
13441342 if (scale_factor < 1 ) {
13451343 scale_factor = 1 ;
13461344 }
1347- gcstate -> work_to_do += gcstate -> heap_size / SCAN_RATE_DIVISOR / scale_factor ;
13481345 add_stats (gcstate , 0 , stats );
13491346}
13501347
@@ -1431,8 +1428,30 @@ completed_cycle(GCState *gcstate)
14311428 gc = next ;
14321429 }
14331430 gcstate -> work_to_do = 0 ;
1431+ gcstate -> scan_reachable = 1 ;
1432+ }
1433+
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 );
14341452}
14351453
1454+
14361455static void
14371456gc_collect_increment (PyThreadState * tstate , struct gc_collection_stats * stats )
14381457{
@@ -1442,13 +1461,14 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
14421461 PyGC_Head * visited = & gcstate -> old [gcstate -> visited_space ].head ;
14431462 PyGC_Head increment ;
14441463 gc_list_init (& increment );
1464+ if (gcstate -> scan_reachable ) {
1465+ gc_mark_reachable (tstate );
1466+ gcstate -> scan_reachable = 0 ;
1467+ }
14451468 Py_ssize_t scale_factor = gcstate -> old [0 ].threshold ;
14461469 if (scale_factor < 1 ) {
14471470 scale_factor = 1 ;
14481471 }
1449- gc_list_merge (& gcstate -> young .head , & increment );
1450- gcstate -> young .count = 0 ;
1451- gc_list_validate_space (& increment , gcstate -> visited_space );
14521472 Py_ssize_t increment_size = 0 ;
14531473 while (increment_size < gcstate -> work_to_do ) {
14541474 if (gc_list_is_empty (not_visited )) {
@@ -1467,7 +1487,12 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
14671487 gc_list_validate_space (& survivors , gcstate -> visited_space );
14681488 gc_list_merge (& survivors , visited );
14691489 assert (gc_list_is_empty (& increment ));
1470- gcstate -> work_to_do += gcstate -> heap_size / SCAN_RATE_DIVISOR / scale_factor ;
1490+ Py_ssize_t delta = (gcstate -> heap_size - gcstate -> prior_heap_size )* 3 ;
1491+ delta += gcstate -> young .threshold * SCAN_RATE_MULTIPLIER / scale_factor ;
1492+ if (delta > 0 ) {
1493+ gcstate -> work_to_do += delta ;
1494+ }
1495+ gcstate -> prior_heap_size = gcstate -> heap_size ;
14711496 gcstate -> work_to_do -= increment_size ;
14721497
14731498 validate_old (gcstate );
@@ -1856,6 +1881,7 @@ _PyGC_Collect(PyThreadState *tstate, int generation, _PyGC_Reason reason)
18561881 gc_collect_young (tstate , & stats );
18571882 break ;
18581883 case 1 :
1884+ gc_collect_young (tstate , & stats );
18591885 gc_collect_increment (tstate , & stats );
18601886 break ;
18611887 case 2 :
0 commit comments