Skip to content
6 changes: 4 additions & 2 deletions Include/cpython/optimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ typedef int (*optimize_func)(_PyOptimizerObject* self, PyCodeObject *code, _Py_C
typedef struct _PyOptimizerObject {
PyObject_HEAD
optimize_func optimize;
uint16_t resume_threshold;
uint16_t backedge_threshold;
uint32_t resume_threshold;
uint32_t backedge_threshold;
/* Data needed by the optimizer goes here, but is opaque to the VM */
} _PyOptimizerObject;

Expand Down Expand Up @@ -76,6 +76,8 @@ PyAPI_FUNC(PyObject *)PyUnstable_Optimizer_NewCounter(void);
PyAPI_FUNC(PyObject *)PyUnstable_Optimizer_NewUOpOptimizer(void);

#define OPTIMIZER_BITS_IN_COUNTER 4
/* Minimum of 16 additional executions before retry */
#define MINIMUM_TIER2_BACKOFF 4

#ifdef __cplusplus
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Use exponential backoff to reduce the number of failed tier 2 optimization
attempts by over 99%.
18 changes: 14 additions & 4 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2335,19 +2335,29 @@ dummy_func(
JUMPBY(-oparg);
#if ENABLE_SPECIALIZATION
this_instr[1].cache += (1 << OPTIMIZER_BITS_IN_COUNTER);
if (this_instr[1].cache > tstate->interp->optimizer_backedge_threshold &&
uint16_t counter = this_instr[1].cache;
if (counter == tstate->interp->optimizer_backedge_threshold) {
// Double-check that the opcode isn't instrumented or something:
this_instr->op.code == JUMP_BACKWARD)
{
assert(this_instr->op.code == JUMP_BACKWARD);
OPT_STAT_INC(attempts);
int optimized = _PyOptimizer_BackEdge(frame, this_instr, next_instr, stack_pointer);
ERROR_IF(optimized < 0, error);
if (optimized) {
// Rewind and enter the executor:
assert(this_instr->op.code == ENTER_EXECUTOR);
next_instr = this_instr;
this_instr[1].cache &= ((1 << OPTIMIZER_BITS_IN_COUNTER) - 1);
}
else {
int backoff = counter & ((1 << OPTIMIZER_BITS_IN_COUNTER) - 1);
if (backoff < OPTIMIZER_BITS_IN_COUNTER + MINIMUM_TIER2_BACKOFF) {
backoff = OPTIMIZER_BITS_IN_COUNTER + MINIMUM_TIER2_BACKOFF;
}
else if (backoff < 15) {
backoff++;
}
this_instr[1].cache = (0U - (1 << backoff)) | backoff;
}
this_instr[1].cache &= ((1 << OPTIMIZER_BITS_IN_COUNTER) - 1);
}
#endif /* ENABLE_SPECIALIZATION */
}
Expand Down
18 changes: 14 additions & 4 deletions Python/generated_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Python/optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ PyTypeObject _PyDefaultOptimizer_Type = {
_PyOptimizerObject _PyOptimizer_Default = {
PyObject_HEAD_INIT(&_PyDefaultOptimizer_Type)
.optimize = error_optimize,
.resume_threshold = UINT16_MAX,
.backedge_threshold = UINT16_MAX,
.resume_threshold = UINT16_MAX+1,
.backedge_threshold = UINT16_MAX+1,
};

_PyOptimizerObject *
Expand Down
1 change: 1 addition & 0 deletions Python/pylifecycle.c
Original file line number Diff line number Diff line change
Expand Up @@ -1240,6 +1240,7 @@ init_interp_main(PyThreadState *tstate)
if (_Py_get_xoption(&config->xoptions, L"uops") != NULL) {
enabled = 1;
}
enabled = 1;
if (enabled) {
PyObject *opt = PyUnstable_Optimizer_NewUOpOptimizer();
if (opt == NULL) {
Expand Down