8000 Tier 2 cleanups and tweaks (#115534) · python/cpython@142502e · GitHub
[go: up one dir, main page]

Skip to content

Commit 142502e

Browse files
authored
Tier 2 cleanups and tweaks (#115534)
* Rename `_testinternalcapi.get_{uop,counter}_optimizer` to `new_*_optimizer` * Use `_PyUOpName()` instead of` _PyOpcode_uop_name[]` * Add `target` to executor iterator items -- `list(ex)` now returns `(opcode, oparg, target, operand)` quadruples * Add executor methods `get_opcode()` and `get_oparg()` to get `vmdata.opcode`, `vmdata.oparg` * Define a helper for printing uops, and unify various places where they are printed * Add a hack to summarize_stats.py to fix legacy uop names (e.g. `POP_TOP` -> `_POP_TOP`) * Define helpers in `test_opt.py` for accessing the set or list of opnames of an executor
1 parent 520403e commit 142502e

File tree

8 files changed

+203
-121
lines changed

8 files changed

+203
-121
lines changed

Lib/test/test_capi/test_opt.py

Lines changed: 85 additions & 72 deletions
Large diffs are not rendered by default.

Lib/test/test_monitoring.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1799,7 +1799,7 @@ class TestOptimizer(MonitoringTestBase, unittest.TestCase):
17991799
def setUp(self):
18001800
import _testinternalcapi
18011801
self.old_opt = _testinternalcapi.get_optimizer()
1802-
opt = _testinternalcapi.get_counter_optimizer()
1802+
opt = _testinternalcapi.new_counter_optimizer()
18031803
_testinternalcapi.set_optimizer(opt)
18041804
super(TestOptimizer, self).setUp()
18051805

Modules/_testinternalcapi.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -960,13 +960,13 @@ iframe_getlasti(PyObject *self, PyObject *frame)
960960
}
961961

962962
static PyObject *
963-
get_counter_optimizer(PyObject *self, PyObject *arg)
963+
new_counter_optimizer(PyObject *self, PyObject *arg)
964964
{
965965
return PyUnstable_Optimizer_NewCounter();
966966
}
967967

968968
static PyObject *
969-
get_uop_optimizer(PyObject *self, PyObject *arg)
969+
new_uop_optimizer(PyObject *self, PyObject *arg)
970970
{
971971
return PyUnstable_Optimizer_NewUOpOptimizer();
972972
}
@@ -1711,8 +1711,8 @@ static PyMethodDef module_functions[] = {
17111711
{"get_optimizer", get_optimizer, METH_NOARGS, NULL},
17121712
{"set_optimizer", set_optimizer, METH_O, NULL},
17131713
{"get_executor", _PyCFunction_CAST(get_executor), METH_FASTCALL, NULL},
1714-
{"get_counter_optimizer", get_counter_optimizer, METH_NOARGS, NULL},
1715-
{"get_uop_optimizer", get_uop_optimizer, METH_NOARGS, NULL},
1714+
{"new_counter_optimizer", new_counter_optimizer, METH_NOARGS, NULL},
1715+
{"new_uop_optimizer", new_uop_optimizer, METH_NOARGS, NULL},
17161716
{"add_executor_dependency", add_executor_dependency, METH_VARARGS, NULL},
17171717
{"invalidate_executors", invalidate_executors, METH_O, NULL},
17181718
{"pending_threadfunc", _PyCFunction_CAST(pending_threadfunc),

Python/ceval.c

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,10 @@ static const _Py_CODEUNIT _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS[] = {
649649

650650
extern const struct _PyCode_DEF(8) _Py_InitCleanup;
651651

652-
extern const char *_PyUOpName(int index);
652+
#ifdef Py_DEBUG
653+
extern void _PyUOpPrint(const _PyUOpInstruction *uop);
654+
#endif
655+
653656

654657
/* Disable unused label warnings. They are handy for debugging, even
655658
if computed gotos aren't used. */
@@ -1006,14 +1009,14 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
10061009
assert(next_uop->opcode == _START_EXECUTOR || next_uop->opcode == _COLD_EXIT);
10071010
for (;;) {
10081011
uopcode = next_uop->opcode;
1009-
DPRINTF(3,
1010-
"%4d: uop %s, oparg %d, operand %" PRIu64 ", target %d, stack_level %d\n",
1011-
(int)(next_uop - (current_executor == NULL ? next_uop : current_executor->trace)),
1012-
_PyUOpName(uopcode),
1013-
next_uop->oparg,
1014-
next_uop->operand,
1015-
next_uop->target,
1012+
#ifdef Py_DEBUG
1013+
if (lltrace >= 3) {
1014+
printf("%4d uop: ", (int)(next_uop - (current_executor == NULL ? next_uop : current_executor->trace)));
1015+
_PyUOpPrint(next_uop);
1016+
printf(" stack_level=%d\n",
10161017
(int)(stack_pointer - _PyFrame_Stackbase(frame)));
1018+
}
1019+
#endif
10171020
next_uop++;
10181021
OPT_STAT_INC(uops_executed);
10191022
UOP_STAT_INC(uopcode, execution_count);
@@ -1028,9 +1031,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
10281031
default:
10291032
#ifdef Py_DEBUG
10301033
{
1031-
fprintf(stderr, "Unknown uop %d, oparg %d, operand %" PRIu64 " @ %d\n",
1032-
next_uop[-1].opcode, next_uop[-1].oparg, next_uop[-1].operand,
1033-
(int)(next_uop - (current_executor == NULL ? next_uop : current_executor->trace) - 1));
1034+
printf("Unknown uop: ");
1035+
_PyUOpPrint(&next_uop[-1]);
1036+
printf(" @ %d\n", (int)(next_uop - current_executor->trace - 1));
10341037
Py_FatalError("Unknown uop");
10351038
}
10361039
#else
@@ -1058,10 +1061,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
10581061
pop_1_error_tier_two:
10591062
STACK_SHRINK(1);
10601063
error_tier_two:
1061-
DPRINTF(2, "Error: [UOp %d (%s), oparg %d, operand %" PRIu64 ", target %d @ %d -> %s]\n",
1062-
uopcode, _PyUOpName(uopcode), next_uop[-1].oparg, next_uop[-1].operand, next_uop[-1].target,
1063-
(int)(next_uop - current_executor->trace - 1),
1064-
_PyOpcode_OpName[frame->instr_ptr->op.code]);
1064+
#ifdef Py_DEBUG
1065+
if (lltrace >= 2) {
1066+
printf("Error: [UOp ");
1067+
_PyUOpPrint(&next_uop[-1]);
1068+
printf(" @ %d -> %s]\n",
1069+
(int)(next_uop - current_executor->trace - 1),
1070+
_PyOpcode_OpName[frame->instr_ptr->op.code]);
1071+
}
1072+
#endif
10651073
OPT_HIST(trace_uop_execution_counter, trace_run_length_hist);
10661074
frame->return_offset = 0; // Don't leave this random
10671075
_PyFrame_SetStackPointer(frame, stack_pointer);
@@ -1072,9 +1080,14 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
10721080
// Jump here from DEOPT_IF()
10731081
deoptimize:
10741082
next_instr = next_uop[-1].target + _PyCode_CODE(_PyFrame_GetCode(frame));
1075-
DPRINTF(2, "DEOPT: [UOp %d (%s), oparg %d, operand %" PRIu64 ", target %d -> %s]\n",
1076-
uopcode, _PyUOpName(uopcode), next_uop[-1].oparg, next_uop[-1].operand, next_uop[-1].target,
1077-
_PyOpcode_OpName[next_instr->op.code]);
1083+
#ifdef Py_DEBUG
1084+
if (lltrace >= 2) {
1085+
printf("DEOPT: [UOp ");
1086+
_PyUOpPrint(&next_uop[-1]);
1087+
printf(" -> %s]\n",
1088+
_PyOpcode_OpName[frame->instr_ptr->op.code]);
1089+
}
1090+
#endif
10781091
OPT_HIST(trace_uop_execution_counter, trace_run_length_hist);
10791092
UOP_STAT_INC(uopcode, miss);
10801093
Py_DECREF(current_executor);
@@ -1088,9 +1101,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
10881101
uint32_t exit_index = next_uop[-1].exit_index;
10891102
assert(exit_index < current_executor->exit_count);
10901103
_PyExitData *exit = &current_executor->exits[exit_index];
1091-
DPRINTF(2, "SIDE EXIT: [UOp %d (%s), oparg %d, operand %" PRIu64 ", exit %u, temp %d, target %d -> %s]\n",
1092-
uopcode, _PyUOpName(uopcode), next_uop[-1].oparg, next_uop[-1].operand, exit_index, exit->temperature,
1093-
exit->target, _PyOpcode_OpName[_PyCode_CODE(_PyFrame_GetCode(frame))[exit->target].op.code]);
1104+
#ifdef Py_DEBUG
1105+
if (lltrace >= 2) {
1106+
printf("SIDE EXIT: [UO 10000 p ");
1107+
_PyUOpPrint(&next_uop[-1]);
1108+
printf(", exit %u, temp %d, target %d -> %s]\n",
1109+
exit_index, exit->temperature, exit->target,
1110+
_PyOpcode_OpName[frame->instr_ptr->op.code]);
1111+
}
1112+
#endif
10941113
Py_INCREF(exit->executor);
10951114
tstate->previous_executor = (PyObject *)current_executor;
10961115
GOTO_TIER_TWO(exit->executor);

Python/optimizer.c

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,22 @@ is_valid(PyObject *self, PyObject *Py_UNUSED(ignored))
262262
return PyBool_FromLong(((_PyExecutorObject *)self)->vm_data.valid);
263263
}
264264

265+
static PyObject *
266+
get_opcode(PyObject *self, PyObject *Py_UNUSED(ignored))
267+
{
268+
return PyLong_FromUnsignedLong(((_PyExecutorObject *)self)->vm_data.opcode);
269+
}
270+
271+
static PyObject *
272+
get_oparg(PyObject *self, PyObject *Py_UNUSED(ignored))
273+
{
274+
return PyLong_FromUnsignedLong(((_PyExecutorObject *)self)->vm_data.oparg);
275+
}
276+
265277
static PyMethodDef executor_methods[] = {
266278
{ "is_valid", is_valid, METH_NOARGS, NULL },
279+
{ "get_opcode", get_opcode, METH_NOARGS, NULL },
280+
{ "get_oparg", get_oparg, METH_NOARGS, NULL },
267281
{ NULL, NULL },
268282
};
269283

@@ -282,9 +296,30 @@ uop_dealloc(_PyExecutorObject *self) {
282296
const char *
283297
_PyUOpName(int index)
284298
{
299+
if (index < 0 || index > MAX_UOP_ID) {
300+
return NULL;
301+
}
285302
return _PyOpcode_uop_name[index];
286303
}
287304

305+
#ifdef Py_DEBUG
306+
void
307+
_PyUOpPrint(const _PyUOpInstruction *uop)
308+
{
309+
const char *name = _PyUOpName(uop->opcode);
310+
if (name == NULL) {
311+
printf("<uop %d>", uop->opcode);
312+
}
313+
else {
314+
printf("%s", name);
315+
}
316+
printf(" (%d, target=%d, operand=%" PRIx64 ")",
317+
uop->oparg,
318+
uop->target,
319+
(uint64_t)uop->operand);
320+
}
321+
#endif
322+
288323
static Py_ssize_t
289324
uop_len(_PyExecutorObject *self)
290325
{
@@ -312,14 +347,21 @@ uop_item(_PyExecutorObject *self, Py_ssize_t index)
312347
Py_DECREF(oname);
313348
return NULL;
314349
}
350+
PyObject *target = PyLong_FromUnsignedLong(self->trace[index].target);
351+
if (oparg == NULL) {
352+
Py_DECREF(oparg);
353+
Py_DECREF(oname);
354+
return NULL;
355+
}
315356
PyObject *operand = PyLong_FromUnsignedLongLong(self->trace[index].operand);
316357
if (operand == NULL) {
358+
Py_DECREF(target);
317359
Py_DECREF(oparg);
318360
Py_DECREF(oname);
319361
return NULL;
320362
}
321-
PyObject *args[3] = { oname, oparg, operand };
322-
return _PyTuple_FromArraySteal(args, 3);
363+
PyObject *args[4] = { oname, oparg, target, operand };
364+
return _PyTuple_FromArraySteal(args, 4);
323365
}
324366

325367
PySequenceMethods uop_as_sequence = {
@@ -390,19 +432,29 @@ BRANCH_TO_GUARD[4][2] = {
390432
#endif
391433

392434

435+
// Beware: Macro arg order differs from struct member order
436+
#ifdef Py_DEBUG
393437
#define ADD_TO_TRACE(OPCODE, OPARG, OPERAND, TARGET) \
394-
DPRINTF(2, \
395-
" ADD_TO_TRACE(%s, %d, %" PRIu64 ", %d)\n", \
396-
_PyUOpName(OPCODE), \
397-
(OPARG), \
398-
(uint64_t)(OPERAND), \
399-
TARGET); \
400438
assert(trace_length < max_length); \
401439
trace[trace_length].opcode = (OPCODE); \
402440
trace[trace_length].oparg = (OPARG); \
441+
trace[trace_length].target = (TARGET); \
403442
trace[trace_length].operand = (OPERAND); \
443+
if (lltrace >= 2) { \
444+
printf("%4d ADD_TO_TRACE: ", trace_length); \
445+
_PyUOpPrint(&trace[trace_length]); \
446+
printf("\n"); \
447+
} \
448+
trace_length++;
449+
#else
450+
#define ADD_TO_TRACE(OPCODE, OPARG, OPERAND, TARGET) \
451+
assert(trace_length < max_length); \
452+
trace[trace_length].opcode = (OPCODE); \
453+
trace[trace_length].oparg = (OPARG); \
404454
trace[trace_length].target = (TARGET); \
455+
trace[trace_length].operand = (OPERAND); \
405456
trace_length++;
457+
#endif
406458

407459
#define INSTR_IP(INSTR, CODE) \
408460
((uint32_t)((INSTR) - ((_Py_CODEUNIT *)(CODE)->co_code_adaptive)))
@@ -890,12 +942,9 @@ make_executor_from_uops(_PyUOpInstruction *buffer, const _PyBloomFilter *depende
890942
if (lltrace >= 2) {
891943
printf("Optimized executor (length %d):\n", length);
892944
for (int i = 0; i < length; i++) {
893-
printf("%4d %s(%d, %d, %" PRIu64 ")\n",
894-
i,
895-
_PyUOpName(executor->trace[i].opcode),
896-
executor->trace[i].oparg,
897-
executor->trace[i].target,
898-
executor->trace[i].operand);
945+
printf("%4d OPTIMIZED: ", i);
946+
_PyUOpPrint(&executor->trace[i]);
947+
printf("\n");
899948
}
900949
}
901950
#endif

Python/optimizer_analysis.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#define MAX_ABSTRACT_FRAME_DEPTH (TRACE_STACK_SIZE + 2)
4545

4646
#ifdef Py_DEBUG
47+
extern const char *_PyUOpName(int index);
4748
static const char *const DEBUG_ENV = "PYTHON_OPT_DEBUG";
4849
static inline int get_lltrace(void) {
4950
char *uop_debug = Py_GETENV(DEBUG_ENV);
@@ -632,7 +633,7 @@ uop_redundancy_eliminator(
632633
_Py_UOpsSymType **stack_pointer = ctx->frame->stack_pointer;
633634

634635
DPRINTF(3, "Abstract interpreting %s:%d ",
635-
_PyOpcode_uop_name[opcode],
636+
_PyUOpName(opcode),
636637
oparg);
637638
switch (opcode) {
638639
#include "tier2_redundancy_eliminator_cases.c.h"

Python/specialize.c

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include <stdlib.h> // rand()
1919

20+
extern const char *_PyUOpName(int index);
2021

2122
/* For guidance on adding or extending families of instructions see
2223
* ./adaptive.md
@@ -246,17 +247,12 @@ print_optimization_stats(FILE *out, OptimizationStats *stats)
246247
stats->optimizer_failure_reason_no_memory);
247248

248249
const char* const* names;
249-
for (int i = 0; i < 512; i++) {
250-
if (i < 256) {
251-
names = _PyOpcode_OpName;
252-
} else {
253-
names = _PyOpcode_uop_name;
254-
}
250+
for (int i = 0; i <= MAX_UOP_ID; i++) {
255251
if (stats->opcode[i].execution_count) {
256-
fprintf(out, "uops[%s].execution_count : %" PRIu64 "\n", names[i], stats->opcode[i].execution_count);
252+
fprintf(out, "uops[%s].execution_count : %" PRIu64 "\n", _PyUOpName(i), stats->opcode[i].execution_count);
257253
}
258254
if (stats->opcode[i].miss) {
259-
fprintf(out, "uops[%s].specialization.miss : %" PRIu64 "\n", names[i], stats->opcode[i].miss);
255+
fprintf(out, "uops[%s].specialization.miss : %" PRIu64 "\n", _PyUOpName(i), stats->opcode[i].miss);
260256
}
261257
}
262258

Tools/scripts/summarize_stats.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ def load_raw_data(input: Path) -> RawData:
102102
file=sys.stderr,
103103
)
104104
continue
105+
# Hack to handle older data files where some uops
106+
# are missing an underscore prefix in their name
107+
if key.startswith("uops[") and key[5:6] != "_":
108+
key = "uops[_" + key[5:]
105109
stats[key.strip()] += int(value)
106110
stats["__nfiles__"] += 1
107111

0 commit comments

Comments
 (0)
0