Skip to content

Commit

Permalink
Jump directly to side-exit executors
Browse files Browse the repository at this point in the history
  • Loading branch information
gvanrossum committed Dec 12, 2023
1 parent 65c41cb commit 864d68d
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 22 deletions.
2 changes: 1 addition & 1 deletion Include/cpython/optimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ PyAPI_FUNC(_PyOptimizerObject *) PyUnstable_GetOptimizer(void);
PyAPI_FUNC(_PyExecutorObject *) PyUnstable_GetExecutor(PyCodeObject *code, int offset);

int _PyOptimizer_BackEdge(struct _PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest, PyObject **stack_pointer);
int _PyOptimizer_Anywhere(struct _PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest, PyObject **stack_pointer);
int _PyOptimizer_Unanchored(struct _PyInterpreterFrame *frame, _Py_CODEUNIT *instr, _PyExecutorObject **pexecutor, PyObject **stack_pointer);

extern _PyOptimizerObject _PyOptimizer_Default;

Expand Down
35 changes: 33 additions & 2 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -1108,9 +1108,22 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
_PyFrame_SetStackPointer(frame, stack_pointer);
// Increment side exit counter for this uop
int pc = next_uop - 1 - current_executor->trace;

Check warning on line 1110 in Python/ceval.c

View workflow job for this annotation

GitHub Actions / Windows / build and test (x64)

'initializing': conversion from '__int64' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 1110 in Python/ceval.c

View workflow job for this annotation

GitHub Actions / Windows / build and test (x64)

'initializing': conversion from '__int64' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 1110 in Python/ceval.c

View workflow job for this annotation

GitHub Actions / Windows / build (arm64)

'initializing': conversion from '__int64' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 1110 in Python/ceval.c

View workflow job for this annotation

GitHub Actions / Windows / build (arm64)

'initializing': conversion from '__int64' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 1110 in Python/ceval.c

View workflow job for this annotation

GitHub Actions / Windows (free-threaded) / build and test (x64)

'initializing': conversion from '__int64' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 1110 in Python/ceval.c

View workflow job for this annotation

GitHub Actions / Windows (free-threaded) / build and test (x64)

'initializing': conversion from '__int64' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 1110 in Python/ceval.c

View workflow job for this annotation

GitHub Actions / Windows (free-threaded) / build (arm64)

'initializing': conversion from '__int64' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]

Check warning on line 1110 in Python/ceval.c

View workflow job for this annotation

GitHub Actions / Windows (free-threaded) / build (arm64)

