@@ -1277,17 +1277,12 @@ gc_list_set_space(PyGC_Head *list, int space)
1277
1277
* space faster than objects are added to the old space.
1278
1278
*
1279
1279
* Each young or incremental collection adds a number of
1280
- * objects, S (for survivors ) to the old space , and
1281
- * incremental collectors scan I objects from the old space.
1282
- * I > S must be true. We also want I > S * N to be where
1283
- * N > 1 . Higher values of N mean that the old space is
1280
+ * new objects (N ) to the heap , and incremental collectors
1281
+ * scan I objects from the old space.
1282
+ * I > N must be true. We also want I > N * K to be where
1283
+ * K > 2 . Higher values of K mean that the old space is
1284
1284
* scanned more rapidly.
1285
- * The default incremental threshold of 10 translates to
1286
- * N == 1.4 (1 + 4/threshold)
1287
1285
*/
1288
-
1289
- /* Divide by 10, so that the default incremental threshold of 10
1290
- * scans objects at 1% of the heap size */
1291
1286
#define SCAN_RATE_DIVISOR 10
1292
1287
1293
1288
static void
@@ -1335,6 +1330,7 @@ typedef struct work_stack {
1335
1330
int visited_space ;
1336
1331
} WorkStack ;
1337
1332
1333
+ /* Remove gc from the list it is currently in and push it to the stack */
1338
1334
static inline void
1339
1335
push_to_stack (PyGC_Head * gc , WorkStack * stack )
1340
1336
{
@@ -1346,6 +1342,14 @@ push_to_stack(PyGC_Head *gc, WorkStack *stack)
1346
1342
stack -> top = gc ;
1347
1343
}
1348
1344
1345
+ static inline PyGC_Head *
1346
+ pop_from_stack (WorkStack * stack )
1347
+ {
1348
+ PyGC_Head * gc = stack -> top ;
1349
+ stack -> top = _PyGCHead_PREV (gc );
1350
+ return gc ;
1351
+ }
1352
+
1349
1353
/* append list `from` to `stack`; `from` becomes an empty list */
1350
1354
static void
1351
1355
move_list_to_stack (PyGC_Head * from , WorkStack * stack )
@@ -1418,7 +1422,6 @@ completed_scavenge(GCState *gcstate)
1418
1422
gc_list_set_space (& gcstate -> old [not_visited ].head , not_visited );
1419
1423
}
1420
1424
assert (gc_list_is_empty (& gcstate -> old [visited ].head ));
1421
- gcstate -> work_to_do = 0 ;
1422
1425
gcstate -> phase = GC_PHASE_MARK ;
1423
1426
}
1424
1427
@@ -1450,9 +1453,7 @@ move_all_transitively_reachable(WorkStack *stack, PyGC_Head *visited, int visite
1450
1453
// Transitively traverse all objects from reachable, until empty
1451
1454
Py_ssize_t objects_marked = 0 ;
1452
1455
while (stack -> top != NULL ) {
1453
- /* Pop from the stack, to do depth first traversal */
1454
- PyGC_Head * gc = stack -> top ;
1455
- stack -> top = _PyGCHead_PREV (gc );
1456
+ PyGC_Head * gc = pop_from_stack (stack );
1456
1457
assert (gc_old_space (gc ) == visited_space );
1457
1458
gc_list_append (gc , visited );
1458
1459
objects_marked ++ ;
@@ -1513,7 +1514,6 @@ mark_global_roots(PyInterpreterState *interp, PyGC_Head *visited, int visited_sp
1513
1514
WorkStack stack ;
1514
1515
stack .top = NULL ;
1515
1516
stack .visited_space = visited_space ;
1516
- Py_ssize_t objects_marked = 0 ;
1517
1517
MOVE_UNVISITED (interp -> sysdict , & stack , visited_space );
1518
1518
MOVE_UNVISITED (interp -> builtins , & stack , visited_space );
1519
1519
MOVE_UNVISITED (interp -> dict , & stack , visited_space );
@@ -1526,7 +1526,7 @@ mark_global_roots(PyInterpreterState *interp, PyGC_Head *visited, int visited_sp
1526
1526
MOVE_UNVISITED (types -> for_extensions .initialized [i ].tp_dict , & stack , visited_space );
1527
1527
MOVE_UNVISITED (types -> for_extensions .initialized [i ].tp_subclasses , & stack , visited_space );
1528
1528
}
1529
- objects_marked + = move_all_transitively_reachable (& stack , visited , visited_space );
1529
+ Py_ssize_t objects_marked = move_all_transitively_reachable (& stack , visited , visited_space );
1530
1530
assert (stack .top == NULL );
1531
1531
return objects_marked ;
1532
1532
}
@@ -1547,12 +1547,11 @@ mark_at_start(PyThreadState *tstate)
1547
1547
static intptr_t
1548
1548
assess_work_to_do (GCState * gcstate )
1549
1549
{
1550
- /* The amount of work we want to do depends on three things.
1550
+ /* The amount of work we want to do depends on two things.
1551
1551
* 1. The number of new objects created
1552
- * 2. The growth in heap size since the last collection
1553
- * 3. The heap size (up to the number of new objects, to avoid quadratic effects)
1552
+ * 2. The heap size (up to twice the number of new objects, to avoid quadratic effects)
1554
1553
*
1555
- * For a steady state heap, the amount of work to do is three times the number
1554
+ * For a large, steady state heap, the amount of work to do is three times the number
1556
1555
* of new objects added to the heap. This ensures that we stay ahead in the
1557
1556
* worst case of all new objects being garbage.
1558
1557
*
@@ -1564,13 +1563,13 @@ assess_work_to_do(GCState *gcstate)
1564
1563
scale_factor = 2 ;
1565
1564
}
1566
1565
intptr_t new_objects = gcstate -> young .count ;
1567
- intptr_t max_heap_fraction = new_objects * 3 / 2 ;
1566
+ intptr_t max_heap_fraction = new_objects * 2 ;
1568
1567
intptr_t heap_fraction = gcstate -> heap_size / SCAN_RATE_DIVISOR / scale_factor ;
1569
1568
if (heap_fraction > max_heap_fraction ) {
1570
1569
heap_fraction = max_heap_fraction ;
1571
1570
}
1572
1571
gcstate -> young .count = 0 ;
1573
- return new_objects + heap_fraction * 2 ;
1572
+ return new_objects + heap_fraction ;
1574
1573
}
1575
1574
1576
1575
static void
@@ -1606,7 +1605,7 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
1606
1605
Py_ssize_t increment_size = move_all_transitively_reachable (& working , & increment , gcstate -> visited_space );
1607
1606
gc_list_validate_space (& increment , gcstate -> visited_space );
1608
1607
assert (working .top == NULL );
1609
- while (increment_size < gcstate -> work_to_do ) {
1608
+ while (increment_size < gcstate -> work_to_do * 2 ) {
1610
1609
if (gc_list_is_empty (not_visited )) {
1611
1610
break ;
1612
1611
}
@@ -1626,7 +1625,7 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
1626
1625
gc_collect_region (tstate , & increment , & survivors , stats );
1627
1626
gc_list_merge (& survivors , visited );
1628
1627
assert (gc_list_is_empty (& increment ));
1629
- gcstate -> work_to_do -= increment_size ;
1628
+ gcstate -> work_to_do -= increment_size / 2 ;
1630
1629
1631
1630
add_stats (gcstate , 1 , stats );
1632
1631
if (gc_list_is_empty (not_visited )) {
@@ -1661,6 +1660,7 @@ gc_collect_full(PyThreadState *tstate,
1661
1660
gcstate -> old [0 ].count = 0 ;
1662
1661
gcstate -> old [1 ].count = 0 ;
1663
1662
completed_scavenge (gcstate );
1663
+ gcstate -> work_to_do = - gcstate -> young .threshold ;
1664
1664
_PyGC_ClearAllFreeLists (tstate -> interp );
1665
1665
validate_spaces (gcstate );
1666
1666
add_stats (gcstate , 2 , stats );
0 commit comments