https://github.com/python/cpython/commit/12283f6373baba43cf1e03e5e06e34c924247f7d
commit: 12283f6373baba43cf1e03e5e06e34c924247f7d
branch: main
author: Donghee Na <[email protected]>
committer: corona10 <[email protected]>
date: 2026-01-03T15:22:14Z
summary:

gh-141504: Factor out tracing and optimization heuristics into a single object 
(gh-143381)

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-01-03-15-44-51.gh-issue-141504.sbnJlM.rst
M Include/internal/pycore_backoff.h
M Include/internal/pycore_tstate.h
M Python/ceval.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 23ca7299e0d2bd..fadd11f04ec5ca 100644
--- a/Include/internal/pycore_backoff.h
+++ b/Include/internal/pycore_backoff.h
@@ -12,6 +12,7 @@ extern "C" {
 #include <assert.h>
 #include <stdbool.h>
 #include "pycore_structs.h"       // _Py_BackoffCounter
+#include "pycore_tstate.h"        // _PyPolicy
 
 /* 16-bit countdown counters using exponential backoff.
 
@@ -127,10 +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(void)
+initial_jump_backoff_counter(_PyPolicy *policy)
 {
-    return make_backoff_counter(JUMP_BACKWARD_INITIAL_VALUE,
-                                JUMP_BACKWARD_INITIAL_BACKOFF);
+    return make_backoff_counter(
+        policy->interp.jump_backward_initial_value,
+        policy->interp.jump_backward_initial_backoff);
 }
 
 /* Initial exit temperature.
@@ -141,10 +143,11 @@ initial_jump_backoff_counter(void)
 #define SIDE_EXIT_INITIAL_BACKOFF 6
 
 static inline _Py_BackoffCounter
-initial_temperature_backoff_counter(void)
+initial_temperature_backoff_counter(_PyPolicy *policy)
 {
-    return make_backoff_counter(SIDE_EXIT_INITIAL_VALUE,
-                                SIDE_EXIT_INITIAL_BACKOFF);
+    return make_backoff_counter(
+        policy->jit.side_exit_initial_value,
+        policy->jit.side_exit_initial_backoff);
 }
 
 /* Unreachable backoff counter. */
diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h
index 1a7ebb01403208..27299d1ebcb57a 100644
--- a/Include/internal/pycore_tstate.h
+++ b/Include/internal/pycore_tstate.h
@@ -52,8 +52,24 @@ typedef struct _PyJitTracerState {
     _PyJitTracerInitialState initial_state;
     _PyJitTracerPreviousState prev_state;
 } _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.
@@ -132,6 +148,7 @@ typedef struct _PyThreadStateImpl {
 #if _Py_TIER2
     _PyJitTracerState jit_tracer_state;
 #endif
+    _PyPolicy policy;
 } _PyThreadStateImpl;
 
 #ifdef __cplusplus
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-01-03-15-44-51.gh-issue-141504.sbnJlM.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-01-03-15-44-51.gh-issue-141504.sbnJlM.rst
new file mode 100644
index 00000000000000..627a2dbbdcf7f2
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-01-03-15-44-51.gh-issue-141504.sbnJlM.rst
@@ -0,0 +1,2 @@
+Factor out tracing and optimization heuristics into a single object.
+Patch by Donghee Na.
diff --git a/Python/ceval.c b/Python/ceval.c
index d90ac5c69b9e81..c9868ef8cc6525 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -1473,7 +1473,7 @@ stop_tracing_and_jit(PyThreadState *tstate, 
_PyInterpreterFrame *frame)
             
_tstate->jit_tracer_state.initial_state.jump_backward_instr[1].counter = 
restart_backoff_counter(counter);
         }
         else {
-            
_tstate->jit_tracer_state.initial_state.jump_backward_instr[1].counter = 
initial_jump_backoff_counter();
+            
_tstate->jit_tracer_state.initial_state.jump_backward_instr[1].counter = 
initial_jump_backoff_counter(&_tstate->policy);
         }
     }
     else {
@@ -1483,7 +1483,7 @@ stop_tracing_and_jit(PyThreadState *tstate, 
_PyInterpreterFrame *frame)
             exit->temperature = restart_backoff_counter(exit->temperature);
         }
         else {
-            exit->temperature = initial_temperature_backoff_counter();
+            exit->temperature = 
initial_temperature_backoff_counter(&_tstate->policy);
         }
     }
     _PyJit_FinalizeTracing(tstate);
