Skip to content

Commit 794f758

Browse files
authored
gh-141504: Refactor policy object into a single opt_config (gh-143644)
1 parent a73ba4d commit 794f758

File tree

8 files changed

+89
-64
lines changed

8 files changed

+89
-64
lines changed

Include/internal/pycore_backoff.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ extern "C" {
1212
#include <assert.h>
1313
#include <stdbool.h>
1414
#include "pycore_structs.h" // _Py_BackoffCounter
15-
#include "pycore_tstate.h" // _PyPolicy
15+
#include "pycore_interp_structs.h" // _PyOptimizationConfig
1616

1717
/* 16-bit countdown counters using exponential backoff.
1818
@@ -128,11 +128,11 @@ trigger_backoff_counter(void)
128128
#define JUMP_BACKWARD_INITIAL_VALUE 4000
129129
#define JUMP_BACKWARD_INITIAL_BACKOFF 6
130130
static inline _Py_BackoffCounter
131-
initial_jump_backoff_counter(_PyPolicy *policy)
131+
initial_jump_backoff_counter(_PyOptimizationConfig *opt_config)
132132
{
133133
return make_backoff_counter(
134-
policy->interp.jump_backward_initial_value,
135-
policy->interp.jump_backward_initial_backoff);
134+
opt_config->jump_backward_initial_value,
135+
opt_config->jump_backward_initial_backoff);
136136
}
137137

138138
/* Initial exit temperature.
@@ -143,11 +143,11 @@ initial_jump_backoff_counter(_PyPolicy *policy)
143143
#define SIDE_EXIT_INITIAL_BACKOFF 6
144144

145145
static inline _Py_BackoffCounter
146-
initial_temperature_backoff_counter(_PyPolicy *policy)
146+
initial_temperature_backoff_counter(_PyOptimizationConfig *opt_config)
147147
{
148148
return make_backoff_counter(
149-
policy->jit.side_exit_initial_value,
150-
policy->jit.side_exit_initial_backoff);
149+
opt_config->side_exit_initial_value,
150+
opt_config->side_exit_initial_backoff);
151151
}
152152

153153
/* Unreachable backoff counter. */

Include/internal/pycore_interp_structs.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,21 @@ typedef struct _rare_events {
398398
uint8_t func_modification;
399399
} _rare_events;
400400

401+
// Optimization configuration for the interpreter.
402+
// This groups all thresholds and optimization flags for both JIT and interpreter.
403+
typedef struct _PyOptimizationConfig {
404+
// Interpreter optimization thresholds
405+
uint16_t jump_backward_initial_value;
406+
uint16_t jump_backward_initial_backoff;
407+
408+
// JIT optimization thresholds
409+
uint16_t side_exit_initial_value;
410+
uint16_t side_exit_initial_backoff;
411+
412+
// Optimization flags
413+
bool specialization_enabled;
414+
} _PyOptimizationConfig;
415+
401416
struct
402417
Bigint {
403418
struct Bigint *next;
@@ -945,6 +960,9 @@ struct _is {
945960
PyObject *common_consts[NUM_COMMON_CONSTANTS];
946961
bool jit;
947962
bool compiling;
963+
964+
// Optimization configuration (thresholds and flags for JIT and interpreter)
965+
_PyOptimizationConfig opt_config;
948966
struct _PyExecutorObject *executor_list_head;
949967
struct _PyExecutorObject *executor_deletion_list_head;
950968
struct _PyExecutorObject *cold_executor;

Include/internal/pycore_tstate.h

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -64,21 +64,6 @@ typedef struct _PyJitTracerState {
6464

6565
#endif
6666

67-
typedef struct _PyJitPolicy {
68-
uint16_t side_exit_initial_value;
69-
uint16_t side_exit_initial_backoff;
70-
} _PyJitPolicy;
71-
72-
typedef struct _PyInterpreterPolicy {
73-
uint16_t jump_backward_initial_value;
74-
uint16_t jump_backward_initial_backoff;
75-
} _PyInterpreterPolicy;
76-
77-
typedef struct _PyPolicy {
78-
_PyJitPolicy jit;
79-
_PyInterpreterPolicy interp;
80-
} _PyPolicy;
81-
8267
// Every PyThreadState is actually allocated as a _PyThreadStateImpl. The
8368
// PyThreadState fields are exposed as part of the C API, although most fields
8469
// are intended to be private. The _PyThreadStateImpl fields not exposed.
@@ -157,7 +142,6 @@ typedef struct _PyThreadStateImpl {
157142
#if _Py_TIER2
158143
_PyJitTracerState *jit_tracer_state;
159144
#endif
160-
_PyPolicy policy;
161145
} _PyThreadStateImpl;
162146

163147
#ifdef __cplusplus

Lib/test/test_capi/test_opt.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3789,7 +3789,7 @@ def __next__(self):
37893789
pass
37903790
37913791
f1()
3792-
"""), PYTHON_JIT="1", PYTHON_JIT_JUMP_BACKWARD_INITIAL_VALUE="64")
3792+
"""), PYTHON_JIT="1", PYTHON_JIT_STRESS="1")
37933793
self.assertEqual(result[0].rc, 0, result)
37943794

37953795
def global_identity(x):

Objects/codeobject.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -580,9 +580,10 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
580580
}
581581
co->_co_firsttraceable = entry_point;
582582
#ifdef Py_GIL_DISABLED
583-
_PyCode_Quicken(_PyCode_CODE(co), Py_SIZE(co), interp->config.tlbc_enabled);
583+
int enable_counters = interp->config.tlbc_enabled && interp->opt_config.specialization_enabled;
584+
_PyCode_Quicken(_PyCode_CODE(co), Py_SIZE(co), enable_counters);
584585
#else
585-
_PyCode_Quicken(_PyCode_CODE(co), Py_SIZE(co), 1);
586+
_PyCode_Quicken(_PyCode_CODE(co), Py_SIZE(co), interp->opt_config.specialization_enabled);
586587
#endif
587588
notify_code_watchers(PY_CODE_EVENT_CREATE, co);
588589
return 0;
@@ -3369,13 +3370,13 @@ deopt_code_unit(PyCodeObject *code, int i)
33693370
}
33703371