'initializing': conversion from '__int64' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\pythoncore.vcxproj]
_PyExecutorObject **pexecutor = current_executor->executors + pc;
if (*pexecutor != NULL) {
PyCodeObject *code = _PyFrame_GetCode(frame);

Check warning on line 1113 in Python/ceval.c

View workflow job for this annotation

GitHub Actions / Address sanitizer

unused variable ‘code’ [-Wunused-variable]
DPRINTF(2, "Jumping to new executor for %s (%s:%d) at byte offset %d\n",
PyUnicode_AsUTF8(code->co_qualname),
PyUnicode_AsUTF8(code->co_filename),
code->co_firstlineno,
2 * (int)(frame->instr_ptr - _PyCode_CODE(_PyFrame_GetCode(frame))));
Py_DECREF(current_executor);
current_executor = (_PyUOpExecutorObject *)*pexecutor;
Py_INCREF(current_executor);
goto enter_tier_two;
}
uint16_t *pcounter = current_executor->counters + pc;
*pcounter += 1;
if (*pcounter == 16 && // TODO: use resume_threshold
if (*pcounter == 32 && // TODO: use resume_threshold
tstate->interp->optimizer != &_PyOptimizer_Default &&
(opcode == POP_JUMP_IF_FALSE ||
opcode == POP_JUMP_IF_TRUE ||
Expand All @@ -1121,14 +1134,32 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
_PyUOpName(uopcode), pc, current_executor, (int)(*pcounter));
DPRINTF(2, " T1: %s\n", _PyOpcode_OpName[opcode]);
// The counter will cycle around once the 16 bits overflow
int optimized = _PyOptimizer_Anywhere(frame, src, dest, stack_pointer);
int optimized = _PyOptimizer_Unanchored(frame, dest, pexecutor, stack_pointer);
if (optimized < 0) {
goto error_tier_two;
}
if (optimized) {
DPRINTF(1, "--> Optimized %s @ %d in %p\n",
_PyUOpName(uopcode), pc, current_executor);
DPRINTF(1, " T1: %s\n", _PyOpcode_OpName[src->op.code]);
PyCodeObject *code = _PyFrame_GetCode(frame);

Check warning on line 1145 in Python/ceval.c

View workflow job for this annotation

GitHub Actions / Address sanitizer

unused variable ‘code’ [-Wunused-variable]
DPRINTF(2, "Jumping to fresh executor for %s (%s:%d) at byte offset %d\n",
PyUnicode_AsUTF8(code->co_qualname),
PyUnicode_AsUTF8(code->co_filename),
code->co_firstlineno,
2 * (int)(frame->instr_ptr - _PyCode_CODE(_PyFrame_GetCode(frame))));
Py_DECREF(current_executor);
current_executor = (_PyUOpExecutorObject *)*pexecutor;
if (current_executor->trace[0].opcode != uopcode) {
Py_INCREF(current_executor);
goto enter_tier_two;
}
// This is guaranteed to deopt again; forget about it
DPRINTF(2, "It's not an improvement -- discarding trace\n");
*pexecutor = NULL;
Py_DECREF(current_executor);
next_instr = frame->instr_ptr;
goto resume_frame;
}
else {
DPRINTF(2, "--> Failed to optimize %s @ %d in %p\n",
Expand Down
37 changes: 18 additions & 19 deletions Python/optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,10 @@ PyUnstable_SetOptimizer(_PyOptimizerObject *optimizer)
Py_DECREF(old);
}

// src is where to insert ENTER_EXECUTOR
// dest is where to start tracing
static int
optimizer_wherever(_PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest, PyObject **stack_pointer)
int
_PyOptimizer_BackEdge(_PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest, PyObject **stack_pointer)
{
assert(src->op.code == JUMP_BACKWARD);
PyCodeObject *code = _PyFrame_GetCode(frame);
assert(PyCode_Check(code));
PyInterpreterState *interp = _PyInterpreterState_GET();
Expand Down Expand Up @@ -189,27 +188,27 @@ optimizer_wherever(_PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *
return 1;
}

// Return an unanchored executor. The caller owns the executor when returning 1.
// No ENTER_EXECUTOR is inserted, nor is the executor added to the code object.
int
_PyOptimizer_BackEdge(_PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest, PyObject **stack_pointer)
{
assert(src->op.code == JUMP_BACKWARD);
return optimizer_wherever(frame, src, dest, stack_pointer);
}

// Start tracing and insert ENTER_EXECUTOR at the same place.
// Normally src == dest, but when there's an EXTENDED_ARG involved,
// dest points at the preceding EXTENDED_ARG.
// Do not use at JUMP_BACKWARD. Won't replace ENTER_EXECUTOR.
int
_PyOptimizer_Anywhere(_PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest, PyObject **stack_pointer)
_PyOptimizer_Unanchored(
_PyInterpreterFrame *frame,
_Py_CODEUNIT *instr,
_PyExecutorObject **pexecutor,
PyObject **stack_pointer)
{
if (src->op.code == JUMP_BACKWARD) {
if (instr->op.code == JUMP_BACKWARD || instr->op.code == ENTER_EXECUTOR) {
return 0;
}
if (src->op.code == ENTER_EXECUTOR) {
PyCodeObject *code = _PyFrame_GetCode(frame);
assert(PyCode_Check(code));
PyInterpreterState *interp = _PyInterpreterState_GET();
_PyOptimizerObject *opt = interp->optimizer;
if (strcmp(opt->ob_base.ob_type->tp_name, "uop_optimizer") != 0) {
return 0;
}
return optimizer_wherever(frame, src, dest, stack_pointer);
*pexecutor = NULL;
return opt->optimize(opt, code, instr, pexecutor, (int)(stack_pointer - _PyFrame_Stackbase(frame)));
}

_PyExecutorObject *
Expand Down

0 comments on commit 864d68d

Please sign in to comment.