diff --git a/Python/optimizer.c b/Python/optimizer.c
index 900b07473fe351..d32fae2e489af4 100644
--- a/Python/optimizer.c
+++ b/Python/optimizer.c
@@ -113,7 +113,7 @@ insert_executor(PyCodeObject *code, _Py_CODEUNIT *instr, 
int index, _PyExecutorO
 }
 
 static _PyExecutorObject *
-make_executor_from_uops(_PyUOpInstruction *buffer, int length, const 
_PyBloomFilter *dependencies, int chain_depth);
+make_executor_from_uops(_PyThreadStateImpl *tstate, _PyUOpInstruction *buffer, 
int length, const _PyBloomFilter *dependencies);
 
 static int
 uop_optimize(_PyInterpreterFrame *frame, PyThreadState *tstate,
@@ -1328,7 +1328,7 @@ sanity_check(_PyExecutorObject *executor)
  * and not a NOP.
  */
 static _PyExecutorObject *
-make_executor_from_uops(_PyUOpInstruction *buffer, int length, const 
_PyBloomFilter *dependencies, int chain_depth)
+make_executor_from_uops(_PyThreadStateImpl *tstate, _PyUOpInstruction *buffer, 
int length, const _PyBloomFilter *dependencies)
 {
     int exit_count = count_exits(buffer, length);
     _PyExecutorObject *executor = allocate_executor(exit_count, length);
@@ -1337,12 +1337,13 @@ make_executor_from_uops(_PyUOpInstruction *buffer, int 
length, const _PyBloomFil
     }
 
     /* Initialize exits */
+    int chain_depth = tstate->jit_tracer_state.initial_state.chain_depth;
     _PyExecutorObject *cold = _PyExecutor_GetColdExecutor();
     _PyExecutorObject *cold_dynamic = _PyExecutor_GetColdDynamicExecutor();
     cold->vm_data.chain_depth = chain_depth;
     for (int i = 0; i < exit_count; i++) {
         executor->exits[i].index = i;
-        executor->exits[i].temperature = initial_temperature_backoff_counter();
+        executor->exits[i].temperature = 
initial_temperature_backoff_counter(&tstate->policy);
     }
     int next_exit = exit_count-1;
     _PyUOpInstruction *dest = (_PyUOpInstruction *)&executor->trace[length];
@@ -1510,7 +1511,7 @@ uop_optimize(
     length = prepare_for_execution(buffer, length);
     assert(length <= UOP_MAX_TRACE_LENGTH);
     _PyExecutorObject *executor = make_executor_from_uops(
-        buffer, length, dependencies, 
_tstate->jit_tracer_state.initial_state.chain_depth);
+        _tstate, buffer, length, dependencies);
     if (executor == NULL) {
         return -1;
     }
diff --git a/Python/pystate.c b/Python/pystate.c
index cf55297cf8d94e..f605527598a86d 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -4,6 +4,7 @@
 #include "Python.h"
 #include "pycore_abstract.h"      // _PyIndex_Check()
 #include "pycore_audit.h"         // _Py_AuditHookEntry
+#include "pycore_backoff.h"       // JUMP_BACKWARD_INITIAL_VALUE, 
SIDE_EXIT_INITIAL_VALUE
 #include "pycore_ceval.h"         // _PyEval_AcquireLock()
 #include "pycore_codecs.h"        // _PyCodec_Fini()
 #include "pycore_critical_section.h" // _PyCriticalSection_Resume()
@@ -1438,6 +1439,20 @@ 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,
@@ -1523,8 +1538,21 @@ 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.code_buffer = NULL;
 #endif
     tstate->delete_later = NULL;
diff --git a/Python/specialize.c b/Python/specialize.c
index e9302157e7782a..d19d4a8f7a1ea7 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -47,7 +47,9 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, 
int enable_counters
     #if ENABLE_SPECIALIZATION_FT
     _Py_BackoffCounter jump_counter, adaptive_counter;
     if (enable_counters) {
-        jump_counter = initial_jump_backoff_counter();
+        PyThreadState *tstate = _PyThreadState_GET();
+        _PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate;
+        jump_counter = initial_jump_backoff_counter(&tstate_impl->policy);
         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