https://github.com/python/cpython/commit/794f758cd862fbe1872545d73ddc3fc7067272fb
commit: 794f758cd862fbe1872545d73ddc3fc7067272fb
branch: main
author: Donghee Na <[email protected]>
committer: corona10 <[email protected]>
date: 2026-01-15T09:53:00+09:00
summary:

gh-141504: Refactor policy object into a single opt_config (gh-143644)

files:
M Include/internal/pycore_backoff.h
M Include/internal/pycore_interp_structs.h
M Include/internal/pycore_tstate.h
M Lib/test/test_capi/test_opt.py
M Objects/codeobject.c
M Python/optimizer.c
M Python/pystate.c
M Python/specialize.c

diff --git a/Include/internal/pycore_backoff.h 
b/Include/internal/pycore_backoff.h
index fadd11f04ec5ca..ee907ae0534e4f 100644
--- a/Include/internal/pycore_backoff.h
+++ b/Include/internal/pycore_backoff.h
@@ -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. */
diff --git a/Include/internal/pycore_interp_structs.h 
b/Include/internal/pycore_interp_structs.h
index 3fe1fdaa1589b6..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;
@@ -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;
diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h
index 25f4f6ed7078df..dc4444717b4e87 100644
--- a/Include/internal/pycore_tstate.h
+++ b/Include/internal/pycore_tstate.h
@@ -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
diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py
index c0aecb8d841224..79c7f530b8ae89 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -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):
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/optimizer.c b/Python/optimizer.c
index c5f47d7f22c3f0..9892a9731e24f0 100644
--- a/Python/optimizer.c
+++ b/Python/optimizer.c
@@ -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];
diff --git a/Python/pystate.c b/Python/pystate.c
index ebe56b8f32c06b..86dee70734a097 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -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;
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 {

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to