Skip to content

bpo-46072: Add top level stats struct #30169

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 4 commits into from
Dec 17, 2021
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
29 changes: 21 additions & 8 deletions Include/internal/pycore_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,27 +281,40 @@ void _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,

#define SPECIALIZATION_FAILURE_KINDS 30

typedef struct _stats {
uint64_t specialization_success;
uint64_t specialization_failure;
typedef struct _specialization_stats {
uint64_t success;
uint64_t failure;
uint64_t hit;
uint64_t deferred;
uint64_t miss;
uint64_t deopt;
uint64_t unquickened;
uint64_t specialization_failure_kinds[SPECIALIZATION_FAILURE_KINDS];
uint64_t failure_kinds[SPECIALIZATION_FAILURE_KINDS];
} SpecializationStats;

extern SpecializationStats _specialization_stats[256];
#define STAT_INC(opname, name) _specialization_stats[opname].name++
#define STAT_DEC(opname, name) _specialization_stats[opname].name--
typedef struct _opcode_stats {
SpecializationStats specialization;
uint64_t execution_count;
uint64_t pair_count[256];
} OpcodeStats;

typedef struct _stats {
OpcodeStats opcode_stats[256];
} PyStats;

extern PyStats _py_stats;

#define STAT_INC(opname, name) _py_stats.opcode_stats[opname].specialization.name++
#define STAT_DEC(opname, name) _py_stats.opcode_stats[opname].specialization.name--
#define OPCODE_EXE_INC(opname) _py_stats.opcode_stats[opname].execution_count++

void _Py_PrintSpecializationStats(int to_file);

PyAPI_FUNC(PyObject*) _Py_GetSpecializationStats(void);

#else
#define STAT_INC(opname, name) ((void)0)
#define STAT_DEC(opname, name) ((void)0)
#define OPCODE_EXE_INC(opname) ((void)0)
#endif


Expand Down
5 changes: 2 additions & 3 deletions Lib/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,11 +288,10 @@ def jabs_op(name, op):
"STORE_FAST__STORE_FAST",
]
_specialization_stats = [
"specialization_success",
"specialization_failure",
"success",
"failure",
"hit",
"deferred",
"miss",
"deopt",
"unquickened",
]
6 changes: 3 additions & 3 deletions Lib/test/test__opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,13 @@ def test_specialization_stats(self):
self.assertCountEqual(stats.keys(), specialized_opcodes)
self.assertCountEqual(
stats['load_attr'].keys(),
stat_names + ['specialization_failure_kinds'])
stat_names + ['failure_kinds'])
for sn in stat_names:
self.assertIsInstance(stats['load_attr'][sn], int)
self.assertIsInstance(
stats['load_attr']['specialization_failure_kinds'],
stats['load_attr']['failure_kinds'],
tuple)
for v in stats['load_attr']['specialization_failure_kinds']:
for v in stats['load_attr']['failure_kinds']:
self.assertIsInstance(v, int)


Expand Down
31 changes: 8 additions & 23 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -1308,13 +1308,17 @@ eval_frame_handle_pending(PyThreadState *tstate)
#define USE_COMPUTED_GOTOS 0
#endif

#define INSTRUCTION_START() frame->f_lasti = INSTR_OFFSET(); next_instr++
#ifdef Py_STATS
#define INSTRUCTION_START(op) frame->f_lasti = INSTR_OFFSET(); next_instr++; OPCODE_EXE_INC(op);
#else
#define INSTRUCTION_START(op) frame->f_lasti = INSTR_OFFSET(); next_instr++
#endif

#if USE_COMPUTED_GOTOS
#define TARGET(op) TARGET_##op: INSTRUCTION_START();
#define TARGET(op) TARGET_##op: INSTRUCTION_START(op);
#define DISPATCH_GOTO() goto *opcode_targets[opcode]
#else
#define TARGET(op) case op: INSTRUCTION_START();
#define TARGET(op) case op: INSTRUCTION_START(op);
#define DISPATCH_GOTO() goto dispatch_opcode
#endif

Expand Down Expand Up @@ -1434,7 +1438,7 @@ eval_frame_handle_pending(PyThreadState *tstate)
opcode = _Py_OPCODE(word) | cframe.use_tracing OR_DTRACE_LINE; \
if (opcode == op) { \
oparg = _Py_OPARG(word); \
INSTRUCTION_START(); \
INSTRUCTION_START(op); \
goto PREDICT_ID(op); \
} \
} while(0)
Expand Down Expand Up @@ -2204,7 +2208,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr

TARGET(BINARY_SUBSCR) {
PREDICTED(BINARY_SUBSCR);
STAT_INC(BINARY_SUBSCR, unquickened);
PyObject *sub = POP();
PyObject *container = TOP();
PyObject *res = PyObject_GetItem(container, sub);
Expand Down Expand Up @@ -2232,7 +2235,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
cache->adaptive.counter--;
assert(cache->adaptive.original_oparg == 0);
/* No need to set oparg here; it isn't used by BINARY_SUBSCR */
STAT_DEC(BINARY_SUBSCR, unquickened);
JUMP_TO_INSTRUCTION(BINARY_SUBSCR);
}
}
Expand Down Expand Up @@ -2357,7 +2359,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr

