Skip to content

Commit be72b3f

Browse files
Extra stats
1 parent fbb7342 commit be72b3f

File tree

4 files changed

+25
-10
lines changed

4 files changed

+25
-10
lines changed

Include/internal/pycore_interp_structs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ struct gc_collection_stats {
181181
Py_ssize_t collected;
182182
/* total number of uncollectable objects (put into gc.garbage) */
183183
Py_ssize_t uncollectable;
184+
Py_ssize_t untracked_tuples;
184185
};
185186

186187
/* Running stats per generation */
@@ -191,6 +192,7 @@ struct gc_generation_stats {
191192
Py_ssize_t collected;
192193
/* total number of uncollectable objects (put into gc.garbage) */
193194
Py_ssize_t uncollectable;
195+
Py_ssize_t untracked_tuples;
194196
};
195197

196198
enum _GCPhase {

Include/internal/pycore_tuple.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ extern "C" {
1111
#include "pycore_object.h" // _PyObject_GC_IS_TRACKED
1212
#include "pycore_structs.h" // _PyStackRef
1313

14-
extern void _PyTuple_MaybeUntrack(PyObject *);
14+
extern int _PyTuple_MaybeUntrack(PyObject *);
1515
extern void _PyTuple_DebugMallocStats(FILE *out);
1616

1717
/* runtime lifecycle */

Objects/tupleobject.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,14 +134,14 @@ PyTuple_SetItem(PyObject *op, Py_ssize_t i, PyObject *newitem)
134134
return 0;
135135
}
136136

137-
void
137+
int
138138
_PyTuple_MaybeUntrack(PyObject *op)
139139
{
140140
PyTupleObject *t;
141141
Py_ssize_t i, n;
142142

143143
if (!PyTuple_CheckExact(op) || !_PyObject_GC_IS_TRACKED(op))
144-
return;
144+
return 0;
145145
t = (PyTupleObject *) op;
146146
n = Py_SIZE(t);
147147
for (i = 0; i < n; i++) {
@@ -151,9 +151,10 @@ _PyTuple_MaybeUntrack(PyObject *op)
151151
them yet. */
152152
if (!elt ||
153153
_PyObject_GC_MAY_BE_TRACKED(elt))
154-
return;
154+
return 0;
155155
}
156156
_PyObject_GC_UNTRACK(op);
157+
return 1;
157158
}
158159

159160
PyObject *

Python/gc.c

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -753,18 +753,20 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
753753
* and is much faster than a more complex approach that
754754
* would untrack all relevant tuples.
755755
*/
756-
static void
756+
static Py_ssize_t
757757
untrack_tuples(PyGC_Head *head)
758758
{
759+
Py_ssize_t untracked = 0;
759760
PyGC_Head *gc = GC_NEXT(head);
760761
while (gc != head) {
761762
PyObject *op = FROM_GC(gc);
762763
PyGC_Head *next = GC_NEXT(gc);
763764
if (PyTuple_CheckExact(op)) {
764-
_PyTuple_MaybeUntrack(op);
765+
untracked += _PyTuple_MaybeUntrack(op);
765766
}
766767
gc = next;
767768
}
769+
return untracked;
768770
}
769771

770772
/* Return true if object has a pre-PEP 442 finalization method. */
@@ -1376,7 +1378,7 @@ gc_collect_young(PyThreadState *tstate,
13761378
validate_spaces(gcstate);
13771379
PyGC_Head *young = &gcstate->young.head;
13781380
PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head;
1379-
untrack_tuples(young);
1381+
stats->untracked_tuples += untrack_tuples(young);
13801382
GC_STAT_ADD(0, collections, 1);
13811383

13821384
PyGC_Head survivors;
@@ -1654,7 +1656,7 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
16541656
GC_STAT_ADD(1, collections, 1);
16551657
GCState *gcstate = &tstate->interp->gc;
16561658
gcstate->work_to_do += assess_work_to_do(gcstate);
1657-
untrack_tuples(&gcstate->young.head);
1659+
stats->untracked_tuples += untrack_tuples(&gcstate->young.head);
16581660
if (gcstate->phase == GC_PHASE_MARK) {
16591661
Py_ssize_t objects_marked = mark_at_start(tstate);
16601662
GC_STAT_ADD(1, objects_transitively_reachable, objects_marked);
@@ -1716,7 +1718,7 @@ gc_collect_full(PyThreadState *tstate,
17161718
PyGC_Head *young = &gcstate->young.head;
17171719
PyGC_Head *pending = &gcstate->old[gcstate->visited_space^1].head;
17181720
PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head;
1719-
untrack_tuples(young);
1721+
stats->untracked_tuples += untrack_tuples(young);
17201722
/* merge all generations into visited */
17211723
gc_list_merge(young, pending);
17221724
gc_list_validate_space(pending, 1-gcstate->visited_space);
@@ -1756,7 +1758,7 @@ gc_collect_region(PyThreadState *tstate,
17561758
gc_list_init(&unreachable);
17571759
deduce_unreachable(from, &unreachable);
17581760
validate_consistent_old_space(from);
1759-
untrack_tuples(from);
1761+
stats->untracked_tuples += untrack_tuples(from);
17601762

17611763
/* Move reachable objects to next generation. */
17621764
validate_consistent_old_space(to);
@@ -2098,12 +2100,22 @@ _PyGC_Collect(PyThreadState *tstate, int generation, _PyGC_Reason reason)
20982100
default:
20992101
Py_UNREACHABLE();
21002102
}
2103+
gcstate->generation_stats[generation].untracked_tuples += stats.untracked_tuples;
21012104
if (PyDTrace_GC_DONE_ENABLED()) {
21022105
PyDTrace_GC_DONE(stats.uncollectable + stats.collected);
21032106
}
21042107
if (reason != _Py_GC_REASON_SHUTDOWN) {
21052108
invoke_gc_callback(gcstate, "stop", generation, &stats);
21062109
}
2110+
else {
2111+
FILE *out = stderr;
2112+
for (int i = 0; i < NUM_GENERATIONS; i++) {
2113+
fprintf(out, "GC[%d] collections : %zd\n", i, gcstate->generation_stats[i].collections);
2114+
fprintf(out, "GC[%d] collected : %zd\n", i, gcstate->generation_stats[i].collected);
2115+
fprintf(out, "GC[%d] uncollectable : %zd\n", i, gcstate->generation_stats[i].uncollectable);
2116+
fprintf(out, "GC[%d] untracked_tuples: %zd\n", i, gcstate->generation_stats[i].untracked_tuples);
2117+
}
2118+
}
21072119
_PyErr_SetRaisedException(tstate, exc);
21082120
GC_STAT_ADD(generation, objects_collected, stats.collected);
21092121
#ifdef Py_STATS

0 commit comments

Comments
 (0)