From 187930f74c44e460ba09c60ba5d9bb4fac543d8f Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 2 Feb 2022 11:01:33 +0000 Subject: [PATCH] bpo-46072: Add some frame stats. (GH-31060) --- Include/internal/pycore_code.h | 2 ++ Objects/frameobject.c | 1 + Python/ceval.c | 5 ++++- Python/frame.c | 2 ++ Python/pystate.c | 2 ++ Python/specialize.c | 2 ++ Tools/scripts/summarize_stats.py | 3 +++ 7 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 45c7752112b..3897ea0ab6a 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -303,6 +303,8 @@ typedef struct _opcode_stats { typedef struct _call_stats { uint64_t inlined_py_calls; uint64_t pyeval_calls; + uint64_t frames_pushed; + uint64_t frame_objects_created; } CallStats; typedef struct _object_stats { diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 81ad4cc65d1..15da1325d14 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -794,6 +794,7 @@ init_frame(InterpreterFrame *frame, PyFunctionObject *func, PyObject *locals) PyFrameObject* _PyFrame_New_NoTrack(PyCodeObject *code) { + CALL_STAT_INC(frame_objects_created); int slots = code->co_nlocalsplus + code->co_stacksize; PyFrameObject *f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, slots); if (f == NULL) { diff --git a/Python/ceval.c b/Python/ceval.c index b69d5aa9d32..70748e8911f 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2242,6 +2242,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr if (new_frame == NULL) { goto error; } + CALL_STAT_INC(frames_pushed); _PyFrame_InitializeSpecials(new_frame, getitem, NULL, code->co_nlocalsplus); STACK_SHRINK(2); @@ -4660,6 +4661,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr if (new_frame == NULL) { goto error; } + CALL_STAT_INC(inlined_py_calls); STACK_SHRINK(argcount); for (int i = 0; i < argcount; i++) { new_frame->localsplus[i] = stack_pointer[i]; @@ -4690,6 +4692,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr if (new_frame == NULL) { goto error; } + CALL_STAT_INC(inlined_py_calls); STACK_SHRINK(argcount); for (int i = 0; i < argcount; i++) { new_frame->localsplus[i] = stack_pointer[i]; @@ -4708,7 +4711,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr _PyFrame_SetStackPointer(frame, stack_pointer); new_frame->previous = frame; frame = cframe.current_frame = new_frame; - CALL_STAT_INC(inlined_py_calls); goto start_frame; } @@ -6078,6 +6080,7 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, { PyCodeObject * code = (PyCodeObject *)func->func_code; size_t size = code->co_nlocalsplus + code->co_stacksize + FRAME_SPECIALS_SIZE; + CALL_STAT_INC(frames_pushed); InterpreterFrame *frame = _PyThreadState_BumpFramePointer(tstate, size); if (frame == NULL) { goto fail; diff --git a/Python/frame.c b/Python/frame.c index 771de7583be..ca7c5f9c94e 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -1,6 +1,7 @@ #include "Python.h" #include "frameobject.h" +#include "pycore_code.h" // stats #include "pycore_frame.h" #include "pycore_object.h" // _PyObject_GC_UNTRACK() #include "opcode.h" @@ -113,6 +114,7 @@ _PyFrame_Push(PyThreadState *tstate, PyFunctionObject *func) { PyCodeObject *code = (PyCodeObject *)func->func_code; size_t size = code->co_nlocalsplus + code->co_stacksize + FRAME_SPECIALS_SIZE; + CALL_STAT_INC(frames_pushed); InterpreterFrame *new_frame = _PyThreadState_BumpFramePointer(tstate, size); if (new_frame == NULL) { return NULL; diff --git a/Python/pystate.c b/Python/pystate.c index 4378d78a8b7..77467944e2a 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -3,6 +3,7 @@ #include "Python.h" #include "pycore_ceval.h" +#include "pycore_code.h" // stats #include "pycore_frame.h" #include "pycore_initconfig.h" #include "pycore_object.h" // _PyType_InitCache() @@ -2219,6 +2220,7 @@ _PyThreadState_PushFrame(PyThreadState *tstate, PyFunctionObject *func, PyObject int nlocalsplus = code->co_nlocalsplus; size_t size = nlocalsplus + code->co_stacksize + FRAME_SPECIALS_SIZE; + CALL_STAT_INC(frames_pushed); InterpreterFrame *frame = _PyThreadState_BumpFramePointer(tstate, size); if (frame == NULL) { return NULL; diff --git a/Python/specialize.c b/Python/specialize.c index 5771a41dcfd..9290fbe8239 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -169,6 +169,8 @@ print_call_stats(FILE *out, CallStats *stats) { fprintf(out, "Calls to PyEval_EvalDefault: %" PRIu64 "\n", stats->pyeval_calls); fprintf(out, "Calls to Python functions inlined: %" PRIu64 "\n", stats->inlined_py_calls); + fprintf(out, "Frames pushed: %" PRIu64 "\n", stats->frames_pushed); + fprintf(out, "Frame objects created: %" PRIu64 "\n", stats->frame_objects_created); } static void diff --git a/Tools/scripts/summarize_stats.py b/Tools/scripts/summarize_stats.py index 2761dcb5098..319b251c854 100644 --- a/Tools/scripts/summarize_stats.py +++ b/Tools/scripts/summarize_stats.py @@ -106,6 +106,9 @@ def main(): for key, value in stats.items(): if "Calls to" in key: print(f" {key}: {value} {100*value/total:0.1f}%") + for key, value in stats.items(): + if key.startswith("Frame"): + print(f" {key}: {value} {100*value/total:0.1f}%") print("Object stats:") total = stats.get("Object new values") for key, value in stats.items():