Skip to content

Commit f86796d

Browse files
Reduce size of stats buffers
1 parent 2db2df6 commit f86796d

3 files changed

Lines changed: 73 additions & 42 deletions

File tree

Include/internal/pycore_interp_structs.h

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -179,31 +179,41 @@ struct gc_generation {
179179

180180
/* Running stats per generation */
181181
struct gc_generation_stats {
182-
PyTime_t ts;
182+
PyTime_t ts_start;
183+
PyTime_t ts_stop;
184+
185+
/* heap_size on the start of the collection */
186+
Py_ssize_t heap_size;
187+
188+
/* work_to_do on the start of the collection */
189+
Py_ssize_t work_to_do;
190+
183191
/* total number of collections */
184192
Py_ssize_t collections;
185193

194+
/* total number of visited objects */
195+
Py_ssize_t object_visits;
196+
186197
/* total number of collected objects */
187198
Py_ssize_t collected;
188199
/* total number of uncollectable objects (put into gc.garbage) */
189200
Py_ssize_t uncollectable;
190201
// Total number of objects considered for collection and traversed:
191202
Py_ssize_t candidates;
192-
// Duration of the collection in seconds:
193203

194-
Py_ssize_t object_visits;
195204
Py_ssize_t objects_transitively_reachable;
196205
Py_ssize_t objects_not_transitively_reachable;
206+
};
197207

198-
Py_ssize_t heap_size;
199-
Py_ssize_t work_to_do;
200-
201-
double duration;
202-
double total_duration;
208+
#define GC_YOUNG_STATS_SIZE 11
209+
#define GC_OLD_STATS_SIZE 3
210+
struct gc_young_stats_buffer {
211+
struct gc_generation_stats items[GC_YOUNG_STATS_SIZE];
212+
int8_t index;
203213
};
204214

205-
struct gc_generation_stats_buffer {
206-
struct gc_generation_stats items[11];
215+
struct gc_old_stats_buffer {
216+
struct gc_generation_stats items[GC_OLD_STATS_SIZE];
207217
int8_t index;
208218
};
209219

@@ -217,7 +227,8 @@ enum _GCPhase {
217227
#define NUM_GENERATIONS 3
218228

219229
struct gc_stats {
220-
struct gc_generation_stats_buffer gen[NUM_GENERATIONS];
230+
struct gc_young_stats_buffer young;
231+
struct gc_old_stats_buffer old[2];
221232
};
222233

223234
struct _gc_runtime_state {

Modules/gcmodule.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -342,16 +342,15 @@ gc_get_stats_impl(PyObject *module)
342342
/*[clinic end generated code: output=a8ab1d8a5d26f3ab input=1ef4ed9d17b1a470]*/
343343
{
344344
int i;
345+
double duration;
345346
struct gc_generation_stats stats[NUM_GENERATIONS], *st;
346347

347348
/* To get consistent values despite allocations while constructing
348349
the result list, we use a snapshot of the running stats. */
349350
GCState *gcstate = get_gc_state();
350-
struct gc_generation_stats_buffer *buffer;
351-
for (i = 0; i < NUM_GENERATIONS; i++) {
352-
buffer = &gcstate->generation_stats.gen[i];
353-
stats[i] = buffer->items[buffer->index];
354-
}
351+
stats[0] = gcstate->generation_stats.young.items[gcstate->generation_stats.young.index];
352+
stats[1] = gcstate->generation_stats.old[0].items[gcstate->generation_stats.old[0].index];
353+
stats[2] = gcstate->generation_stats.old[1].items[gcstate->generation_stats.old[1].index];
355354

356355
PyObject *result = PyList_New(0);
357356
if (result == NULL)
@@ -360,12 +359,13 @@ gc_get_stats_impl(PyObject *module)
360359
for (i = 0; i < NUM_GENERATIONS; i++) {
361360
PyObject *dict;
362361
st = &stats[i];
362+
duration = PyTime_AsSecondsDouble(st->ts_stop - st->ts_start);
363363
dict = Py_BuildValue("{snsnsnsnsd}",
364364
"collections", st->collections,
365365
"collected", st->collected,
366366
"uncollectable", st->uncollectable,
367367
"candidates", st->candidates,
368-
"duration", st->duration
368+
"duration", duration
369369
);
370370
if (dict == NULL)
371371
goto error;

Python/gc.c

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,18 +1394,33 @@ gc_list_set_space(PyGC_Head *list, int space)
13941394
static struct gc_generation_stats *
13951395
gc_get_stats(GCState *gcstate, int gen)
13961396
{
1397-
struct gc_generation_stats_buffer *buffer = &gcstate->generation_stats.gen[gen];
1398-
buffer->index = (buffer->index + 1) % 11;
1399-
struct gc_generation_stats *stats = &buffer->items[buffer->index];
1400-
return stats;
1397+
if (gen == 0) {
1398+
struct gc_young_stats_buffer *buffer = &gcstate->generation_stats.young;
1399+
buffer->index = (buffer->index + 1) % GC_YOUNG_STATS_SIZE;
1400+
struct gc_generation_stats *stats = &buffer->items[buffer->index];
1401+
return stats;
1402+
}
1403+
else {
1404+
struct gc_old_stats_buffer *buffer = &gcstate->generation_stats.old[gen - 1];
1405+
buffer->index = (buffer->index + 1) % GC_OLD_STATS_SIZE;
1406+
struct gc_generation_stats *stats = &buffer->items[buffer->index];
1407+
return stats;
1408+
}
14011409
}
14021410

14031411
static struct gc_generation_stats *
14041412
gc_get_prev_stats(GCState *gcstate, int gen)
14051413
{
1406-
struct gc_generation_stats_buffer *buffer = &gcstate->generation_stats.gen[gen];
1407-
struct gc_generation_stats *stats = &buffer->items[buffer->index];
1408-
return stats;
1414+
if (gen == 0) {
1415+
struct gc_young_stats_buffer *buffer = &gcstate->generation_stats.young;
1416+
struct gc_generation_stats *stats = &buffer->items[buffer->index];
1417+
return stats;
1418+
}
1419+
else {
1420+
struct gc_old_stats_buffer *buffer = &gcstate->generation_stats.old[gen - 1];
1421+
struct gc_generation_stats *stats = &buffer->items[buffer->index];
1422+
return stats;
1423+
}
14091424
}
14101425

14111426
static void
@@ -1414,18 +1429,21 @@ add_stats(GCState *gcstate, int gen, struct gc_generation_stats *stats)
14141429
struct gc_generation_stats *prev_stats = gc_get_prev_stats(gcstate, gen);
14151430
struct gc_generation_stats *cur_stats = gc_get_stats(gcstate, gen);
14161431

1417-
cur_stats->ts = stats->ts;
1418-
cur_stats->collections = prev_stats->collections + 1;
1419-
cur_stats->object_visits = prev_stats->object_visits + stats->object_visits;
1420-
cur_stats->collected = prev_stats->collected + stats->collected;
1421-
cur_stats->objects_transitively_reachable = prev_stats->objects_transitively_reachable + stats->objects_transitively_reachable;
1422-
cur_stats->objects_not_transitively_reachable = prev_stats->objects_not_transitively_reachable + stats->objects_not_transitively_reachable;
1423-
cur_stats->uncollectable = prev_stats->uncollectable + stats->uncollectable;
1424-
cur_stats->candidates = prev_stats->candidates + stats->candidates;
1425-
cur_stats->duration = stats->duration;
1426-
cur_stats->total_duration = prev_stats->total_duration + stats->duration;
1427-
cur_stats->heap_size = gcstate->heap_size;
1428-
cur_stats->work_to_do = gcstate->work_to_do;
1432+
memcpy(cur_stats, prev_stats, sizeof(struct gc_generation_stats));
1433+
1434+
cur_stats->ts_start = stats->ts_start;
1435+
cur_stats->ts_stop = stats->ts_stop;
1436+
cur_stats->heap_size = stats->heap_size;
1437+
cur_stats->work_to_do = stats->work_to_do;
1438+
1439+
cur_stats->collections += 1;
1440+
cur_stats->object_visits += stats->object_visits;
1441+
cur_stats->collected += stats->collected;
1442+
cur_stats->uncollectable += stats->uncollectable;
1443+
cur_stats->candidates += stats->candidates;
1444+
1445+
cur_stats->objects_transitively_reachable += stats->objects_transitively_reachable;
1446+
cur_stats->objects_not_transitively_reachable += stats->objects_not_transitively_reachable;
14291447
}
14301448

14311449
static void
@@ -1907,12 +1925,13 @@ do_gc_callback(GCState *gcstate, const char *phase,
19071925
assert(PyList_CheckExact(gcstate->callbacks));
19081926
PyObject *info = NULL;
19091927
if (PyList_GET_SIZE(gcstate->callbacks) != 0) {
1928+
double duration = PyTime_AsSecondsDouble(stats->ts_stop - stats->ts_start);
19101929
info = Py_BuildValue("{sisnsnsnsd}",
19111930
"generation", generation,
19121931
"collected", stats->collected,
19131932
"uncollectable", stats->uncollectable,
19141933
"candidates", stats->candidates,
1915-
"duration", stats->duration);
1934+
"duration", duration);
19161935
if (info == NULL) {
19171936
PyErr_FormatUnraisable("Exception ignored while invoking gc callbacks");
19181937
return;
@@ -2150,7 +2169,9 @@ _PyGC_Collect(PyThreadState *tstate, int generation, _PyGC_Reason reason)
21502169
if (PyDTrace_GC_START_ENABLED()) {
21512170
PyDTrace_GC_START(generation);
21522171
}
2153-
(void)PyTime_PerfCounterRaw(&stats.ts);
2172+
stats.heap_size = gcstate->heap_size;
2173+
stats.work_to_do = gcstate->work_to_do;
2174+
(void)PyTime_PerfCounterRaw(&stats.ts_start);
21542175
PyObject *exc = _PyErr_GetRaisedException(tstate);
21552176
switch(generation) {
21562177
case 0:
@@ -2165,9 +2186,7 @@ _PyGC_Collect(PyThreadState *tstate, int generation, _PyGC_Reason reason)
21652186
default:
21662187
Py_UNREACHABLE();
21672188
}
2168-
PyTime_t stop;
2169-
(void)PyTime_PerfCounterRaw(&stop);
2170-
stats.duration = PyTime_AsSecondsDouble(stop - stats.ts);
2189+
(void)PyTime_PerfCounterRaw(&stats.ts_stop);
21712190
add_stats(gcstate, generation, &stats);
21722191
if (PyDTrace_GC_DONE_ENABLED()) {
21732192
PyDTrace_GC_DONE(stats.uncollectable + stats.collected);
@@ -2190,9 +2209,10 @@ _PyGC_Collect(PyThreadState *tstate, int generation, _PyGC_Reason reason)
21902209
_Py_atomic_store_int(&gcstate->collecting, 0);
21912210

21922211
if (gcstate->debug & _PyGC_DEBUG_STATS) {
2212+
double duration = PyTime_AsSecondsDouble(stats.ts_stop - stats.ts_start);
21932213
PySys_WriteStderr(
21942214
"gc: done, %zd unreachable, %zd uncollectable, %.4fs elapsed\n",
2195-
stats.collected + stats.uncollectable, stats.uncollectable, stats.duration
2215+
stats.collected + stats.uncollectable, stats.uncollectable, duration
21962216
);
21972217
}
21982218

0 commit comments

Comments
 (0)