mirror of
https://github.com/python/cpython.git
synced 2025-07-23 11:15:24 +00:00
GH-123516: Improve JIT memory consumption by invalidating cold executors (GH-124443)
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
This commit is contained in:
parent
23e812b84a
commit
65f1237098
14 changed files with 129 additions and 39 deletions
|
@ -4836,6 +4836,14 @@ dummy_func(
|
|||
assert(((_PyExecutorObject *)executor)->vm_data.valid);
|
||||
}
|
||||
|
||||
tier2 op(_MAKE_WARM, (--)) {
|
||||
current_executor->vm_data.warm = true;
|
||||
// It's okay if this ends up going negative.
|
||||
if (--tstate->interp->trace_run_counter == 0) {
|
||||
_Py_set_eval_breaker_bit(tstate, _PY_EVAL_JIT_INVALIDATE_COLD_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
tier2 op(_FATAL_ERROR, (--)) {
|
||||
assert(0);
|
||||
Py_FatalError("Fatal error uop executed.");
|
||||
|
|
|
@ -1289,6 +1289,12 @@ _Py_HandlePending(PyThreadState *tstate)
|
|||
_Py_RunGC(tstate);
|
||||
}
|
||||
|
||||
if ((breaker & _PY_EVAL_JIT_INVALIDATE_COLD_BIT) != 0) {
|
||||
_Py_unset_eval_breaker_bit(tstate, _PY_EVAL_JIT_INVALIDATE_COLD_BIT);
|
||||
_Py_Executors_InvalidateCold(tstate->interp);
|
||||
tstate->interp->trace_run_counter = JIT_CLEANUP_THRESHOLD;
|
||||
}
|
||||
|
||||
/* GIL drop request */
|
||||
if ((breaker & _PY_GIL_DROP_REQUEST_BIT) != 0) {
|
||||
/* Give another thread a chance */
|
||||
|
|
9
Python/executor_cases.c.h
generated
9
Python/executor_cases.c.h
generated
|
@ -5435,6 +5435,15 @@
|
|||
break;
|
||||
}
|
||||
|
||||
case _MAKE_WARM: {
|
||||
current_executor->vm_data.warm = true;
|
||||
// It's okay if this ends up going negative.
|
||||
if (--tstate->interp->trace_run_counter == 0) {
|
||||
_Py_set_eval_breaker_bit(tstate, _PY_EVAL_JIT_INVALIDATE_COLD_BIT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case _FATAL_ERROR: {
|
||||
assert(0);
|
||||
Py_FatalError("Fatal error uop executed.");
|
||||
|
|
|
@ -565,6 +565,7 @@ translate_bytecode_to_trace(
|
|||
code->co_firstlineno,
|
||||
2 * INSTR_IP(initial_instr, code));
|
||||
ADD_TO_TRACE(_START_EXECUTOR, 0, (uintptr_t)instr, INSTR_IP(instr, code));
|
||||
ADD_TO_TRACE(_MAKE_WARM, 0, 0, 0);
|
||||
uint32_t target = 0;
|
||||
|
||||
for (;;) {
|
||||
|
@ -1194,6 +1195,9 @@ make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFil
|
|||
executor->jit_code = NULL;
|
||||
executor->jit_side_entry = NULL;
|
||||
executor->jit_size = 0;
|
||||
// This is initialized to true so we can prevent the executor
|
||||
// from being immediately detected as cold and invalidated.
|
||||
executor->vm_data.warm = true;
|
||||
if (_PyJIT_Compile(executor, executor->trace, length)) {
|
||||
Py_DECREF(executor);
|
||||
return NULL;
|
||||
|
@ -1659,4 +1663,42 @@ _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
_Py_Executors_InvalidateCold(PyInterpreterState *interp)
|
||||
{
|
||||
/* Walk the list of executors */
|
||||
/* TO DO -- Use a tree to avoid traversing as many objects */
|
||||
PyObject *invalidate = PyList_New(0);
|
||||
if (invalidate == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Clearing an executor can deallocate others, so we need to make a list of
|
||||
* executors to invalidate first */
|
||||
for (_PyExecutorObject *exec = interp->executor_list_head; exec != NULL;) {
|
||||
assert(exec->vm_data.valid);
|
||||
_PyExecutorObject *next = exec->vm_data.links.next;
|
||||
|
||||
if (!exec->vm_data.warm && PyList_Append(invalidate, (PyObject *)exec) < 0) {
|
||||
goto error;
|
||||
}
|
||||
else {
|
||||
exec->vm_data.warm = false;
|
||||
}
|
||||
|
||||
exec = next;
|
||||
}
|
||||
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(invalidate); i++) {
|
||||
_PyExecutorObject *exec = (_PyExecutorObject *)PyList_GET_ITEM(invalidate, i);
|
||||
executor_clear(exec);
|
||||
}
|
||||
Py_DECREF(invalidate);
|
||||
return;
|
||||
error:
|
||||
PyErr_Clear();
|
||||
Py_XDECREF(invalidate);
|
||||
// If we're truly out of memory, wiping out everything is a fine fallback
|
||||
_Py_Executors_InvalidateAll(interp, 0);
|
||||
}
|
||||
|
||||
#endif /* _Py_TIER2 */
|
||||
|
|
4
Python/optimizer_cases.c.h
generated
4
Python/optimizer_cases.c.h
generated
|
@ -2381,6 +2381,10 @@
|
|||
break;
|
||||
}
|
||||
|
||||
case _MAKE_WARM: {
|
||||
break;
|
||||
}
|
||||
|
||||
case _FATAL_ERROR: {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -660,6 +660,7 @@ init_interpreter(PyInterpreterState *interp,
|
|||
#ifdef _Py_TIER2
|
||||
(void)_Py_SetOptimizer(interp, NULL);
|
||||
interp->executor_list_head = NULL;
|
||||
interp->trace_run_counter = JIT_CLEANUP_THRESHOLD;
|
||||
#endif
|
||||
if (interp != &runtime->_main_interpreter) {
|
||||
/* Fix the self-referential, statically initialized fields. */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue