Skip to content

gh-90230: Add stats for freelist use and more allocations. #92211

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Include/internal/pycore_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,12 @@ typedef struct _call_stats {

typedef struct _object_stats {
uint64_t allocations;
uint64_t allocations512;
uint64_t allocations4k;
uint64_t allocations_big;
uint64_t frees;
uint64_t to_freelist;
uint64_t from_freelist;
uint64_t new_values;
uint64_t dict_materialized_on_request;
uint64_t dict_materialized_new_key;
Expand All @@ -313,6 +318,8 @@ extern PyStats _py_stats;
#define OPCODE_EXE_INC(opname) _py_stats.opcode_stats[opname].execution_count++
#define CALL_STAT_INC(name) _py_stats.call_stats.name++
#define OBJECT_STAT_INC(name) _py_stats.object_stats.name++
#define OBJECT_STAT_INC_COND(name, cond) \
do { if (cond) _py_stats.object_stats.name++; } while (0)

extern void _Py_PrintSpecializationStats(int to_file);

Expand All @@ -325,6 +332,7 @@ PyAPI_FUNC(PyObject*) _Py_GetSpecializationStats(void);
#define OPCODE_EXE_INC(opname) ((void)0)
#define CALL_STAT_INC(name) ((void)0)
#define OBJECT_STAT_INC(name) ((void)0)
#define OBJECT_STAT_INC_COND(name, cond) ((void)0)
#endif // !Py_STATS

// Cache values are only valid in memory, so use native endianness.
Expand Down
5 changes: 5 additions & 0 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,7 @@ new_keys_object(uint8_t log2_size, bool unicode)
#endif
if (log2_size == PyDict_LOG_MINSIZE && unicode && state->keys_numfree > 0) {
dk = state->keys_free_list[--state->keys_numfree];
OBJECT_STAT_INC(from_freelist);
}
else
#endif
Expand Down Expand Up @@ -681,6 +682,7 @@ free_keys_object(PyDictKeysObject *keys)
&& state->keys_numfree < PyDict_MAXFREELIST
&& DK_IS_UNICODE(keys)) {
state->keys_free_list[state->keys_numfree++] = keys;
OBJECT_STAT_INC(to_freelist);
return;
}
#endif
Expand Down Expand Up @@ -726,6 +728,7 @@ new_dict(PyDictKeysObject *keys, PyDictValues *values, Py_ssize_t used, int free
mp = state->free_list[--state->numfree];
assert (mp != NULL);
assert (Py_IS_TYPE(mp, &PyDict_Type));
OBJECT_STAT_INC(from_freelist);
_Py_NewReference((PyObject *)mp);
}
else
Expand Down Expand Up @@ -1544,6 +1547,7 @@ dictresize(PyDictObject *mp, uint8_t log2_newsize, int unicode)
state->keys_numfree < PyDict_MAXFREELIST)
{
state->keys_free_list[state->keys_numfree++] = oldkeys;
OBJECT_STAT_INC(to_freelist);
}
else
#endif
Expand Down Expand Up @@ -2381,6 +2385,7 @@ dict_dealloc(PyDictObject *mp)
#endif
if (state->numfree < PyDict_MAXFREELIST && Py_IS_TYPE(mp, &PyDict_Type)) {
state->free_list[state->numfree++] = mp;
OBJECT_STAT_INC(to_freelist);
}
else
#endif
Expand Down
2 changes: 2 additions & 0 deletions Objects/floatobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ PyFloat_FromDouble(double fval)
#endif
state->free_list = (PyFloatObject *) Py_TYPE(op);
state->numfree--;
OBJECT_STAT_INC(from_freelist);
}
else
#endif
Expand Down Expand Up @@ -256,6 +257,7 @@ _PyFloat_ExactDealloc(PyObject *obj)
state->numfree++;
Py_SET_TYPE(op, (PyTypeObject *)state->free_list);
state->free_list = op;
OBJECT_STAT_INC(to_freelist);
#else
PyObject_Free(op);
#endif
Expand Down
2 changes: 2 additions & 0 deletions Objects/genobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1942,6 +1942,7 @@ async_gen_wrapped_val_dealloc(_PyAsyncGenWrappedValue *o)
if (state->value_numfree < _PyAsyncGen_MAXFREELIST) {
assert(_PyAsyncGenWrappedValue_CheckExact(o));
state->value_freelist[state->value_numfree++] = o;
OBJECT_STAT_INC(to_freelist);
}
else
#endif
Expand Down Expand Up @@ -2018,6 +2019,7 @@ _PyAsyncGenValueWrapperNew(PyObject *val)
if (state->value_numfree) {
state->value_numfree--;
o = state->value_freelist[state->value_numfree];
OBJECT_STAT_INC(from_freelist);
assert(_PyAsyncGenWrappedValue_CheckExact(o));
_Py_NewReference((PyObject*)o);
}
Expand Down
2 changes: 2 additions & 0 deletions Objects/listobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ PyList_New(Py_ssize_t size)
if (PyList_MAXFREELIST && state->numfree) {
state->numfree--;
op = state->free_list[state->numfree];
OBJECT_STAT_INC(from_freelist);
_Py_NewReference((PyObject *)op);
}
else
Expand Down Expand Up @@ -353,6 +354,7 @@ list_dealloc(PyListObject *op)
#endif
if (state->numfree < PyList_MAXFREELIST && PyList_CheckExact(op)) {
state->free_list[state->numfree++] = op;
OBJECT_STAT_INC(to_freelist);
}
else
#endif
Expand Down
15 changes: 15 additions & 0 deletions Objects/obmalloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,10 @@ PyMem_Malloc(size_t size)
/* see PyMem_RawMalloc() */
if (size > (size_t)PY_SSIZE_T_MAX)
return NULL;
OBJECT_STAT_INC_COND(allocations512, size < 512);
OBJECT_STAT_INC_COND(allocations4k, size >= 512 && size < 4094);
OBJECT_STAT_INC_COND(allocations_big, size >= 4094);
OBJECT_STAT_INC(allocations);
return _PyMem.malloc(_PyMem.ctx, size);
}

