mirror of
https://github.com/python/cpython.git
synced 2026-04-14 07:41:00 +00:00
gh-141504: Refactor policy object into a single opt_config (gh-143644)
This commit is contained in:
parent
a73ba4d46e
commit
794f758cd8
8 changed files with 89 additions and 64 deletions
|
|
@ -12,7 +12,7 @@ extern "C" {
|
|||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include "pycore_structs.h" // _Py_BackoffCounter
|
||||
#include "pycore_tstate.h" // _PyPolicy
|
||||
#include "pycore_interp_structs.h" // _PyOptimizationConfig
|
||||
|
||||
/* 16-bit countdown counters using exponential backoff.
|
||||
|
||||
|
|
@ -128,11 +128,11 @@ trigger_backoff_counter(void)
|
|||
#define JUMP_BACKWARD_INITIAL_VALUE 4000
|
||||
#define JUMP_BACKWARD_INITIAL_BACKOFF 6
|
||||
static inline _Py_BackoffCounter
|
||||
initial_jump_backoff_counter(_PyPolicy *policy)
|
||||
initial_jump_backoff_counter(_PyOptimizationConfig *opt_config)
|
||||
{
|
||||
return make_backoff_counter(
|
||||
policy->interp.jump_backward_initial_value,
|
||||
policy->interp.jump_backward_initial_backoff);
|
||||
opt_config->jump_backward_initial_value,
|
||||
opt_config->jump_backward_initial_backoff);
|
||||
}
|
||||
|
||||
/* Initial exit temperature.
|
||||
|
|
@ -143,11 +143,11 @@ initial_jump_backoff_counter(_PyPolicy *policy)
|
|||
#define SIDE_EXIT_INITIAL_BACKOFF 6
|
||||
|
||||
static inline _Py_BackoffCounter
|
||||
initial_temperature_backoff_counter(_PyPolicy *policy)
|
||||
initial_temperature_backoff_counter(_PyOptimizationConfig *opt_config)
|
||||
{
|
||||
return make_backoff_counter(
|
||||
policy->jit.side_exit_initial_value,
|
||||
policy->jit.side_exit_initial_backoff);
|
||||
opt_config->side_exit_initial_value,
|
||||
opt_config->side_exit_initial_backoff);
|
||||
}
|
||||
|
||||
/* Unreachable backoff counter. */
|
||||
|
|
|
|||
|
|
@ -398,6 +398,21 @@ typedef struct _rare_events {
|
|||
uint8_t func_modification;
|
||||
} _rare_events;
|
||||
|
||||
// Optimization configuration for the interpreter.
|
||||
// This groups all thresholds and optimization flags for both JIT and interpreter.
|
||||
typedef struct _PyOptimizationConfig {
|
||||
// Interpreter optimization thresholds
|
||||
uint16_t jump_backward_initial_value;
|
||||
uint16_t jump_backward_initial_backoff;
|
||||
|
||||
// JIT optimization thresholds
|
||||
uint16_t side_exit_initial_value;
|
||||
uint16_t side_exit_initial_backoff;
|
||||
|
||||
// Optimization flags
|
||||
bool specialization_enabled;
|
||||
} _PyOptimizationConfig;
|
||||
|
||||
struct
|
||||
Bigint {
|
||||
struct Bigint *next;
|
||||
|
|
@ -945,6 +960,9 @@ struct _is {
|
|||
PyObject *common_consts[NUM_COMMON_CONSTANTS];
|
||||
bool jit;
|
||||
bool compiling;
|
||||
|
||||
// Optimization configuration (thresholds and flags for JIT and interpreter)
|
||||
_PyOptimizationConfig opt_config;
|
||||
struct _PyExecutorObject *executor_list_head;
|
||||
struct _PyExecutorObject *executor_deletion_list_head;
|
||||
struct _PyExecutorObject *cold_executor;
|
||||
|
|
|
|||
|
|
@ -64,21 +64,6 @@ typedef struct _PyJitTracerState {
|
|||
|
||||
#endif
|
||||
|
||||
typedef struct _PyJitPolicy {
|
||||
uint16_t side_exit_initial_value;
|
||||
uint16_t side_exit_initial_backoff;
|
||||
} _PyJitPolicy;
|
||||
|
||||
typedef struct _PyInterpreterPolicy {
|
||||
uint16_t jump_backward_initial_value;
|
||||
uint16_t jump_backward_initial_backoff;
|
||||
} _PyInterpreterPolicy;
|
||||
|
||||
typedef struct _PyPolicy {
|
||||
_PyJitPolicy jit;
|
||||
_PyInterpreterPolicy interp;
|
||||
} _PyPolicy;
|
||||
|
||||
// Every PyThreadState is actually allocated as a _PyThreadStateImpl. The
|
||||
// PyThreadState fields are exposed as part of the C API, although most fields
|
||||
// are intended to be private. The _PyThreadStateImpl fields not exposed.
|
||||
|
|
@ -157,7 +142,6 @@ typedef struct _PyThreadStateImpl {
|
|||
#if _Py_TIER2
|
||||
_PyJitTracerState *jit_tracer_state;
|
||||
#endif
|
||||
_PyPolicy policy;
|
||||
} _PyThreadStateImpl;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
|
|
@ -3789,7 +3789,7 @@ def __next__(self):
|
|||
pass
|
||||
|
||||
f1()
|
||||
"""), PYTHON_JIT="1", PYTHON_JIT_JUMP_BACKWARD_INITIAL_VALUE="64")
|
||||
"""), PYTHON_JIT="1", PYTHON_JIT_STRESS="1")
|
||||
self.assertEqual(result[0].rc, 0, result)
|
||||
|
||||
def global_identity(x):
|
||||
|
|
|
|||
|
|
@ -580,9 +580,10 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
|
|||
}
|
||||
co->_co_firsttraceable = entry_point;
|
||||
#ifdef Py_GIL_DISABLED
|
||||
_PyCode_Quicken(_PyCode_CODE(co), Py_SIZE(co), interp->config.tlbc_enabled);
|
||||
int enable_counters = interp->config.tlbc_enabled && interp->opt_config.specialization_enabled;
|
||||
_PyCode_Quicken(_PyCode_CODE(co), Py_SIZE(co), enable_counters);
|
||||
#else
|
||||
_PyCode_Quicken(_PyCode_CODE(co), Py_SIZE(co), 1);
|
||||
_PyCode_Quicken(_PyCode_CODE(co), Py_SIZE(co), interp->opt_config.specialization_enabled);
|
||||
#endif
|
||||
notify_code_watchers(PY_CODE_EVENT_CREATE, co);
|
||||
return 0;
|
||||
|
|
@ -3369,13 +3370,13 @@ deopt_code_unit(PyCodeObject *code, int i)
|
|||
}
|
||||
|
||||
static void
|
||||
copy_code(_Py_CODEUNIT *dst, PyCodeObject *co)
|
||||
copy_code(PyInterpreterState *interp, _Py_CODEUNIT *dst, PyCodeObject *co)
|
||||
{
|
||||
int code_len = (int) Py_SIZE(co);
|
||||
for (int i = 0; i < code_len; i += _PyInstruction_GetLength(co, i)) {
|
||||
dst[i] = deopt_code_unit(co, i);
|
||||
}
|
||||
_PyCode_Quicken(dst, code_len, 1);
|
||||
_PyCode_Quicken(dst, code_len, interp->opt_config.specialization_enabled);
|
||||
}
|
||||
|
||||
static Py_ssize_t
|
||||
|
|
@ -3391,7 +3392,7 @@ get_pow2_greater(Py_ssize_t initial, Py_ssize_t limit)
|
|||
}
|
||||
|
||||
static _Py_CODEUNIT *
|
||||
create_tlbc_lock_held(PyCodeObject *co, Py_ssize_t idx)
|
||||
create_tlbc_lock_held(PyInterpreterState *interp, PyCodeObject *co, Py_ssize_t idx)
|
||||
{
|
||||
_PyCodeArray *tlbc = co->co_tlbc;
|
||||
if (idx >= tlbc->size) {
|
||||
|
|
@ -3414,7 +3415,7 @@ create_tlbc_lock_held(PyCodeObject *co, Py_ssize_t idx)
|
|||
PyErr_NoMemory();
|
||||
return NULL;
|
||||
}
|
||||
copy_code((_Py_CODEUNIT *) bc, co);
|
||||
copy_code(interp, (_Py_CODEUNIT *) bc, co);
|
||||
assert(tlbc->entries[idx] == NULL);
|
||||
tlbc->entries[idx] = bc;
|
||||
return (_Py_CODEUNIT *) bc;
|
||||
|
|
@ -3429,7 +3430,8 @@ get_tlbc_lock_held(PyCodeObject *co)
|
|||
if (idx < tlbc->size && tlbc->entries[idx] != NULL) {
|
||||
return (_Py_CODEUNIT *)tlbc->entries[idx];
|
||||
}
|
||||
return create_tlbc_lock_held(co, idx);
|
||||
PyInterpreterState *interp = tstate->base.interp;
|
||||
return create_tlbc_lock_held(interp, co, idx);
|
||||
}
|
||||
|
||||
_Py_CODEUNIT *
|
||||
|
|
|
|||
|
|
@ -1105,7 +1105,7 @@ _PyJit_FinalizeTracing(PyThreadState *tstate, int err)
|
|||
tracer->initial_state.jump_backward_instr[1].counter = restart_backoff_counter(counter);
|
||||
}
|
||||
else {
|
||||
tracer->initial_state.jump_backward_instr[1].counter = initial_jump_backoff_counter(&_tstate->policy);
|
||||
tracer->initial_state.jump_backward_instr[1].counter = initial_jump_backoff_counter(&tstate->interp->opt_config);
|
||||
}
|
||||
}
|
||||
else if (tracer->initial_state.executor->vm_data.valid) {
|
||||
|
|
@ -1115,7 +1115,7 @@ _PyJit_FinalizeTracing(PyThreadState *tstate, int err)
|
|||
exit->temperature = restart_backoff_counter(exit->temperature);
|
||||
}
|
||||
else {
|
||||
exit->temperature = initial_temperature_backoff_counter(&_tstate->policy);
|
||||
exit->temperature = initial_temperature_backoff_counter(&tstate->interp->opt_config);
|
||||
}
|
||||
}
|
||||
Py_CLEAR(tracer->initial_state.code);
|
||||
|
|
@ -1384,9 +1384,10 @@ make_executor_from_uops(_PyThreadStateImpl *tstate, _PyUOpInstruction *buffer, i
|
|||
_PyExecutorObject *cold = _PyExecutor_GetColdExecutor();
|
||||
_PyExecutorObject *cold_dynamic = _PyExecutor_GetColdDynamicExecutor();
|
||||
cold->vm_data.chain_depth = chain_depth;
|
||||
PyInterpreterState *interp = tstate->base.interp;
|
||||
for (int i = 0; i < exit_count; i++) {
|
||||
executor->exits[i].index = i;
|
||||
executor->exits[i].temperature = initial_temperature_backoff_counter(&tstate->policy);
|
||||
executor->exits[i].temperature = initial_temperature_backoff_counter(&interp->opt_config);
|
||||
}
|
||||
int next_exit = exit_count-1;
|
||||
_PyUOpInstruction *dest = (_PyUOpInstruction *)&executor->trace[length];
|
||||
|
|
|
|||
|
|
@ -514,6 +514,28 @@ _Py_LazyJitShim(
|
|||
main interpreter. We fix those fields here, in addition
|
||||
to the other dynamically initialized fields.
|
||||
*/
|
||||
|
||||
static inline bool
|
||||
is_env_enabled(const char *env_name)
|
||||
{
|
||||
char *env = Py_GETENV(env_name);
|
||||
return env && *env != '\0' && *env != '0';
|
||||
}
|
||||
|
||||
static inline void
|
||||
init_policy(uint16_t *target, const char *env_name, uint16_t default_value,
|
||||
long min_value, long max_value)
|
||||
{
|
||||
*target = default_value;
|
||||
char *env = Py_GETENV(env_name);
|
||||
if (env && *env != '\0') {
|
||||
long value = atol(env);
|
||||
if (value >= min_value && value <= max_value) {
|
||||
*target = (uint16_t)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static PyStatus
|
||||
init_interpreter(PyInterpreterState *interp,
|
||||
_PyRuntimeState *runtime, int64_t id,
|
||||
|
|
@ -572,6 +594,31 @@ init_interpreter(PyInterpreterState *interp,
|
|||
interp->executor_list_head = NULL;
|
||||
interp->executor_deletion_list_head = NULL;
|
||||
interp->executor_creation_counter = JIT_CLEANUP_THRESHOLD;
|
||||
|
||||
// Initialize optimization configuration from environment variables
|
||||
// PYTHON_JIT_STRESS sets aggressive defaults for testing, but can be overridden
|
||||
uint16_t jump_default = JUMP_BACKWARD_INITIAL_VALUE;
|
||||
uint16_t side_exit_default = SIDE_EXIT_INITIAL_VALUE;
|
||||
|
||||
if (is_env_enabled("PYTHON_JIT_STRESS")) {
|
||||
jump_default = 63;
|
||||
side_exit_default = 63;
|
||||
}
|
||||
|
||||
init_policy(&interp->opt_config.jump_backward_initial_value,
|
||||
"PYTHON_JIT_JUMP_BACKWARD_INITIAL_VALUE",
|
||||
jump_default, 1, MAX_VALUE);
|
||||
init_policy(&interp->opt_config.jump_backward_initial_backoff,
|
||||
"PYTHON_JIT_JUMP_BACKWARD_INITIAL_BACKOFF",
|
||||
JUMP_BACKWARD_INITIAL_BACKOFF, 0, MAX_BACKOFF);
|
||||
init_policy(&interp->opt_config.side_exit_initial_value,
|
||||
"PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE",
|
||||
side_exit_default, 1, MAX_VALUE);
|
||||
init_policy(&interp->opt_config.side_exit_initial_backoff,
|
||||
"PYTHON_JIT_SIDE_EXIT_INITIAL_BACKOFF",
|
||||
SIDE_EXIT_INITIAL_BACKOFF, 0, MAX_BACKOFF);
|
||||
|
||||
interp->opt_config.specialization_enabled = !is_env_enabled("PYTHON_SPECIALIZATION_OFF");
|
||||
if (interp != &runtime->_main_interpreter) {
|
||||
/* Fix the self-referential, statically initialized fields. */
|
||||
interp->dtoa = (struct _dtoa_state)_dtoa_state_INIT(interp);
|
||||
|
|
@ -1439,20 +1486,6 @@ decref_threadstate(_PyThreadStateImpl *tstate)
|
|||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
init_policy(uint16_t *target, const char *env_name, uint16_t default_value,
|
||||
long min_value, long max_value)
|
||||
{
|
||||
*target = default_value;
|
||||
char *env = Py_GETENV(env_name);
|
||||
if (env && *env != '\0') {
|
||||
long value = atol(env);
|
||||
if (value >= min_value && value <= max_value) {
|
||||
*target = (uint16_t)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the thread state to a minimal consistent state.
|
||||
Further init happens in pylifecycle.c before it can be used.
|
||||
All fields not initialized here are expected to be zeroed out,
|
||||
|
|
@ -1538,21 +1571,8 @@ init_threadstate(_PyThreadStateImpl *_tstate,
|
|||
|
||||
_tstate->asyncio_running_loop = NULL;
|
||||
_tstate->asyncio_running_task = NULL;
|
||||
// Initialize interpreter policy from environment variables
|
||||
init_policy(&_tstate->policy.interp.jump_backward_initial_value,
|
||||
"PYTHON_JIT_JUMP_BACKWARD_INITIAL_VALUE",
|
||||
JUMP_BACKWARD_INITIAL_VALUE, 1, MAX_VALUE);
|
||||
init_policy(&_tstate->policy.interp.jump_backward_initial_backoff,
|
||||
"PYTHON_JIT_JUMP_BACKWARD_INITIAL_BACKOFF",
|
||||
JUMP_BACKWARD_INITIAL_BACKOFF, 0, MAX_BACKOFF);
|
||||
|
||||
#ifdef _Py_TIER2
|
||||
// Initialize JIT policy from environment variables
|
||||
init_policy(&_tstate->policy.jit.side_exit_initial_value,
|
||||
"PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE",
|
||||
SIDE_EXIT_INITIAL_VALUE, 1, MAX_VALUE);
|
||||
init_policy(&_tstate->policy.jit.side_exit_initial_backoff,
|
||||
"PYTHON_JIT_SIDE_EXIT_INITIAL_BACKOFF",
|
||||
SIDE_EXIT_INITIAL_BACKOFF, 0, MAX_BACKOFF);
|
||||
_tstate->jit_tracer_state = NULL;
|
||||
#endif
|
||||
tstate->delete_later = NULL;
|
||||
|
|
|
|||
|
|
@ -48,8 +48,8 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters
|
|||
_Py_BackoffCounter jump_counter, adaptive_counter;
|
||||
if (enable_counters) {
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
_PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate;
|
||||
jump_counter = initial_jump_backoff_counter(&tstate_impl->policy);
|
||||
PyInterpreterState *interp = tstate->interp;
|
||||
jump_counter = initial_jump_backoff_counter(&interp->opt_config);
|
||||
adaptive_counter = adaptive_counter_warmup();
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue