Skip to content

Commit 5027c77

Browse files
committed
Compute sizes nearer to use, not in stats.
1 parent cef9b9c commit 5027c77

File tree

2 files changed

+34
-56
lines changed

2 files changed

+34
-56
lines changed

Include/internal/pycore_gc.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,6 @@ struct gc_generation {
158158
};
159159

160160
struct gc_collection_stats {
161-
/* number of surviving objects */
162-
Py_ssize_t survivors;
163161
/* number of collected objects */
164162
Py_ssize_t collected;
165163
/* total number of uncollectable objects (put into gc.garbage) */

Modules/gcmodule.c

Lines changed: 34 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -472,16 +472,14 @@ validate_list_is_aging(PyGC_Head *head)
472472
/* Set all gc_refs = ob_refcnt. After this, gc_refs is > 0 and
473473
* PREV_MASK_COLLECTING bit is set for all objects in containers.
474474
*/
475-
static Py_ssize_t
475+
static void
476476
update_refs(PyGC_Head *containers)
477477
{
478478
PyGC_Head *next;
479479
PyGC_Head *gc = GC_NEXT(containers);
480-
Py_ssize_t size = 0;
481480

482481
while (gc != containers) {
483482
next = GC_NEXT(gc);
484-
size++;
485483
/* Move any object that might have become immortal to the
486484
* permanent generation as the reference count is not accurately
487485
* reflecting the actual number of live references to this object
@@ -514,7 +512,6 @@ update_refs(PyGC_Head *containers)
514512
gc = next;
515513
}
516514

517-
return size;
518515
}
519516

520517
/* A traversal callback for subtract_refs. */
@@ -1122,25 +1119,6 @@ clear_freelists(PyInterpreterState *interp)
11221119
_PyContext_ClearFreeList(interp);
11231120
}
11241121

1125-
// Show stats for objects in each generations
1126-
static void
1127-
show_stats_each_generations(GCState *gcstate)
1128-
{
1129-
char buf[100];
1130-
size_t pos = 0;
1131-
1132-
for (int i = 0; i < NUM_GENERATIONS && pos < sizeof(buf); i++) {
1133-
pos += PyOS_snprintf(buf+pos, sizeof(buf)-pos,
1134-
" %zd",
1135-
gc_list_size(GEN_HEAD(gcstate, i)));
1136-
}
1137-
1138-
PySys_FormatStderr(
1139-
"gc: objects in each generation:%s\n"
1140-
"gc: objects in permanent generation: %zd\n",
1141-
buf, gc_list_size(&gcstate->permanent_generation.head));
1142-
}
1143-
11441122
/* Deduce which objects among "base" are unreachable from outside the list
11451123
and move them to 'unreachable'. The process consist in the following steps:
11461124
@@ -1168,15 +1146,15 @@ flag set but it does not clear it to skip unnecessary iteration. Before the
11681146
flag is cleared (for example, by using 'clear_unreachable_mask' function or
11691147
by a call to 'move_legacy_finalizers'), the 'unreachable' list is not a normal
11701148
list and we can not use most gc_list_* functions for it. */
1171-
static inline Py_ssize_t
1149+
static inline void
11721150
deduce_unreachable(PyGC_Head *base, PyGC_Head *unreachable) {
11731151
validate_list(base, collecting_clear_unreachable_clear);
11741152
/* Using ob_refcnt and gc_refs, calculate which objects in the
11751153
* container set are reachable from outside the set (i.e., have a
11761154
* refcount greater than 0 when all the references within the
11771155
* set are taken into account).
11781156
*/
1179-
Py_ssize_t size = update_refs(base); // gc_prev is used for gc_refs
1157+
update_refs(base); // gc_prev is used for gc_refs
11801158
subtract_refs(base);
11811159

11821160
/* Leave everything reachable from outside base in base, and move
@@ -1218,7 +1196,6 @@ deduce_unreachable(PyGC_Head *base, PyGC_Head *unreachable) {
12181196
move_unreachable(base, unreachable); // gc_prev is pointer again
12191197
validate_list(base, collecting_clear_unreachable_clear);
12201198
validate_list(unreachable, collecting_set_unreachable_set);
1221-
return size;
12221199
}
12231200

12241201
/* Handle objects that may have resurrected after a call to 'finalize_garbage', moving
@@ -1263,22 +1240,28 @@ gc_collect_region(PyThreadState *tstate,
12631240
int untrack,
12641241
struct gc_collection_stats *stats);
12651242

1266-
static void
1243+
static Py_ssize_t
12671244
make_space0(PyGC_Head *list)
12681245
{
1246+
Py_ssize_t size = 0;
12691247
PyGC_Head *gc;
12701248
for (gc = GC_NEXT(list); gc != list; gc = GC_NEXT(gc)) {
12711249
gc->_gc_prev &= ~_PyGC_PREV_MASK_OLD_SPACE_1;
1250+
size++;
12721251
}
1252+
return size;
12731253
}
12741254

1275-
static void
1255+
static Py_ssize_t
12761256
make_space1(PyGC_Head *list)
12771257
{
1258+
Py_ssize_t size = 0;
12781259
PyGC_Head *gc;
12791260
for (gc = GC_NEXT(list); gc != list; gc = GC_NEXT(gc)) {
12801261
gc->_gc_prev |= _PyGC_PREV_MASK_OLD_SPACE_1;
1262+
size++;
12811263
}
1264+
return size;
12821265
}
12831266

12841267
static void
@@ -1293,7 +1276,6 @@ static void
12931276
gc_collect_young(PyThreadState *tstate,
12941277
struct gc_collection_stats *stats)
12951278
{
1296-
assert(stats->survivors == 0);
12971279
GCState *gcstate = &tstate->interp->gc;
12981280
PyGC_Head *young = &gcstate->young.head;
12991281
PyGC_Head *aging = &gcstate->old[gcstate->aging_space].head;
@@ -1311,25 +1293,26 @@ gc_collect_young(PyThreadState *tstate,
13111293
gc_list_init(&survivors);
13121294
gc_collect_region(tstate, young, &survivors, UNTRACK_TUPLES, stats);
13131295
validate_old(gcstate);
1296+
Py_ssize_t surivivor_count = 0;
13141297
if (gcstate->aging_space) {
1315-
make_space0(&survivors);
13161298
/* objects in aging space have bit set, so we set it here */
1317-
make_space1(&survivors);
1299+
surivivor_count = make_space1(&survivors);
13181300
}
1319-
#ifdef GC_DEBUG
13201301
else {
13211302
PyGC_Head *gc;
13221303
for (gc = GC_NEXT(&survivors); gc != &survivors; gc = GC_NEXT(gc)) {
1304+
#ifdef GC_DEBUG
13231305
assert((gc->_gc_prev & _PyGC_PREV_MASK_OLD_SPACE_1) == 0);
1306+
#endif
1307+
surivivor_count++;
13241308
}
13251309
}
1326-
#endif
13271310
validate_old(gcstate);
13281311
gc_list_merge(&survivors, aging);
13291312
validate_old(gcstate);
13301313
gcstate->young.count = 0;
13311314
gcstate->old[gcstate->aging_space].count++;
1332-
gcstate->incremental_gc_progress -= stats->survivors;
1315+
gcstate->incremental_gc_progress -= surivivor_count;
13331316
add_stats(gcstate, 0, stats);
13341317
}
13351318

@@ -1343,13 +1326,14 @@ gc_collect_aging(PyThreadState *tstate,
13431326
PyGC_Head *aging = &gcstate->old[gcstate->aging_space].head;
13441327
PyGC_Head *oldest = &gcstate->old[gcstate->aging_space^1].head;
13451328
/* Need to tag all objects as part of oldest generation */
1329+
Py_ssize_t size;
13461330
if (gcstate->aging_space) {
13471331
/* No need to tag young objects */
1348-
make_space0(aging);
1332+
size = make_space0(aging);
13491333
}
13501334
else {
13511335
make_space1(young);
1352-
make_space1(aging);
1336+
size = make_space1(aging);
13531337
}
13541338
/* merge young in pending before collecting */
13551339
gc_list_merge(young, aging);
@@ -1359,7 +1343,7 @@ gc_collect_aging(PyThreadState *tstate,
13591343
gc_collect_region(tstate, aging, oldest,
13601344
UNTRACK_TUPLES, stats);
13611345
validate_old(gcstate);
1362-
gcstate->incremental_gc_progress += stats->survivors + stats->collected;
1346+
gcstate->incremental_gc_progress += size;
13631347
add_stats(gcstate, 1, stats);
13641348
}
13651349

@@ -1373,18 +1357,19 @@ IS_IN_OLDEST(PyGC_Head *head, uintptr_t aging_space)
13731357
struct container_and_flag {
13741358
PyGC_Head *container;
13751359
uintptr_t aging_space;
1376-
uintptr_t size;
13771360
};
13781361

13791362
/* A traversal callback for adding to container) */
13801363
static int
13811364
visit_add_to_container(PyObject *op, void *arg)
13821365
{
13831366
OBJECT_STAT_INC(object_visits);
1367+
struct container_and_flag *cf = (struct container_and_flag *)arg;
1368+
uintptr_t oldest_flag = cf->aging_space ^ _PyGC_PREV_MASK_OLD_SPACE_1;
13841369
if (_PyObject_IS_GC(op)) {
1385-
struct container_and_flag *cf = (struct container_and_flag *)arg;
13861370
PyGC_Head *gc = AS_GC(op);
1387-
if (_PyObject_GC_IS_TRACKED(op) && IS_IN_OLDEST(gc, cf->aging_space)) {
1371+
if (_PyObject_GC_IS_TRACKED(op) &&
1372+
(gc->_gc_prev & _PyGC_PREV_MASK_OLD_SPACE_1) == oldest_flag) {
13881373
assert(!_Py_IsImmortal(op));
13891374
gc->_gc_prev ^= _PyGC_PREV_MASK_OLD_SPACE_1;
13901375
gc_list_move(gc, cf->container);
@@ -1431,11 +1416,6 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
14311416
if (gcstate->incremental_gc_progress >= 0) {
14321417
return;
14331418
}
1434-
/* We rely on stats for controlling work done in incremental GC,
1435-
* so they need to zeroed */
1436-
assert(stats->survivors == 0);
1437-
assert(stats->collected == 0);
1438-
assert(stats->uncollectable == 0);
14391419
PyGC_Head *oldest = &gcstate->old[gcstate->aging_space^1].head;
14401420
PyGC_Head *aging = &gcstate->old[gcstate->aging_space].head;
14411421
validate_old(gcstate);
@@ -1448,7 +1428,9 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
14481428
validate_old(gcstate);
14491429
if (gc_list_is_empty(oldest)) {
14501430
if (gc_list_is_empty(aging)) {
1451-
gcstate->incremental_gc_progress = gcstate->young.threshold;
1431+
if (gcstate->incremental_gc_progress < gcstate->young.threshold) {
1432+
gcstate->incremental_gc_progress = gcstate->young.threshold;
1433+
}
14521434
return;
14531435
}
14541436
/* aging is empty, so swap with oldest */
@@ -1460,6 +1442,7 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
14601442
validate_old(gcstate);
14611443
while (region_size < work_to_do) {
14621444
if (gc_list_is_empty(oldest)) {
1445+
gcstate->incremental_gc_progress = 0;
14631446
break;
14641447
}
14651448
PyGC_Head *gc = _PyGCHead_NEXT(oldest);
@@ -1473,10 +1456,8 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
14731456
gc_collect_region(tstate, &increment, aging, 0, stats);
14741457
validate_old(gcstate);
14751458
assert(gc_list_is_empty(&increment));
1476-
Py_ssize_t work_done = stats->collected + stats->survivors + stats->uncollectable;
1477-
//fprintf(stderr, "Work done %d\n", (int)work_done);
1478-
gcstate->incremental_gc_progress += work_done;
1479-
//fprintf(stderr, "Incremental GC complete\n");
1459+
gcstate->incremental_gc_progress += region_size;
1460+
assert(gcstate->incremental_gc_progress >= 0);
14801461
validate_old(gcstate);
14811462
add_stats(gcstate, 1, stats);
14821463
}
@@ -1508,7 +1489,7 @@ gc_collect_full(PyThreadState *tstate,
15081489
gcstate->old[0].count = 0;
15091490
gcstate->old[1].count = 0;
15101491

1511-
gcstate->incremental_gc_progress += stats->survivors + stats->collected;
1492+
gcstate->incremental_gc_progress = gcstate->young.threshold * 2;
15121493
clear_freelists(tstate->interp);
15131494
validate_old(gcstate);
15141495
add_stats(gcstate, 2, stats);
@@ -1536,7 +1517,7 @@ gc_collect_region(PyThreadState *tstate,
15361517

15371518
validate_list(to, collecting_clear_unreachable_clear);
15381519

1539-
Py_ssize_t processed = deduce_unreachable(from, &unreachable);
1520+
deduce_unreachable(from, &unreachable);
15401521
if (untrack & UNTRACK_TUPLES) {
15411522
untrack_tuples(from);
15421523
}
@@ -1609,7 +1590,6 @@ gc_collect_region(PyThreadState *tstate,
16091590
handle_legacy_finalizers(tstate, gcstate, &finalizers, to);
16101591
validate_list(to, collecting_clear_unreachable_clear);
16111592

1612-
stats->survivors = processed - m - n;
16131593
stats->collected = m;
16141594
stats->uncollectable = n;
16151595
validate_old(gcstate);
@@ -2169,7 +2149,7 @@ gc.unfreeze
21692149
21702150
Unfreeze all objects in the permanent generation.
21712151
2172-
Put all objects in the permanent generation back into old[0].
2152+
Put all objects in the permanent generation back into oldest generation.
21732153
[clinic start generated code]*/
21742154

21752155
static PyObject *

0 commit comments

Comments
 (0)