Expand All @@ -625,6 +629,10 @@ PyMem_Calloc(size_t nelem, size_t elsize)
/* see PyMem_RawMalloc() */
if (elsize != 0 && nelem > (size_t)PY_SSIZE_T_MAX / elsize)
return NULL;
OBJECT_STAT_INC_COND(allocations512, elsize < 512);
OBJECT_STAT_INC_COND(allocations4k, elsize >= 512 && elsize < 4094);
OBJECT_STAT_INC_COND(allocations_big, elsize >= 4094);
OBJECT_STAT_INC(allocations);
return _PyMem.calloc(_PyMem.ctx, nelem, elsize);
}

Expand All @@ -640,6 +648,7 @@ PyMem_Realloc(void *ptr, size_t new_size)
void
PyMem_Free(void *ptr)
{
OBJECT_STAT_INC(frees);
_PyMem.free(_PyMem.ctx, ptr);
}

Expand Down Expand Up @@ -696,6 +705,9 @@ PyObject_Malloc(size_t size)
/* see PyMem_RawMalloc() */
if (size > (size_t)PY_SSIZE_T_MAX)
return NULL;
OBJECT_STAT_INC_COND(allocations512, size < 512);
OBJECT_STAT_INC_COND(allocations4k, size >= 512 && size < 4094);
OBJECT_STAT_INC_COND(allocations_big, size >= 4094);
OBJECT_STAT_INC(allocations);
return _PyObject.malloc(_PyObject.ctx, size);
}
Expand All @@ -706,6 +718,9 @@ PyObject_Calloc(size_t nelem, size_t elsize)
/* see PyMem_RawMalloc() */
if (elsize != 0 && nelem > (size_t)PY_SSIZE_T_MAX / elsize)
return NULL;
OBJECT_STAT_INC_COND(allocations512, elsize < 512);
OBJECT_STAT_INC_COND(allocations4k, elsize >= 512 && elsize < 4094);
OBJECT_STAT_INC_COND(allocations_big, elsize >= 4094);
OBJECT_STAT_INC(allocations);
return _PyObject.calloc(_PyObject.ctx, nelem, elsize);
}
Expand Down
2 changes: 2 additions & 0 deletions Objects/tupleobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,7 @@ maybe_freelist_pop(Py_ssize_t size)
#endif
_Py_NewReference((PyObject *)op);
/* END inlined _PyObject_InitVar() */
OBJECT_STAT_INC(from_freelist);
return op;
}
}
Expand Down Expand Up @@ -1224,6 +1225,7 @@ maybe_freelist_push(PyTupleObject *op)
op->ob_item[0] = (PyObject *) STATE.free_list[index];
STATE.free_list[index] = op;
STATE.numfree[index]++;
OBJECT_STAT_INC(to_freelist);
return 1;
}
#endif
Expand Down
2 changes: 2 additions & 0 deletions Python/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ _context_alloc(void)
state->numfree--;
ctx = state->freelist;
state->freelist = (PyContext *)ctx->ctx_weakreflist;
OBJECT_STAT_INC(from_freelist);
ctx->ctx_weakreflist = NULL;
_Py_NewReference((PyObject *)ctx);
}
Expand Down Expand Up @@ -482,6 +483,7 @@ context_tp_dealloc(PyContext *self)
state->numfree++;
self->ctx_weakreflist = (PyObject *)state->freelist;
state->freelist = self;
OBJECT_STAT_INC(to_freelist);
}
else
#endif
Expand Down
5 changes: 5 additions & 0 deletions Python/specialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,12 @@ print_call_stats(FILE *out, CallStats *stats)
static void
print_object_stats(FILE *out, ObjectStats *stats)
{
fprintf(out, "Object allocations from freelist: %" PRIu64 "\n", stats->from_freelist);
fprintf(out, "Object frees to freelist: %" PRIu64 "\n", stats->to_freelist);
fprintf(out, "Object allocations: %" PRIu64 "\n", stats->allocations);
fprintf(out, "Object allocations to 512 bytes: %" PRIu64 "\n", stats->allocations512);
fprintf(out, "Object allocations to 4 kbytes: %" PRIu64 "\n", stats->allocations4k);
fprintf(out, "Object allocations over 4 kbytes: %" PRIu64 "\n", stats->allocations_big);
fprintf(out, "Object frees: %" PRIu64 "\n", stats->frees);
fprintf(out, "Object new values: %" PRIu64 "\n", stats->new_values);
fprintf(out, "Object materialize dict (on request): %" PRIu64 "\n", stats->dict_materialized_on_request);
Expand Down