From 0ebb67af4f9e63a236ee0bc054178ec9ae523934 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 3 Jan 2026 15:30:19 +0900 Subject: [PATCH 01/15] gh-141504: Configurable JIT optimization thresholds --- Include/internal/pycore_backoff.h | 10 ++++---- Include/internal/pycore_tstate.h | 8 +++++++ Python/ceval.c | 8 +++++-- Python/optimizer.c | 11 +++++---- Python/pystate.c | 39 +++++++++++++++++++++++++++++++ Python/specialize.c | 6 ++++- 6 files changed, 69 insertions(+), 13 deletions(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index 23ca7299e0d2bd..7ca1df58ad4046 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -127,10 +127,9 @@ 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(uint16_t initial_value, uint16_t initial_backoff) { - return make_backoff_counter(JUMP_BACKWARD_INITIAL_VALUE, - JUMP_BACKWARD_INITIAL_BACKOFF); + return make_backoff_counter(initial_value, initial_backoff); } /* Initial exit temperature. @@ -141,10 +140,9 @@ initial_jump_backoff_counter(void) #define SIDE_EXIT_INITIAL_BACKOFF 6 static inline _Py_BackoffCounter -initial_temperature_backoff_counter(void) +initial_temperature_backoff_counter(uint16_t initial_value, uint16_t initial_backoff) { - return make_backoff_counter(SIDE_EXIT_INITIAL_VALUE, - SIDE_EXIT_INITIAL_BACKOFF); + return make_backoff_counter(initial_value, initial_backoff); } /* Unreachable backoff counter. */ diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h index 1a7ebb01403208..523ef03929e21d 100644 --- a/Include/internal/pycore_tstate.h +++ b/Include/internal/pycore_tstate.h @@ -52,6 +52,13 @@ typedef struct _PyJitTracerState { _PyJitTracerInitialState initial_state; _PyJitTracerPreviousState prev_state; } _PyJitTracerState; + +typedef struct _PyJitMetrics { + uint16_t jump_backward_initial_value; + uint16_t jump_backward_initial_backoff; + uint16_t side_exit_initial_value; + uint16_t side_exit_initial_backoff; +} _PyJitMetrics; #endif // Every PyThreadState is actually allocated as a _PyThreadStateImpl. The @@ -131,6 +138,7 @@ typedef struct _PyThreadStateImpl { #endif #if _Py_TIER2 _PyJitTracerState jit_tracer_state; + _PyJitMetrics jit_metrics; #endif } _PyThreadStateImpl; diff --git a/Python/ceval.c b/Python/ceval.c index d90ac5c69b9e81..17ab7145f91aea 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1473,7 +1473,9 @@ 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->jit_metrics.jump_backward_initial_value, + _tstate->jit_metrics.jump_backward_initial_backoff); } } else { @@ -1483,7 +1485,9 @@ 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->jit_metrics.side_exit_initial_value, + _tstate->jit_metrics.side_exit_initial_backoff); } } _PyJit_FinalizeTracing(tstate); diff --git a/Python/optimizer.c b/Python/optimizer.c index 900b07473fe351..07790d01802c26 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,15 @@ 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->jit_metrics.side_exit_initial_value, + tstate->jit_metrics.side_exit_initial_backoff); } int next_exit = exit_count-1; _PyUOpInstruction *dest = (_PyUOpInstruction *)&executor->trace[length]; @@ -1510,7 +1513,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..cd90c3f945ab23 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() @@ -1526,6 +1527,44 @@ init_threadstate(_PyThreadStateImpl *_tstate, #ifdef _Py_TIER2 _tstate->jit_tracer_state.code_buffer = NULL; + + // Initialize JIT metrics from environment variables + _tstate->jit_metrics.jump_backward_initial_value = JUMP_BACKWARD_INITIAL_VALUE; + _tstate->jit_metrics.jump_backward_initial_backoff = JUMP_BACKWARD_INITIAL_BACKOFF; + _tstate->jit_metrics.side_exit_initial_value = SIDE_EXIT_INITIAL_VALUE; + _tstate->jit_metrics.side_exit_initial_backoff = SIDE_EXIT_INITIAL_BACKOFF; + + char *env = Py_GETENV("PYTHON_JIT_JUMP_BACKWARD_INITIAL_VALUE"); + if (env && *env != '\0') { + long value = atol(env); + if (value > 0 && value <= MAX_VALUE) { + _tstate->jit_metrics.jump_backward_initial_value = (uint16_t)value; + } + } + + env = Py_GETENV("PYTHON_JIT_JUMP_BACKWARD_INITIAL_BACKOFF"); + if (env && *env != '\0') { + long value = atol(env); + if (value >= 0 && value <= MAX_BACKOFF) { + _tstate->jit_metrics.jump_backward_initial_backoff = (uint16_t)value; + } + } + + env = Py_GETENV("PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE"); + if (env && *env != '\0') { + long value = atol(env); + if (value > 0 && value <= MAX_VALUE) { + _tstate->jit_metrics.side_exit_initial_value = (uint16_t)value; + } + } + + env = Py_GETENV("PYTHON_JIT_SIDE_EXIT_INITIAL_BACKOFF"); + if (env && *env != '\0') { + long value = atol(env); + if (value >= 0 && value <= MAX_BACKOFF) { + _tstate->jit_metrics.side_exit_initial_backoff = (uint16_t)value; + } + } #endif tstate->delete_later = NULL; diff --git a/Python/specialize.c b/Python/specialize.c index e9302157e7782a..726d008f2ddb5b 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -47,7 +47,11 @@ _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->jit_metrics.jump_backward_initial_value, + tstate_impl->jit_metrics.jump_backward_initial_backoff); adaptive_counter = adaptive_counter_warmup(); } else { From 2b1cbbcdc0d803205bef7868b70005666e794e6a Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 3 Jan 2026 15:47:44 +0900 Subject: [PATCH 02/15] Add NEWS.d --- .../2026-01-03-15-44-51.gh-issue-141504.sbnJlM.rst | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-01-03-15-44-51.gh-issue-141504.sbnJlM.rst 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..6bbd1b9c8d8805 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-01-03-15-44-51.gh-issue-141504.sbnJlM.rst @@ -0,0 +1,5 @@ +paragraph. # Don't start with "- Issue #: " or "- gh-issue-: " or that +sort of stuff. +########################################################################### +Factor out tracing and optimization heuristics into a single object. Patch +by Donghee Na. From 7fce73116eca75b2f5f2b0e8be10f852f4743330 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 3 Jan 2026 15:49:06 +0900 Subject: [PATCH 03/15] nit --- .../2026-01-03-15-44-51.gh-issue-141504.sbnJlM.rst | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) 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 index 6bbd1b9c8d8805..627a2dbbdcf7f2 100644 --- 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 @@ -1,5 +1,2 @@ -paragraph. # Don't start with "- Issue #: " or "- gh-issue-: " or that -sort of stuff. -########################################################################### -Factor out tracing and optimization heuristics into a single object. Patch -by Donghee Na. +Factor out tracing and optimization heuristics into a single object. +Patch by Donghee Na. From d01f5724874a43bda1fa1d458b039c4e391986cc Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 3 Jan 2026 16:48:58 +0900 Subject: [PATCH 04/15] fix --- Include/internal/pycore_tstate.h | 1 + Python/pystate.c | 4 +--- Python/specialize.c | 6 ++---- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h index 523ef03929e21d..90e9bd097e257d 100644 --- a/Include/internal/pycore_tstate.h +++ b/Include/internal/pycore_tstate.h @@ -59,6 +59,7 @@ typedef struct _PyJitMetrics { uint16_t side_exit_initial_value; uint16_t side_exit_initial_backoff; } _PyJitMetrics; + #endif // Every PyThreadState is actually allocated as a _PyThreadStateImpl. The diff --git a/Python/pystate.c b/Python/pystate.c index cd90c3f945ab23..0ff743bae7817c 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1524,10 +1524,7 @@ init_threadstate(_PyThreadStateImpl *_tstate, _tstate->asyncio_running_loop = NULL; _tstate->asyncio_running_task = NULL; - #ifdef _Py_TIER2 - _tstate->jit_tracer_state.code_buffer = NULL; - // Initialize JIT metrics from environment variables _tstate->jit_metrics.jump_backward_initial_value = JUMP_BACKWARD_INITIAL_VALUE; _tstate->jit_metrics.jump_backward_initial_backoff = JUMP_BACKWARD_INITIAL_BACKOFF; @@ -1565,6 +1562,7 @@ init_threadstate(_PyThreadStateImpl *_tstate, _tstate->jit_metrics.side_exit_initial_backoff = (uint16_t)value; } } + _tstate->jit_tracer_state.code_buffer = NULL; #endif tstate->delete_later = NULL; diff --git a/Python/specialize.c b/Python/specialize.c index 726d008f2ddb5b..87a6f3eb782241 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -47,11 +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) { - PyThreadState *tstate = _PyThreadState_GET(); - _PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate; jump_counter = initial_jump_backoff_counter( - tstate_impl->jit_metrics.jump_backward_initial_value, - tstate_impl->jit_metrics.jump_backward_initial_backoff); + JUMP_BACKWARD_INITIAL_VALUE, + JUMP_BACKWARD_INITIAL_BACKOFF); adaptive_counter = adaptive_counter_warmup(); } else { From 1942f720bca3ff2a4cba616cb76026e78ee05f32 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 3 Jan 2026 16:59:22 +0900 Subject: [PATCH 05/15] nit --- Python/pystate.c | 64 +++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c index 0ff743bae7817c..834eaf40851db1 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1439,6 +1439,22 @@ decref_threadstate(_PyThreadStateImpl *tstate) } } +#ifdef _Py_TIER2 +static inline void +init_jit_metric(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; + } + } +} +#endif + /* 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, @@ -1526,42 +1542,18 @@ init_threadstate(_PyThreadStateImpl *_tstate, _tstate->asyncio_running_task = NULL; #ifdef _Py_TIER2 // Initialize JIT metrics from environment variables - _tstate->jit_metrics.jump_backward_initial_value = JUMP_BACKWARD_INITIAL_VALUE; - _tstate->jit_metrics.jump_backward_initial_backoff = JUMP_BACKWARD_INITIAL_BACKOFF; - _tstate->jit_metrics.side_exit_initial_value = SIDE_EXIT_INITIAL_VALUE; - _tstate->jit_metrics.side_exit_initial_backoff = SIDE_EXIT_INITIAL_BACKOFF; - - char *env = Py_GETENV("PYTHON_JIT_JUMP_BACKWARD_INITIAL_VALUE"); - if (env && *env != '\0') { - long value = atol(env); - if (value > 0 && value <= MAX_VALUE) { - _tstate->jit_metrics.jump_backward_initial_value = (uint16_t)value; - } - } - - env = Py_GETENV("PYTHON_JIT_JUMP_BACKWARD_INITIAL_BACKOFF"); - if (env && *env != '\0') { - long value = atol(env); - if (value >= 0 && value <= MAX_BACKOFF) { - _tstate->jit_metrics.jump_backward_initial_backoff = (uint16_t)value; - } - } - - env = Py_GETENV("PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE"); - if (env && *env != '\0') { - long value = atol(env); - if (value > 0 && value <= MAX_VALUE) { - _tstate->jit_metrics.side_exit_initial_value = (uint16_t)value; - } - } - - env = Py_GETENV("PYTHON_JIT_SIDE_EXIT_INITIAL_BACKOFF"); - if (env && *env != '\0') { - long value = atol(env); - if (value >= 0 && value <= MAX_BACKOFF) { - _tstate->jit_metrics.side_exit_initial_backoff = (uint16_t)value; - } - } + init_jit_metric(&_tstate->jit_metrics.jump_backward_initial_value, + "PYTHON_JIT_JUMP_BACKWARD_INITIAL_VALUE", + JUMP_BACKWARD_INITIAL_VALUE, 1, MAX_VALUE); + init_jit_metric(&_tstate->jit_metrics.jump_backward_initial_backoff, + "PYTHON_JIT_JUMP_BACKWARD_INITIAL_BACKOFF", + JUMP_BACKWARD_INITIAL_BACKOFF, 0, MAX_BACKOFF); + init_jit_metric(&_tstate->jit_metrics.side_exit_initial_value, + "PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE", + SIDE_EXIT_INITIAL_VALUE, 1, MAX_VALUE); + init_jit_metric(&_tstate->jit_metrics.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; From a0e69655daca78df29832a3292d5faf7b00da142 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 3 Jan 2026 20:55:26 +0900 Subject: [PATCH 06/15] Address code review --- Include/internal/pycore_tstate.h | 10 +++++++--- Python/ceval.c | 8 ++++---- Python/optimizer.c | 4 ++-- Python/pystate.c | 10 +++++----- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h index 90e9bd097e257d..8c7bf73f12af0f 100644 --- a/Include/internal/pycore_tstate.h +++ b/Include/internal/pycore_tstate.h @@ -53,12 +53,16 @@ typedef struct _PyJitTracerState { _PyJitTracerPreviousState prev_state; } _PyJitTracerState; -typedef struct _PyJitMetrics { +typedef struct _PyJitPolicy { uint16_t jump_backward_initial_value; uint16_t jump_backward_initial_backoff; uint16_t side_exit_initial_value; uint16_t side_exit_initial_backoff; -} _PyJitMetrics; +} _PyJitPolicy; + +typedef struct _PyPolicy { + _PyJitPolicy jit; +} _PyPolicy; #endif @@ -139,7 +143,7 @@ typedef struct _PyThreadStateImpl { #endif #if _Py_TIER2 _PyJitTracerState jit_tracer_state; - _PyJitMetrics jit_metrics; + _PyPolicy policy; #endif } _PyThreadStateImpl; diff --git a/Python/ceval.c b/Python/ceval.c index 17ab7145f91aea..34127962b53d64 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1474,8 +1474,8 @@ stop_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame) } else { _tstate->jit_tracer_state.initial_state.jump_backward_instr[1].counter = initial_jump_backoff_counter( - _tstate->jit_metrics.jump_backward_initial_value, - _tstate->jit_metrics.jump_backward_initial_backoff); + _tstate->policy.jit.jump_backward_initial_value, + _tstate->policy.jit.jump_backward_initial_backoff); } } else { @@ -1486,8 +1486,8 @@ stop_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame) } else { exit->temperature = initial_temperature_backoff_counter( - _tstate->jit_metrics.side_exit_initial_value, - _tstate->jit_metrics.side_exit_initial_backoff); + _tstate->policy.jit.side_exit_initial_value, + _tstate->policy.jit.side_exit_initial_backoff); } } _PyJit_FinalizeTracing(tstate); diff --git a/Python/optimizer.c b/Python/optimizer.c index 07790d01802c26..2229866ce0874b 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -1344,8 +1344,8 @@ make_executor_from_uops(_PyThreadStateImpl *tstate, _PyUOpInstruction *buffer, i for (int i = 0; i < exit_count; i++) { executor->exits[i].index = i; executor->exits[i].temperature = initial_temperature_backoff_counter( - tstate->jit_metrics.side_exit_initial_value, - tstate->jit_metrics.side_exit_initial_backoff); + tstate->policy.jit.side_exit_initial_value, + tstate->policy.jit.side_exit_initial_backoff); } int next_exit = exit_count-1; _PyUOpInstruction *dest = (_PyUOpInstruction *)&executor->trace[length]; diff --git a/Python/pystate.c b/Python/pystate.c index 834eaf40851db1..58de4c9db7bf2e 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1541,17 +1541,17 @@ init_threadstate(_PyThreadStateImpl *_tstate, _tstate->asyncio_running_loop = NULL; _tstate->asyncio_running_task = NULL; #ifdef _Py_TIER2 - // Initialize JIT metrics from environment variables - init_jit_metric(&_tstate->jit_metrics.jump_backward_initial_value, + // Initialize JIT policy from environment variables + init_jit_metric(&_tstate->policy.jit.jump_backward_initial_value, "PYTHON_JIT_JUMP_BACKWARD_INITIAL_VALUE", JUMP_BACKWARD_INITIAL_VALUE, 1, MAX_VALUE); - init_jit_metric(&_tstate->jit_metrics.jump_backward_initial_backoff, + init_jit_metric(&_tstate->policy.jit.jump_backward_initial_backoff, "PYTHON_JIT_JUMP_BACKWARD_INITIAL_BACKOFF", JUMP_BACKWARD_INITIAL_BACKOFF, 0, MAX_BACKOFF); - init_jit_metric(&_tstate->jit_metrics.side_exit_initial_value, + init_jit_metric(&_tstate->policy.jit.side_exit_initial_value, "PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE", SIDE_EXIT_INITIAL_VALUE, 1, MAX_VALUE); - init_jit_metric(&_tstate->jit_metrics.side_exit_initial_backoff, + init_jit_metric(&_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; From 196d204a461886bba04550f77483337c5450a2be Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 3 Jan 2026 21:09:22 +0900 Subject: [PATCH 07/15] nit --- Python/specialize.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Python/specialize.c b/Python/specialize.c index 87a6f3eb782241..512528c63a7e54 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -47,9 +47,11 @@ _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) { + PyThreadState *tstate = _PyThreadState_GET(); + _PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate; jump_counter = initial_jump_backoff_counter( - JUMP_BACKWARD_INITIAL_VALUE, - JUMP_BACKWARD_INITIAL_BACKOFF); + tstate_impl->policy.jit.jump_backward_initial_value, + tstate_impl->policy.jit.jump_backward_initial_backoff); adaptive_counter = adaptive_counter_warmup(); } else { From f0ccb2898e6ee7a0d34e1d732418632abd7d64b0 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 3 Jan 2026 22:38:38 +0900 Subject: [PATCH 08/15] Address code review --- Include/internal/pycore_tstate.h | 16 ++++++++++++---- Python/ceval.c | 4 ++-- Python/pystate.c | 11 +++++------ Python/specialize.c | 4 ++-- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h index 8c7bf73f12af0f..8239c349476780 100644 --- a/Include/internal/pycore_tstate.h +++ b/Include/internal/pycore_tstate.h @@ -54,17 +54,25 @@ typedef struct _PyJitTracerState { } _PyJitTracerState; typedef struct _PyJitPolicy { - uint16_t jump_backward_initial_value; - uint16_t jump_backward_initial_backoff; uint16_t side_exit_initial_value; uint16_t side_exit_initial_backoff; } _PyJitPolicy; +#endif + +typedef struct _PyInterpreterPolicy { + uint16_t jump_backward_initial_value; + uint16_t jump_backward_initial_backoff; +} _PyInterpreterPolicy; + + typedef struct _PyPolicy { - _PyJitPolicy jit; -} _PyPolicy; +#ifdef _Py_TIER2 + _PyJitPolicy jit; #endif + _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 diff --git a/Python/ceval.c b/Python/ceval.c index 34127962b53d64..875d72b913ee1a 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1474,8 +1474,8 @@ stop_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame) } else { _tstate->jit_tracer_state.initial_state.jump_backward_instr[1].counter = initial_jump_backoff_counter( - _tstate->policy.jit.jump_backward_initial_value, - _tstate->policy.jit.jump_backward_initial_backoff); + _tstate->policy.interp.jump_backward_initial_value, + _tstate->policy.interp.jump_backward_initial_backoff); } } else { diff --git a/Python/pystate.c b/Python/pystate.c index 58de4c9db7bf2e..d1482340c0b61a 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1439,7 +1439,6 @@ decref_threadstate(_PyThreadStateImpl *tstate) } } -#ifdef _Py_TIER2 static inline void init_jit_metric(uint16_t *target, const char *env_name, uint16_t default_value, long min_value, long max_value) @@ -1453,7 +1452,6 @@ init_jit_metric(uint16_t *target, const char *env_name, uint16_t default_value, } } } -#endif /* Get the thread state to a minimal consistent state. Further init happens in pylifecycle.c before it can be used. @@ -1540,14 +1538,15 @@ init_threadstate(_PyThreadStateImpl *_tstate, _tstate->asyncio_running_loop = NULL; _tstate->asyncio_running_task = NULL; -#ifdef _Py_TIER2 - // Initialize JIT policy from environment variables - init_jit_metric(&_tstate->policy.jit.jump_backward_initial_value, + // Initialize interpreter policy from environment variables + init_jit_metric(&_tstate->policy.interp.jump_backward_initial_value, "PYTHON_JIT_JUMP_BACKWARD_INITIAL_VALUE", JUMP_BACKWARD_INITIAL_VALUE, 1, MAX_VALUE); - init_jit_metric(&_tstate->policy.jit.jump_backward_initial_backoff, + init_jit_metric(&_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_jit_metric(&_tstate->policy.jit.side_exit_initial_value, "PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE", SIDE_EXIT_INITIAL_VALUE, 1, MAX_VALUE); diff --git a/Python/specialize.c b/Python/specialize.c index 512528c63a7e54..a68eb596a23b71 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -50,8 +50,8 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters PyThreadState *tstate = _PyThreadState_GET(); _PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate; jump_counter = initial_jump_backoff_counter( - tstate_impl->policy.jit.jump_backward_initial_value, - tstate_impl->policy.jit.jump_backward_initial_backoff); + tstate_impl->policy.interp.jump_backward_initial_value, + tstate_impl->policy.interp.jump_backward_initial_backoff); adaptive_counter = adaptive_counter_warmup(); } else { From b3b608bb300351693e521728403de4e674f228c8 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 3 Jan 2026 22:54:53 +0900 Subject: [PATCH 09/15] fix --- Include/internal/pycore_tstate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h index 8239c349476780..16e9485382551d 100644 --- a/Include/internal/pycore_tstate.h +++ b/Include/internal/pycore_tstate.h @@ -151,8 +151,8 @@ typedef struct _PyThreadStateImpl { #endif #if _Py_TIER2 _PyJitTracerState jit_tracer_state; - _PyPolicy policy; #endif + _PyPolicy policy; } _PyThreadStateImpl; #ifdef __cplusplus From 6a32b9a2aa70bf37f438650454a4d5e7be1743e6 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 3 Jan 2026 23:49:04 +0900 Subject: [PATCH 10/15] Address code review --- Include/internal/pycore_backoff.h | 15 +++++++++++---- Python/ceval.c | 7 ++----- Python/optimizer.c | 4 +--- Python/pystate.c | 26 +++++++++++++------------- Python/specialize.c | 4 +--- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index 7ca1df58ad4046..650e8b028cfb25 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -12,6 +12,7 @@ extern "C" { #include #include #include "pycore_structs.h" // _Py_BackoffCounter +#include "pycore_tstate.h" // _PyThreadStateImpl /* 16-bit countdown counters using exponential backoff. @@ -127,9 +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(uint16_t initial_value, uint16_t initial_backoff) +initial_jump_backoff_counter(_PyPolicy *policy) { - return make_backoff_counter(initial_value, initial_backoff); + return make_backoff_counter( + policy->interp.jump_backward_initial_value, + policy->interp.jump_backward_initial_backoff); } /* Initial exit temperature. @@ -139,11 +142,15 @@ initial_jump_backoff_counter(uint16_t initial_value, uint16_t initial_backoff) #define SIDE_EXIT_INITIAL_VALUE 4000 #define SIDE_EXIT_INITIAL_BACKOFF 6 +#ifdef _TIER2 static inline _Py_BackoffCounter -initial_temperature_backoff_counter(uint16_t initial_value, uint16_t initial_backoff) +initial_temperature_backoff_counter(_PyPolicy *policy) { - return make_backoff_counter(initial_value, initial_backoff); + return make_backoff_counter( + policy->jit.side_exit_initial_value, + policy->jit.side_exit_initial_backoff); } +#endif /* Unreachable backoff counter. */ static inline _Py_BackoffCounter diff --git a/Python/ceval.c b/Python/ceval.c index 875d72b913ee1a..6ea7a3c98b3541 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1474,8 +1474,7 @@ stop_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame) } else { _tstate->jit_tracer_state.initial_state.jump_backward_instr[1].counter = initial_jump_backoff_counter( - _tstate->policy.interp.jump_backward_initial_value, - _tstate->policy.interp.jump_backward_initial_backoff); + &_tstate->policy); } } else { @@ -1485,9 +1484,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.jit.side_exit_initial_value, - _tstate->policy.jit.side_exit_initial_backoff); + exit->temperature = initial_temperature_backoff_counter(&_tstate->policy); } } _PyJit_FinalizeTracing(tstate); diff --git a/Python/optimizer.c b/Python/optimizer.c index 2229866ce0874b..d32fae2e489af4 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -1343,9 +1343,7 @@ make_executor_from_uops(_PyThreadStateImpl *tstate, _PyUOpInstruction *buffer, i 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( - tstate->policy.jit.side_exit_initial_value, - tstate->policy.jit.side_exit_initial_backoff); + executor->exits[i].temperature = initial_temperature_backoff_counter(&tstate->policy); } int next_exit = exit_count-1; _PyUOpInstruction *dest = (_PyUOpInstruction *)&executor->trace[length]; diff --git a/Python/pystate.c b/Python/pystate.c index d1482340c0b61a..f605527598a86d 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1440,7 +1440,7 @@ decref_threadstate(_PyThreadStateImpl *tstate) } static inline void -init_jit_metric(uint16_t *target, const char *env_name, uint16_t default_value, +init_policy(uint16_t *target, const char *env_name, uint16_t default_value, long min_value, long max_value) { *target = default_value; @@ -1539,20 +1539,20 @@ init_threadstate(_PyThreadStateImpl *_tstate, _tstate->asyncio_running_loop = NULL; _tstate->asyncio_running_task = NULL; // Initialize interpreter policy from environment variables - init_jit_metric(&_tstate->policy.interp.jump_backward_initial_value, - "PYTHON_JIT_JUMP_BACKWARD_INITIAL_VALUE", - JUMP_BACKWARD_INITIAL_VALUE, 1, MAX_VALUE); - init_jit_metric(&_tstate->policy.interp.jump_backward_initial_backoff, - "PYTHON_JIT_JUMP_BACKWARD_INITIAL_BACKOFF", - JUMP_BACKWARD_INITIAL_BACKOFF, 0, MAX_BACKOFF); + 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_jit_metric(&_tstate->policy.jit.side_exit_initial_value, - "PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE", - SIDE_EXIT_INITIAL_VALUE, 1, MAX_VALUE); - init_jit_metric(&_tstate->policy.jit.side_exit_initial_backoff, - "PYTHON_JIT_SIDE_EXIT_INITIAL_BACKOFF", - SIDE_EXIT_INITIAL_BACKOFF, 0, MAX_BACKOFF); + 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 a68eb596a23b71..d19d4a8f7a1ea7 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -49,9 +49,7 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters if (enable_counters) { PyThreadState *tstate = _PyThreadState_GET(); _PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate; - jump_counter = initial_jump_backoff_counter( - tstate_impl->policy.interp.jump_backward_initial_value, - tstate_impl->policy.interp.jump_backward_initial_backoff); + jump_counter = initial_jump_backoff_counter(&tstate_impl->policy); adaptive_counter = adaptive_counter_warmup(); } else { From f55ce78eb077f6393a34212d0d9cc232c859a711 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 3 Jan 2026 23:51:02 +0900 Subject: [PATCH 11/15] nit --- Include/internal/pycore_backoff.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index 650e8b028cfb25..604226932800f7 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -142,7 +142,6 @@ initial_jump_backoff_counter(_PyPolicy *policy) #define SIDE_EXIT_INITIAL_VALUE 4000 #define SIDE_EXIT_INITIAL_BACKOFF 6 -#ifdef _TIER2 static inline _Py_BackoffCounter initial_temperature_backoff_counter(_PyPolicy *policy) { @@ -150,7 +149,6 @@ initial_temperature_backoff_counter(_PyPolicy *policy) policy->jit.side_exit_initial_value, policy->jit.side_exit_initial_backoff); } -#endif /* Unreachable backoff counter. */ static inline _Py_BackoffCounter From 03fe066132b12800d0832f4a6717834aa5af64f6 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 3 Jan 2026 23:53:20 +0900 Subject: [PATCH 12/15] fix --- Include/internal/pycore_tstate.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h index 16e9485382551d..b9292552f24df3 100644 --- a/Include/internal/pycore_tstate.h +++ b/Include/internal/pycore_tstate.h @@ -53,24 +53,21 @@ typedef struct _PyJitTracerState { _PyJitTracerPreviousState prev_state; } _PyJitTracerState; +#endif + typedef struct _PyJitPolicy { uint16_t side_exit_initial_value; uint16_t side_exit_initial_backoff; } _PyJitPolicy; -#endif - typedef struct _PyInterpreterPolicy { uint16_t jump_backward_initial_value; uint16_t jump_backward_initial_backoff; } _PyInterpreterPolicy; - typedef struct _PyPolicy { -#ifdef _Py_TIER2 _PyJitPolicy jit; -#endif _PyInterpreterPolicy interp; } _PyPolicy; From fe0d38a2b57a0dff2c0c005aca33beb01c0e1a3d Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 3 Jan 2026 23:54:43 +0900 Subject: [PATCH 13/15] nit --- Include/internal/pycore_tstate.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h index b9292552f24df3..27299d1ebcb57a 100644 --- a/Include/internal/pycore_tstate.h +++ b/Include/internal/pycore_tstate.h @@ -66,7 +66,6 @@ typedef struct _PyInterpreterPolicy { } _PyInterpreterPolicy; typedef struct _PyPolicy { - _PyJitPolicy jit; _PyInterpreterPolicy interp; } _PyPolicy; From 304c70cf8c6cfe33e95523bfd1477b788c12d58c Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 3 Jan 2026 23:55:49 +0900 Subject: [PATCH 14/15] nit --- Python/ceval.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 6ea7a3c98b3541..c9868ef8cc6525 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1473,8 +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->policy); + _tstate->jit_tracer_state.initial_state.jump_backward_instr[1].counter = initial_jump_backoff_counter(&_tstate->policy); } } else { From e6e3202822be8530c62a5c06a6a50696d7f0a2da Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 3 Jan 2026 23:57:39 +0900 Subject: [PATCH 15/15] nit --- Include/internal/pycore_backoff.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index 604226932800f7..fadd11f04ec5ca 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" // _PyThreadStateImpl +#include "pycore_tstate.h" // _PyPolicy /* 16-bit countdown counters using exponential backoff.