33713372
static void
3372-
copy_code(_Py_CODEUNIT *dst, PyCodeObject *co)
3373+
copy_code(PyInterpreterState *interp, _Py_CODEUNIT *dst, PyCodeObject *co)
33733374
{
33743375
int code_len = (int) Py_SIZE(co);
33753376
for (int i = 0; i < code_len; i += _PyInstruction_GetLength(co, i)) {
33763377
dst[i] = deopt_code_unit(co, i);
33773378
}
3378-
_PyCode_Quicken(dst, code_len, 1);
3379+
_PyCode_Quicken(dst, code_len, interp->opt_config.specialization_enabled);
33793380
}
33803381

33813382
static Py_ssize_t
@@ -3391,7 +3392,7 @@ get_pow2_greater(Py_ssize_t initial, Py_ssize_t limit)
33913392
}
33923393

33933394
static _Py_CODEUNIT *
3394-
create_tlbc_lock_held(PyCodeObject *co, Py_ssize_t idx)
3395+
create_tlbc_lock_held(PyInterpreterState *interp, PyCodeObject *co, Py_ssize_t idx)
33953396
{
33963397
_PyCodeArray *tlbc = co->co_tlbc;
33973398
if (idx >= tlbc->size) {
@@ -3414,7 +3415,7 @@ create_tlbc_lock_held(PyCodeObject *co, Py_ssize_t idx)
34143415
PyErr_NoMemory();
34153416
return NULL;
34163417
}
3417-
copy_code((_Py_CODEUNIT *) bc, co);
3418+
copy_code(interp, (_Py_CODEUNIT *) bc, co);
34183419
assert(tlbc->entries[idx] == NULL);
34193420
tlbc->entries[idx] = bc;
34203421
return (_Py_CODEUNIT *) bc;
@@ -3429,7 +3430,8 @@ get_tlbc_lock_held(PyCodeObject *co)
34293430
if (idx < tlbc->size && tlbc->entries[idx] != NULL) {
34303431
return (_Py_CODEUNIT *)tlbc->entries[idx];
34313432
}
3432-
return create_tlbc_lock_held(co, idx);
3433+
PyInterpreterState *interp = tstate->base.interp;
3434+
return create_tlbc_lock_held(interp, co, idx);
34333435
}
34343436

