Skip to content

Commit fc84a62

Browse files
committed
gh-141504: Refactor policy object into a single opt_config
1 parent 78e868f commit fc84a62

File tree

8 files changed

+77
-59
lines changed

8 files changed

+77
-59
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_tstate.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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,9 @@ struct _is {
945945
PyObject *common_consts[NUM_COMMON_CONSTANTS];
946946
bool jit;
947947
bool compiling;
948+
949+
// Optimization configuration (thresholds and flags for JIT and interpreter)
950+
_PyOptimizationConfig opt_config;
948951
struct _PyExecutorObject *executor_list_head;
949952
struct _PyExecutorObject *executor_deletion_list_head;
950953
struct _PyExecutorObject *cold_executor;

Include/internal/pycore_tstate.h

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,20 +62,20 @@ typedef struct _PyJitTracerState {
6262

6363
#endif
6464

65-
typedef struct _PyJitPolicy {
66-
uint16_t side_exit_initial_value;
67-
uint16_t side_exit_initial_backoff;
68-
} _PyJitPolicy;
69-
70-
typedef struct _PyInterpreterPolicy {
65+
// Optimization configuration for the interpreter.
66+
// This groups all thresholds and optimization flags for both JIT and interpreter.
67+
typedef struct _PyOptimizationConfig {
68+
// Interpreter optimization thresholds
7169
uint16_t jump_backward_initial_value;
7270
uint16_t jump_backward_initial_backoff;
73-
} _PyInterpreterPolicy;
7471

75-
typedef struct _PyPolicy {
76-
_PyJitPolicy jit;
77-
_PyInterpreterPolicy interp;
78-
} _PyPolicy;
72+
// JIT optimization thresholds
73+
uint16_t side_exit_initial_value;
74+
uint16_t side_exit_initial_backoff;
75+
76+
// Optimization flags
77+
bool specialization_enabled;
78+
} _PyOptimizationConfig;
7979

8080
// Every PyThreadState is actually allocated as a _PyThreadStateImpl. The
8181
// PyThreadState fields are exposed as part of the C API, although most fields
@@ -155,7 +155,6 @@ typedef struct _PyThreadStateImpl {
155155
#if _Py_TIER2
156156
_PyJitTracerState *jit_tracer_state;
157157
#endif
158-
_PyPolicy policy;
159158
} _PyThreadStateImpl;
160159

161160
#ifdef __cplusplus

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/ceval.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,7 +1472,7 @@ stop_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame)
14721472
tracer->initial_state.jump_backward_instr[1].counter = restart_backoff_counter(counter);
14731473
}
14741474
else {
1475-
tracer->initial_state.jump_backward_instr[1].counter = initial_jump_backoff_counter(&_tstate->policy);
1475+
tracer->initial_state.jump_backward_instr[1].counter = initial_jump_backoff_counter(&tstate->interp->opt_config);
14761476
}
14771477
}
14781478
else {
@@ -1482,7 +1482,7 @@ stop_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame)
14821482
exit->temperature = restart_backoff_counter(exit->temperature);
14831483
}
14841484
else {
1485-
exit->temperature = initial_temperature_backoff_counter(&_tstate->policy);
1485+
exit->temperature = initial_temperature_backoff_counter(&tstate->interp->opt_config);
14861486
}
14871487
}
14881488
_PyJit_FinalizeTracing(tstate);

