Skip to content

Commit 34a03e9

Browse files
authored
pythonGH-111843: Tier 2 exponential backoff (pythonGH-111850)
1 parent 25c4956 commit 34a03e9

File tree

5 files changed

+47
-14
lines changed

5 files changed

+47
-14
lines changed

Include/cpython/optimizer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ typedef int (*optimize_func)(_PyOptimizerObject* self, PyCodeObject *code, _Py_C
4545
typedef struct _PyOptimizerObject {
4646
PyObject_HEAD
4747
optimize_func optimize;
48+
/* These thresholds are treated as signed so do not exceed INT16_MAX
49+
* Use INT16_MAX to indicate that the optimizer should never be called */
4850
uint16_t resume_threshold;
4951
uint16_t backedge_threshold;
5052
/* Data needed by the optimizer goes here, but is opaque to the VM */
@@ -76,6 +78,8 @@ PyAPI_FUNC(PyObject *)PyUnstable_Optimizer_NewCounter(void);
7678
PyAPI_FUNC(PyObject *)PyUnstable_Optimizer_NewUOpOptimizer(void);
7779

7880
#define OPTIMIZER_BITS_IN_COUNTER 4
81+
/* Minimum of 16 additional executions before retry */
82+
#define MINIMUM_TIER2_BACKOFF 4
7983

8084
#ifdef __cplusplus
8185
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Use exponential backoff to reduce the number of failed tier 2 optimization
2+
attempts by over 99%.

Python/bytecodes.c

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2335,19 +2335,32 @@ dummy_func(
23352335
JUMPBY(-oparg);
23362336
#if ENABLE_SPECIALIZATION
23372337
this_instr[1].cache += (1 << OPTIMIZER_BITS_IN_COUNTER);
2338-
if (this_instr[1].cache > tstate->interp->optimizer_backedge_threshold &&
2339-
// Double-check that the opcode isn't instrumented or something:
2340-
this_instr->op.code == JUMP_BACKWARD)
2341-
{
2338+
/* We are using unsigned values, but we really want signed values, so
2339+
* do the 2s complement comparison manually */
2340+
uint16_t ucounter = this_instr[1].cache + (1 << 15);
2341+
uint16_t threshold = tstate->interp->optimizer_backedge_threshold + (1 << 15);
2342+
// Double-check that the opcode isn't instrumented or something:
2343+
if (ucounter > threshold && this_instr->op.code == JUMP_BACKWARD) {
23422344
OPT_STAT_INC(attempts);
23432345
int optimized = _PyOptimizer_BackEdge(frame, this_instr, next_instr, stack_pointer);
23442346
ERROR_IF(optimized < 0, error);
23452347
if (optimized) {
23462348
// Rewind and enter the executor:
23472349
assert(this_instr->op.code == ENTER_EXECUTOR);
23482350
next_instr = this_instr;
2351+
this_instr[1].cache &= ((1 << OPTIMIZER_BITS_IN_COUNTER) - 1);
2352+
}
2353+
else {
2354+
int backoff = this_instr[1].cache & ((1 << OPTIMIZER_BITS_IN_COUNTER) - 1);
2355+
if (backoff < MINIMUM_TIER2_BACKOFF) {
2356+
backoff = MINIMUM_TIER2_BACKOFF;
2357+
}
2358+
else if (backoff < 15 - OPTIMIZER_BITS_IN_COUNTER) {
2359+
backoff++;
2360+
}
2361+
assert(backoff <= 15 - OPTIMIZER_BITS_IN_COUNTER);
2362+
this_instr[1].cache = ((1 << 16) - ((1 << OPTIMIZER_BITS_IN_COUNTER) << backoff)) | backoff;
23492363
}
2350-
this_instr[1].cache &= ((1 << OPTIMIZER_BITS_IN_COUNTER) - 1);
23512364
}
23522365
#endif /* ENABLE_SPECIALIZATION */
23532366
}

Python/generated_cases.c.h

Lines changed: 18 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/optimizer.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ error_optimize(
107107
_PyExecutorObject **exec,
108108
int Py_UNUSED(stack_entries))
109109
{
110+
assert(0);
110111
PyErr_Format(PyExc_SystemError, "Should never call error_optimize");
111112
return -1;
112113
}
@@ -122,8 +123,8 @@ PyTypeObject _PyDefaultOptimizer_Type = {
122123
_PyOptimizerObject _PyOptimizer_Default = {
123124
PyObject_HEAD_INIT(&_PyDefaultOptimizer_Type)
124125
.optimize = error_optimize,
125-
.resume_threshold = UINT16_MAX,
126-
.backedge_threshold = UINT16_MAX,
126+
.resume_threshold = INT16_MAX,
127+
.backedge_threshold = INT16_MAX,
127128
};
128129

129130
_PyOptimizerObject *
@@ -309,7 +310,7 @@ PyUnstable_Optimizer_NewCounter(void)
309310
return NULL;
310311
}
311312
opt->base.optimize = counter_optimize;
312-
opt->base.resume_threshold = UINT16_MAX;
313+
opt->base.resume_threshold = INT16_MAX;
313314
opt->base.backedge_threshold = 0;
314315
opt->count = 0;
315316
return (PyObject *)opt;
@@ -915,7 +916,7 @@ PyUnstable_Optimizer_NewUOpOptimizer(void)
915916
return NULL;
916917
}
917918
opt->optimize = uop_optimize;
918-
opt->resume_threshold = UINT16_MAX;
919+
opt->resume_threshold = INT16_MAX;
919920
// Need at least 3 iterations to settle specializations.
920921
// A few lower bits of the counter are reserved for other flags.
921922
opt->backedge_threshold = 16 << OPTIMIZER_BITS_IN_COUNTER;

0 commit comments

Comments
 (0)