34353437
_Py_CODEUNIT *

Python/optimizer.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,7 +1105,7 @@ _PyJit_FinalizeTracing(PyThreadState *tstate, int err)
11051105
tracer->initial_state.jump_backward_instr[1].counter = restart_backoff_counter(counter);
11061106
}
11071107
else {
1108-
tracer->initial_state.jump_backward_instr[1].counter = initial_jump_backoff_counter(&_tstate->policy);
1108+
tracer->initial_state.jump_backward_instr[1].counter = initial_jump_backoff_counter(&tstate->interp->opt_config);
11091109
}
11101110
}
11111111
else if (tracer->initial_state.executor->vm_data.valid) {
@@ -1115,7 +1115,7 @@ _PyJit_FinalizeTracing(PyThreadState *tstate, int err)
11151115
exit->temperature = restart_backoff_counter(exit->temperature);
11161116
}
11171117
else {
1118-
exit->temperature = initial_temperature_backoff_counter(&_tstate->policy);
1118+
exit->temperature = initial_temperature_backoff_counter(&tstate->interp->opt_config);
11191119
}
11201120
}
11211121
Py_CLEAR(tracer->initial_state.code);
@@ -1384,9 +1384,10 @@ make_executor_from_uops(_PyThreadStateImpl *tstate, _PyUOpInstruction *buffer, i
13841384
_PyExecutorObject *cold = _PyExecutor_GetColdExecutor();
13851385
_PyExecutorObject *cold_dynamic = _PyExecutor_GetColdDynamicExecutor();
13861386
cold->vm_data.chain_depth = chain_depth;
1387+
PyInterpreterState *interp = tstate->base.interp;
13871388
for (int i = 0; i < exit_count; i++) {
13881389
executor->exits[i].index = i;
1389-
executor->exits[i].temperature = initial_temperature_backoff_counter(&tstate->policy);
1390+
executor->exits[i].temperature = initial_temperature_backoff_counter(&interp->opt_config);
13901391
}
13911392
int next_exit = exit_count-1;
13921393
_PyUOpInstruction *dest = (_PyUOpInstruction *)&executor->trace[length];

Python/pystate.c

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,28 @@ _Py_LazyJitShim(
514514
main interpreter. We fix those fields here, in addition
515515
to the other dynamically initialized fields.
516516
*/
517+
518+
static inline bool
519+
is_env_enabled(const char *env_name)
520+
{
521+
char *env = Py_GETENV(env_name);
522+
return env && *env != '\0' && *env != '0';
523+
}
524+
525+
static inline void
526+
init_policy(uint16_t *target, const char *env_name, uint16_t default_value,
527+
long min_value, long max_value)
528+
{
529+
*target = default_value;
530+
char *env = Py_GETENV(env_name);
531+
if (env && *env != '\0') {
532+
long value = atol(env);
533+
if (value >= min_value && value <= max_value) {
534+
*target = (uint16_t)value;
535+
}
536+
}
537+
}
538+
517539
static PyStatus
518540
init_interpreter(PyInterpreterState *interp,
519541
_PyRuntimeState *runtime, int64_t id,
@@ -572,6 +594,31 @@ init_interpreter(PyInterpreterState *interp,
572594
interp->executor_list_head = NULL;
573595
interp->executor_deletion_list_head = NULL;
574596
interp->executor_creation_counter = JIT_CLEANUP_THRESHOLD;
597+
598+
// Initialize optimization configuration from environment variables
599+
// PYTHON_JIT_STRESS sets aggressive defaults for testing, but can be overridden
600+
uint16_t jump_default = JUMP_BACKWARD_INITIAL_VALUE;
601+
uint16_t side_exit_default = SIDE_EXIT_INITIAL_VALUE;
602+
603+
if (is_env_enabled("PYTHON_JIT_STRESS")) {
604+
jump_default = 63;
605+
side_exit_default = 63;
606+
}
607+
608+
init_policy(&interp->opt_config.jump_backward_initial_value,
609+
"PYTHON_JIT_JUMP_BACKWARD_INITIAL_VALUE",
610+
jump_default, 1, MAX_VALUE);
611+
init_policy(&interp->opt_config.jump_backward_initial_backoff,
612+
"PYTHON_JIT_JUMP_BACKWARD_INITIAL_BACKOFF",
613+
JUMP_BACKWARD_INITIAL_BACKOFF, 0, MAX_BACKOFF);
614+
init_policy(&interp->opt_config.side_exit_initial_value,
615+
"PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE",
616+
side_exit_default, 1, MAX_VALUE);
617+
init_policy(&interp->opt_config.side_exit_initial_backoff,
618+
"PYTHON_JIT_SIDE_EXIT_INITIAL_BACKOFF",
619+
SIDE_EXIT_INITIAL_BACKOFF, 0, MAX_BACKOFF);
620+
621+
interp->opt_config.specialization_enabled = !is_env_enabled("PYTHON_SPECIALIZATION_OFF");
575622
if (interp != &runtime->_main_interpreter) {
576623
/* Fix the self-referential, statically initialized fields. */
577624
interp->dtoa = (struct _dtoa_state)_dtoa_state_INIT(interp);
@@ -1439,20 +1486,6 @@ decref_threadstate(_PyThreadStateImpl *tstate)
14391486
}
14401487
}
14411488

1442-
static inline void
1443-
init_policy(uint16_t *target, const char *env_name, uint16_t default_value,
1444-
long min_value, long max_value)
1445-
{
1446-
*target = default_value;
1447-
char *env = Py_GETENV(env_name);
1448-
if (env && *env != '\0') {
1449-
long value = atol(env);
1450-
if (value >= min_value && value <= max_value) {
1451-
*target = (uint16_t)value;
1452-
}
1453-
}
1454-
}
1455-
14561489
/* Get the thread state to a minimal consistent state.
14571490
Further init happens in pylifecycle.c before it can be used.
14581491
All fields not initialized here are expected to be zeroed out,
@@ -1538,21 +1571,8 @@ init_threadstate(_PyThreadStateImpl *_tstate,
15381571

15391572
_tstate->asyncio_running_loop = NULL;
15401573
_tstate->asyncio_running_task = NULL;
1541-
// Initialize interpreter policy from environment variables
1542-
init_policy(&_tstate->policy.interp.jump_backward_initial_value,
1543-
"PYTHON_JIT_JUMP_BACKWARD_INITIAL_VALUE",
1544-
JUMP_BACKWARD_INITIAL_VALUE, 1, MAX_VALUE);
1545-
init_policy(&_tstate->policy.interp.jump_backward_initial_backoff,
1546-
"PYTHON_JIT_JUMP_BACKWARD_INITIAL_BACKOFF",
1547-
JUMP_BACKWARD_INITIAL_BACKOFF, 0, MAX_BACKOFF);
1574+
15481575
#ifdef _Py_TIER2
1549-
// Initialize JIT policy from environment variables
1550-
init_policy(&_tstate->policy.jit.side_exit_initial_value,
1551-
"PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE",
1552-
SIDE_EXIT_INITIAL_VALUE, 1, MAX_VALUE);
1553-
init_policy(&_tstate->policy.jit.side_exit_initial_backoff,
1554-
"PYTHON_JIT_SIDE_EXIT_INITIAL_BACKOFF",
1555-
SIDE_EXIT_INITIAL_BACKOFF, 0, MAX_BACKOFF);
15561576
_tstate->jit_tracer_state = NULL;
15571577
#endif
15581578
tstate->delete_later = NULL;

Python/specialize.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters
4848
_Py_BackoffCounter jump_counter, adaptive_counter;
4949
if (enable_counters) {
5050
PyThreadState *tstate = _PyThreadState_GET();
51-
_PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate;
52-
jump_counter = initial_jump_backoff_counter(&tstate_impl->policy);
51+
PyInterpreterState *interp = tstate->interp;
52+
jump_counter = initial_jump_backoff_counter(&interp->opt_config);
5353
adaptive_counter = adaptive_counter_warmup();
5454
}
5555
else {

0 commit comments

Comments
 (0)