Python/optimizer.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1351,9 +1351,10 @@ make_executor_from_uops(_PyThreadStateImpl *tstate, _PyUOpInstruction *buffer, i
13511351
_PyExecutorObject *cold = _PyExecutor_GetColdExecutor();
13521352
_PyExecutorObject *cold_dynamic = _PyExecutor_GetColdDynamicExecutor();
13531353
cold->vm_data.chain_depth = chain_depth;
1354+
PyInterpreterState *interp = tstate->base.interp;
13541355
for (int i = 0; i < exit_count; i++) {
13551356
executor->exits[i].index = i;
1356-
executor->exits[i].temperature = initial_temperature_backoff_counter(&tstate->policy);
1357+
executor->exits[i].temperature = initial_temperature_backoff_counter(&interp->opt_config);
13571358
}
13581359
int next_exit = exit_count-1;
13591360
_PyUOpInstruction *dest = (_PyUOpInstruction *)&executor->trace[length];

Python/pystate.c

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,21 @@ _Py_LazyJitShim(
514514
main interpreter. We fix those fields here, in addition
515515
to the other dynamically initialized fields.
516516
*/
517+
518+
static inline void
519+
init_policy(uint16_t *target, const char *env_name, uint16_t default_value,
520+
long min_value, long max_value)
521+
{
522+
*target = default_value;
523+
char *env = Py_GETENV(env_name);
524+
if (env && *env != '\0') {
525+
long value = atol(env);
526+
if (value >= min_value && value <= max_value) {
527+
*target = (uint16_t)value;
528+
}
529+
}
530+
}
531+
517532
static PyStatus
518533
init_interpreter(PyInterpreterState *interp,
519534
_PyRuntimeState *runtime, int64_t id,
@@ -572,6 +587,31 @@ init_interpreter(PyInterpreterState *interp,
572587
interp->executor_list_head = NULL;
573588
interp->executor_deletion_list_head = NULL;
574589
interp->executor_creation_counter = JIT_CLEANUP_THRESHOLD;
590+
591+
// Initialize optimization configuration from environment variables
592+
init_policy(&interp->opt_config.jump_backward_initial_value,
593+
"PYTHON_JIT_JUMP_BACKWARD_INITIAL_VALUE",
594+
JUMP_BACKWARD_INITIAL_VALUE, 1, MAX_VALUE);
595+
init_policy(&interp->opt_config.jump_backward_initial_backoff,
596+
"PYTHON_JIT_JUMP_BACKWARD_INITIAL_BACKOFF",
597+
JUMP_BACKWARD_INITIAL_BACKOFF, 0, MAX_BACKOFF);
598+
#ifdef _Py_TIER2
599+
init_policy(&interp->opt_config.side_exit_initial_value,
600+
"PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE",
601+
SIDE_EXIT_INITIAL_VALUE, 1, MAX_VALUE);
602+
init_policy(&interp->opt_config.side_exit_initial_backoff,
603+
"PYTHON_JIT_SIDE_EXIT_INITIAL_BACKOFF",
604+
SIDE_EXIT_INITIAL_BACKOFF, 0, MAX_BACKOFF);
605+
#endif
606+
607+
// Check if specialization should be disabled
608+
// If PYTHON_SPECIALIZATION_OFF is set to any non-empty value, disable specialization
609+
char *spec_off_env = Py_GETENV("PYTHON_SPECIALIZATION_OFF");
610+
if (spec_off_env && *spec_off_env != '\0' && *spec_off_env != '0') {
611+
interp->opt_config.specialization_enabled = false;
612+
} else {
613+
interp->opt_config.specialization_enabled = true;
614+
}
575615
if (interp != &runtime->_main_interpreter) {
576616
/* Fix the self-referential, statically initialized fields. */
577617
interp->dtoa = (struct _dtoa_state)_dtoa_state_INIT(interp);
@@ -1439,20 +1479,6 @@ decref_threadstate(_PyThreadStateImpl *tstate)
14391479
}
14401480
}
14411481

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-
14561482
/* Get the thread state to a minimal consistent state.
14571483
Further init happens in pylifecycle.c before it can be used.
14581484
All fields not initialized here are expected to be zeroed out,
@@ -1538,21 +1564,8 @@ init_threadstate(_PyThreadStateImpl *_tstate,
15381564

15391565
_tstate->asyncio_running_loop = NULL;
15401566
_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);
1567+
15481568
#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);
15561569
_tstate->jit_tracer_state = NULL;
15571570
#endif
15581571
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)