From fc84a628f0471cd1553d456e7317031873a6ddfe Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 10 Jan 2026 17:06:11 +0900 Subject: [PATCH 1/4] gh-141504: Refactor policy object into a single opt_config --- Include/internal/pycore_backoff.h | 14 ++--- Include/internal/pycore_interp_structs.h | 3 ++ Include/internal/pycore_tstate.h | 23 ++++---- Objects/codeobject.c | 16 +++--- Python/ceval.c | 4 +- Python/optimizer.c | 3 +- Python/pystate.c | 69 ++++++++++++++---------- Python/specialize.c | 4 +- 8 files changed, 77 insertions(+), 59 deletions(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index fadd11f04ec5ca..1707a00d97754f 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -12,7 +12,7 @@ extern "C" { #include #include #include "pycore_structs.h" // _Py_BackoffCounter -#include "pycore_tstate.h" // _PyPolicy +#include "pycore_tstate.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. */ diff --git a/Include/internal/pycore_interp_structs.h b/Include/internal/pycore_interp_structs.h index 3fe1fdaa1589b6..341fe36ffb7eeb 100644 --- a/Include/internal/pycore_interp_structs.h +++ b/Include/internal/pycore_interp_structs.h @@ -945,6 +945,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; diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h index ff9327ff57833a..3e27e934616248 100644 --- a/Include/internal/pycore_tstate.h +++ b/Include/internal/pycore_tstate.h @@ -62,20 +62,20 @@ typedef struct _PyJitTracerState { #endif -typedef struct _PyJitPolicy { - uint16_t side_exit_initial_value; - uint16_t side_exit_initial_backoff; -} _PyJitPolicy; - -typedef struct _PyInterpreterPolicy { +// 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; -} _PyInterpreterPolicy; -typedef struct _PyPolicy { - _PyJitPolicy jit; - _PyInterpreterPolicy interp; -} _PyPolicy; + // JIT optimization thresholds + uint16_t side_exit_initial_value; + uint16_t side_exit_initial_backoff; + + // Optimization flags + bool specialization_enabled; +} _PyOptimizationConfig; // Every PyThreadState is actually allocated as a _PyThreadStateImpl. The // PyThreadState fields are exposed as part of the C API, although most fields @@ -155,7 +155,6 @@ typedef struct _PyThreadStateImpl { #if _Py_TIER2 _PyJitTracerState *jit_tracer_state; #endif - _PyPolicy policy; } _PyThreadStateImpl; #ifdef __cplusplus diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 3aea2038fd17e7..ed3cc41480ab5c 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -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 * diff --git a/Python/ceval.c b/Python/ceval.c index dfd014e90b0e17..7dbf0deac9df4d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1472,7 +1472,7 @@ stop_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame) 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 { @@ -1482,7 +1482,7 @@ stop_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame) 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); } } _PyJit_FinalizeTracing(tstate); diff --git a/Python/optimizer.c b/Python/optimizer.c index 3c561a8a7fd0e8..a9fc491c680916 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -1351,9 +1351,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]; diff --git a/Python/pystate.c b/Python/pystate.c index b3d375a7feabb0..2b3fed8bad7338 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -514,6 +514,21 @@ _Py_LazyJitShim( main interpreter. We fix those fields here, in addition to the other dynamically initialized fields. */ + +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 +587,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 + init_policy(&interp->opt_config.jump_backward_initial_value, + "PYTHON_JIT_JUMP_BACKWARD_INITIAL_VALUE", + JUMP_BACKWARD_INITIAL_VALUE, 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); +#ifdef _Py_TIER2 + init_policy(&interp->opt_config.side_exit_initial_value, + "PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE", + SIDE_EXIT_INITIAL_VALUE, 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); +#endif + + // Check if specialization should be disabled + // If PYTHON_SPECIALIZATION_OFF is set to any non-empty value, disable specialization + char *spec_off_env = Py_GETENV("PYTHON_SPECIALIZATION_OFF"); + if (spec_off_env && *spec_off_env != '\0' && *spec_off_env != '0') { + interp->opt_config.specialization_enabled = false; + } else { + interp->opt_config.specialization_enabled = true; + } if (interp != &runtime->_main_interpreter) { /* Fix the self-referential, statically initialized fields. */ interp->dtoa = (struct _dtoa_state)_dtoa_state_INIT(interp); @@ -1439,20 +1479,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 +1564,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; diff --git a/Python/specialize.c b/Python/specialize.c index 80db7d01f38f1e..2f82fb4ff4ef84 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -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 { From 3dbdd01140a00fc69ad25ee15efee72766b94ae0 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 10 Jan 2026 17:14:10 +0900 Subject: [PATCH 2/4] nit --- Include/internal/pycore_backoff.h | 2 +- Include/internal/pycore_interp_structs.h | 15 +++++++++++++++ Include/internal/pycore_tstate.h | 15 --------------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index 1707a00d97754f..ee907ae0534e4f 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -12,7 +12,7 @@ extern "C" { #include #include #include "pycore_structs.h" // _Py_BackoffCounter -#include "pycore_tstate.h" // _PyOptimizationConfig +#include "pycore_interp_structs.h" // _PyOptimizationConfig /* 16-bit countdown counters using exponential backoff. diff --git a/Include/internal/pycore_interp_structs.h b/Include/internal/pycore_interp_structs.h index 341fe36ffb7eeb..f11448b06696ad 100644 --- a/Include/internal/pycore_interp_structs.h +++ b/Include/internal/pycore_interp_structs.h @@ -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; diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h index 3e27e934616248..7e6dd88066ed10 100644 --- a/Include/internal/pycore_tstate.h +++ b/Include/internal/pycore_tstate.h @@ -62,21 +62,6 @@ typedef struct _PyJitTracerState { #endif -// 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; - // 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. From c8e4b7f05763c6b6189c4fd5531ce4b4be648af7 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 10 Jan 2026 22:46:09 +0900 Subject: [PATCH 3/4] Address code review --- Python/pystate.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c index 2b3fed8bad7338..5f9200da527d7f 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -515,6 +515,13 @@ _Py_LazyJitShim( 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) @@ -589,29 +596,31 @@ init_interpreter(PyInterpreterState *interp, 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_BACKWARD_INITIAL_VALUE, 1, MAX_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); #ifdef _Py_TIER2 init_policy(&interp->opt_config.side_exit_initial_value, "PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE", - SIDE_EXIT_INITIAL_VALUE, 1, MAX_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); #endif - // Check if specialization should be disabled - // If PYTHON_SPECIALIZATION_OFF is set to any non-empty value, disable specialization - char *spec_off_env = Py_GETENV("PYTHON_SPECIALIZATION_OFF"); - if (spec_off_env && *spec_off_env != '\0' && *spec_off_env != '0') { - interp->opt_config.specialization_enabled = false; - } else { - interp->opt_config.specialization_enabled = true; - } + 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); From ed1a96f540a7508db8990e218e319a4ef5cdb1df Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sun, 11 Jan 2026 18:31:36 +0900 Subject: [PATCH 4/4] fix ci --- Python/pystate.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c index 5f9200da527d7f..00c80adce28c3f 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -611,14 +611,12 @@ init_interpreter(PyInterpreterState *interp, init_policy(&interp->opt_config.jump_backward_initial_backoff, "PYTHON_JIT_JUMP_BACKWARD_INITIAL_BACKOFF", JUMP_BACKWARD_INITIAL_BACKOFF, 0, MAX_BACKOFF); -#ifdef _Py_TIER2 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); -#endif interp->opt_config.specialization_enabled = !is_env_enabled("PYTHON_SPECIALIZATION_OFF"); if (interp != &runtime->_main_interpreter) {