TARGET(STORE_SUBSCR) {
PREDICTED(STORE_SUBSCR);
STAT_INC(STORE_SUBSCR, unquickened);
PyObject *sub = TOP();
PyObject *container = SECOND();
PyObject *v = THIRD();
Expand Down Expand Up @@ -2387,7 +2388,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
STAT_INC(STORE_SUBSCR, deferred);
// oparg is the adaptive cache counter
UPDATE_PREV_INSTR_OPARG(next_instr, oparg - 1);
STAT_DEC(STORE_SUBSCR, unquickened);
JUMP_TO_INSTRUCTION(STORE_SUBSCR);
}
}
Expand Down Expand Up @@ -2983,7 +2983,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr

TARGET(STORE_ATTR) {
PREDICTED(STORE_ATTR);
STAT_INC(STORE_ATTR, unquickened);
PyObject *name = GETITEM(names, oparg);
PyObject *owner = TOP();
PyObject *v = SECOND();
Expand Down Expand Up @@ -3099,7 +3098,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr

TARGET(LOAD_GLOBAL) {
PREDICTED(LOAD_GLOBAL);
STAT_INC(LOAD_GLOBAL, unquickened);
PyObject *name = GETITEM(names, oparg);
PyObject *v;
if (PyDict_CheckExact(GLOBALS())
Expand Down Expand Up @@ -3162,7 +3160,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
STAT_INC(LOAD_GLOBAL, deferred);
cache->adaptive.counter--;
oparg = cache->adaptive.original_oparg;
STAT_DEC(LOAD_GLOBAL, unquickened);
JUMP_TO_INSTRUCTION(LOAD_GLOBAL);
}
}
Expand Down Expand Up @@ -3582,7 +3579,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr

TARGET(LOAD_ATTR) {
PREDICTED(LOAD_ATTR);
STAT_INC(LOAD_ATTR, unquickened);
PyObject *name = GETITEM(names, oparg);
PyObject *owner = TOP();
PyObject *res = PyObject_GetAttr(owner, name);
Expand Down Expand Up @@ -3610,7 +3606,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
STAT_INC(LOAD_ATTR, deferred);
cache->adaptive.counter--;
oparg = cache->adaptive.original_oparg;
STAT_DEC(LOAD_ATTR, unquickened);
JUMP_TO_INSTRUCTION(LOAD_ATTR);
}
}
Expand Down Expand Up @@ -3713,7 +3708,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
STAT_INC(STORE_ATTR, deferred);
cache->adaptive.counter--;
oparg = cache->adaptive.original_oparg;
STAT_DEC(STORE_ATTR, unquickened);
JUMP_TO_INSTRUCTION(STORE_ATTR);
}
}
Expand Down Expand Up @@ -3804,7 +3798,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr

TARGET(COMPARE_OP) {
PREDICTED(COMPARE_OP);
STAT_INC(COMPARE_OP, unquickened);
assert(oparg <= Py_GE);
PyObject *right = POP();
PyObject *left = TOP();
Expand Down Expand Up @@ -3833,7 +3826,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
STAT_INC(COMPARE_OP, deferred);
cache->adaptive.counter--;
oparg = cache->adaptive.original_oparg;
STAT_DEC(COMPARE_OP, unquickened);
JUMP_TO_INSTRUCTION(COMPARE_OP);
}
}
Expand Down Expand Up @@ -4535,7 +4527,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr

TARGET(LOAD_METHOD) {
PREDICTED(LOAD_METHOD);
STAT_INC(LOAD_METHOD, unquickened);
/* Designed to work in tandem with CALL_METHOD. */
PyObject *name = GETITEM(names, oparg);
PyObject *obj = TOP();
Expand Down Expand Up @@ -4588,7 +4579,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
STAT_INC(LOAD_METHOD, deferred);
cache->adaptive.counter--;
oparg = cache->adaptive.original_oparg;
STAT_DEC(LOAD_METHOD, unquickened);
JUMP_TO_INSTRUCTION(LOAD_METHOD);
}
}
Expand Down Expand Up @@ -4714,7 +4704,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
TARGET(CALL_NO_KW) {
PyObject *function;
PREDICTED(CALL_NO_KW);
STAT_INC(CALL_NO_KW, unquickened);
kwnames = NULL;
oparg += extra_args;
nargs = oparg;
Expand Down Expand Up @@ -5283,7 +5272,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr

TARGET(BINARY_OP) {
PREDICTED(BINARY_OP);
STAT_INC(BINARY_OP, unquickened);
PyObject *rhs = POP();
PyObject *lhs = TOP();
assert(0 <= oparg);
Expand Down Expand Up @@ -5313,7 +5301,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
STAT_INC(BINARY_OP, deferred);
cache->adaptive.counter--;
oparg = cache->adaptive.original_oparg;
STAT_DEC(BINARY_OP, unquickened);
JUMP_TO_INSTRUCTION(BINARY_OP);
}
}
Expand Down Expand Up @@ -5398,7 +5385,6 @@ opname ## _miss: \
cache_backoff(cache); \
} \
oparg = cache->original_oparg; \
STAT_DEC(opname, unquickened); \
JUMP_TO_INSTRUCTION(opname); \
}

Expand All @@ -5414,7 +5400,6 @@ opname ## _miss: \
next_instr[-1] = _Py_MAKECODEUNIT(opname ## _ADAPTIVE, oparg); \
STAT_INC(opname, deopt); \
} \
STAT_DEC(opname, unquickened); \
JUMP_TO_INSTRUCTION(opname); \
}

Expand Down
Loading