Skip to content

Commit a79a283

Browse files
Count tracked tuples
1 parent ff81eb2 commit a79a283

File tree

3 files changed

+58
-15
lines changed

3 files changed

+58
-15
lines changed

Include/internal/pycore_interp_structs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ typedef struct {
165165
// Lowest two bits are used for flags documented later.
166166
// Those bits are made available by the struct's minimum alignment.
167167
uintptr_t _gc_prev;
168+
uintptr_t _visited;
168169
} PyGC_Head;
169170

170171
#define _PyGC_Head_UNUSED PyGC_Head
@@ -181,6 +182,7 @@ struct gc_collection_stats {
181182
Py_ssize_t collected;
182183
/* total number of uncollectable objects (put into gc.garbage) */
183184
Py_ssize_t uncollectable;
185+
Py_ssize_t tracked_tuples;
184186
Py_ssize_t untracked_tuples;
185187
};
186188

@@ -192,6 +194,8 @@ struct gc_generation_stats {
192194
Py_ssize_t collected;
193195
/* total number of uncollectable objects (put into gc.garbage) */
194196
Py_ssize_t uncollectable;
197+
Py_ssize_t tracked_tuples;
198+
Py_ssize_t total_tracked_tuples;
195199
Py_ssize_t untracked_tuples;
196200
Py_ssize_t total_untracked_tuples;
197201
Py_ssize_t total_tuples;

Include/internal/pycore_object.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -937,8 +937,8 @@ extern int _PyType_CacheInitForSpecialization(PyHeapTypeObject *type,
937937
# define MANAGED_DICT_OFFSET (((Py_ssize_t)sizeof(PyObject *))*-1)
938938
# define MANAGED_WEAKREF_OFFSET (((Py_ssize_t)sizeof(PyObject *))*-2)
939939
#else
940-
# define MANAGED_DICT_OFFSET (((Py_ssize_t)sizeof(PyObject *))*-3)
941-
# define MANAGED_WEAKREF_OFFSET (((Py_ssize_t)sizeof(PyObject *))*-4)
940+
# define MANAGED_DICT_OFFSET (((Py_ssize_t)sizeof(PyObject *))*-4)
941+
# define MANAGED_WEAKREF_OFFSET (((Py_ssize_t)sizeof(PyObject *))*-5)
942942
#endif
943943

944944
typedef union {

Python/gc.c

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,18 @@ gc_clear_collecting(PyGC_Head *g)
7070
g->_gc_prev &= ~PREV_MASK_COLLECTING;
7171
}
7272

73+
static inline int
74+
gc_is_visited(PyGC_Head *g)
75+
{
76+
return (int)g->_visited;
77+
}
78+
79+
static inline void
80+
gc_set_is_visited(PyGC_Head *g)
81+
{
82+
g->_visited = 1;
83+
}
84+
7385
static inline Py_ssize_t
7486
gc_get_refs(PyGC_Head *g)
7587
{
@@ -769,6 +781,25 @@ untrack_tuples(PyGC_Head *head)
769781
return untracked;
770782
}
771783

784+
static Py_ssize_t
785+
count_tuples(PyGC_Head *head)
786+
{
787+
Py_ssize_t tuples = 0;
788+
PyGC_Head *gc = GC_NEXT(head);
789+
while (gc != head) {
790+
PyObject *op = FROM_GC(gc);
791+
PyGC_Head *next = GC_NEXT(gc);
792+
if (!gc_is_visited(gc)) {
793+
if (PyTuple_CheckExact(op)) {
794+
tuples += 1;
795+
}
796+
gc_set_is_visited(gc);
797+
}
798+
gc = next;
799+
}
800+
return tuples;
801+
}
802+
772803
/* Return true if object has a pre-PEP 442 finalization method. */
773804
static int
774805
has_legacy_finalizer(PyObject *op)
@@ -1378,6 +1409,7 @@ gc_collect_young(PyThreadState *tstate,
13781409
validate_spaces(gcstate);
13791410
PyGC_Head *young = &gcstate->young.head;
13801411
PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head;
1412+
stats->tracked_tuples += count_tuples(young);
13811413
stats->untracked_tuples += untrack_tuples(young);
13821414
GC_STAT_ADD(0, collections, 1);
13831415

@@ -1656,6 +1688,7 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
16561688
GC_STAT_ADD(1, collections, 1);
16571689
GCState *gcstate = &tstate->interp->gc;
16581690
gcstate->work_to_do += assess_work_to_do(gcstate);
1691+
stats->tracked_tuples += count_tuples(&gcstate->young.head);
16591692
stats->untracked_tuples += untrack_tuples(&gcstate->young.head);
16601693
if (gcstate->phase == GC_PHASE_MARK) {
16611694
Py_ssize_t objects_marked = mark_at_start(tstate);
@@ -1718,6 +1751,7 @@ gc_collect_full(PyThreadState *tstate,
17181751
PyGC_Head *young = &gcstate->young.head;
17191752
PyGC_Head *pending = &gcstate->old[gcstate->visited_space^1].head;
17201753
PyGC_Head *visited = &gcstate->old[gcstate->visited_space].head;
1754+
stats->tracked_tuples += count_tuples(young);
17211755
stats->untracked_tuples += untrack_tuples(young);
17221756
/* merge all generations into visited */
17231757
gc_list_merge(young, pending);
@@ -1758,6 +1792,7 @@ gc_collect_region(PyThreadState *tstate,
17581792
gc_list_init(&unreachable);
17591793
deduce_unreachable(from, &unreachable);
17601794
validate_consistent_old_space(from);
1795+
stats->tracked_tuples += count_tuples(from);
17611796
stats->untracked_tuples += untrack_tuples(from);
17621797

17631798
/* Move reachable objects to next generation. */
@@ -2100,28 +2135,31 @@ _PyGC_Collect(PyThreadState *tstate, int generation, _PyGC_Reason reason)
21002135
default:
21012136
Py_UNREACHABLE();
21022137
}
2103-
gcstate->generation_stats[generation].untracked_tuples += stats.untracked_tuples;
2138+
gcstate->generation_stats[0].total_tracked_tuples += stats.tracked_tuples;
21042139
gcstate->generation_stats[0].total_untracked_tuples += stats.untracked_tuples;
2140+
gcstate->generation_stats[generation].tracked_tuples += stats.tracked_tuples;
2141+
gcstate->generation_stats[generation].untracked_tuples += stats.untracked_tuples;
21052142
if (PyDTrace_GC_DONE_ENABLED()) {
21062143
PyDTrace_GC_DONE(stats.uncollectable + stats.collected);
21072144
}
21082145
if (reason != _Py_GC_REASON_SHUTDOWN) {
21092146
invoke_gc_callback(gcstate, "stop", generation, &stats);
21102147
}
21112148
else {
2112-
FILE *out = stderr;
2113-
2114-
fprintf(out, "GC[%d] total tuples : %zd\n", 0, gcstate->generation_stats[0].total_tuples);
2115-
fprintf(out, "GC[%d] total untracked_tuples : %zd\n", 0, gcstate->generation_stats[0].total_untracked_tuples);
2116-
for (int i = 0; i < 33; i++) {
2117-
fprintf(out, "GC[%d] by size %d : %zd\n", 0, i, gcstate->generation_stats[0].tuples_by_size[i]);
2118-
}
2149+
if (true) {
2150+
FILE *out = stderr;
2151+
2152+
fprintf(out, "GC[%d] total tuples : %zd\n", 0, gcstate->generation_stats[0].total_tuples);
2153+
fprintf(out, "GC[%d] total tracked_tuples : %zd\n", 0, gcstate->generation_stats[0].total_tracked_tuples);
2154+
fprintf(out, "GC[%d] total untracked_tuples : %zd\n", 0, gcstate->generation_stats[0].total_untracked_tuples);
2155+
for (int i = 0; i < 33; i++) {
2156+
fprintf(out, "GC[%d] by size %d : %zd\n", 0, i, gcstate->generation_stats[0].tuples_by_size[i]);
2157+
}
21192158

2120-
for (int i = 0; i < NUM_GENERATIONS; i++) {
2121-
fprintf(out, "GC[%d] collections : %zd\n", i, gcstate->generation_stats[i].collections);
2122-
fprintf(out, "GC[%d] collected : %zd\n", i, gcstate->generation_stats[i].collected);
2123-
fprintf(out, "GC[%d] uncollectable : %zd\n", i, gcstate->generation_stats[i].uncollectable);
2124-
fprintf(out, "GC[%d] untracked_tuples: %zd\n", i, gcstate->generation_stats[i].untracked_tuples);
2159+
for (int i = 0; i < NUM_GENERATIONS; i++) {
2160+
fprintf(out, "GC[%d] tracked_tuples : %zd\n", i, gcstate->generation_stats[i].tracked_tuples);
2161+
fprintf(out, "GC[%d] untracked_tuples: %zd\n", i, gcstate->generation_stats[i].untracked_tuples);
2162+
}
21252163
}
21262164
}
21272165
_PyErr_SetRaisedException(tstate, exc);
@@ -2316,6 +2354,7 @@ _PyObject_GC_Link(PyObject *op)
23162354
GCState *gcstate = &tstate->interp->gc;
23172355
gc->_gc_next = 0;
23182356
gc->_gc_prev = 0;
2357+
gc->_visited = 0;
23192358
gcstate->young.count++; /* number of allocated GC objects */
23202359
gcstate->heap_size++;
23212360
if (gcstate->young.count > gcstate->young.threshold &&

0 commit comments

Comments
 (0)