#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.
#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.
#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. */
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;
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;
#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.
#if _Py_TIER2
_PyJitTracerState *jit_tracer_state;
#endif
- _PyPolicy policy;
} _PyThreadStateImpl;
#ifdef __cplusplus
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):
}
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;
}
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
}
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) {
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;
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 *
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) {
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);
_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];
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,
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);
}
}
-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,
_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;
_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 {