Skip to content

Commit 12283f6

Browse files
authored
gh-141504: Factor out tracing and optimization heuristics into a single object (gh-143381)
1 parent 3c56f9e commit 12283f6

File tree

7 files changed

+67
-14
lines changed

7 files changed

+67
-14
lines changed

Include/internal/pycore_backoff.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +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
1516

1617
/* 16-bit countdown counters using exponential backoff.
1718
@@ -127,10 +128,11 @@ trigger_backoff_counter(void)
127128
#define JUMP_BACKWARD_INITIAL_VALUE 4000
128129
#define JUMP_BACKWARD_INITIAL_BACKOFF 6
129130
static inline _Py_BackoffCounter
130-
initial_jump_backoff_counter(void)
131+
initial_jump_backoff_counter(_PyPolicy *policy)
131132
{
132-
return make_backoff_counter(JUMP_BACKWARD_INITIAL_VALUE,
133-
JUMP_BACKWARD_INITIAL_BACKOFF);
133+
return make_backoff_counter(
134+
policy->interp.jump_backward_initial_value,
135+
policy->interp.jump_backward_initial_backoff);
134136
}
135137

136138
/* Initial exit temperature.
@@ -141,10 +143,11 @@ initial_jump_backoff_counter(void)
141143
#define SIDE_EXIT_INITIAL_BACKOFF 6
142144

143145
static inline _Py_BackoffCounter
144-
initial_temperature_backoff_counter(void)
146+
initial_temperature_backoff_counter(_PyPolicy *policy)
145147
{
146-
return make_backoff_counter(SIDE_EXIT_INITIAL_VALUE,
147-
SIDE_EXIT_INITIAL_BACKOFF);
148+
return make_backoff_counter(
149+
policy->jit.side_exit_initial_value,
150+
policy->jit.side_exit_initial_backoff);
148151
}
149152

150153
/* Unreachable backoff counter. */

Include/internal/pycore_tstate.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,24 @@ typedef struct _PyJitTracerState {
5252
_PyJitTracerInitialState initial_state;
5353
_PyJitTracerPreviousState prev_state;
5454
} _PyJitTracerState;
55+
5556
#endif
5657

58+
typedef struct _PyJitPolicy {
59+
uint16_t side_exit_initial_value;
60+
uint16_t side_exit_initial_backoff;
61+
} _PyJitPolicy;
62+
63+
typedef struct _PyInterpreterPolicy {
64+
uint16_t jump_backward_initial_value;
65+
uint16_t jump_backward_initial_backoff;
66+
} _PyInterpreterPolicy;
67+
68+
typedef struct _PyPolicy {
69+
_PyJitPolicy jit;
70+
_PyInterpreterPolicy interp;
71+
} _PyPolicy;
72+
5773
// Every PyThreadState is actually allocated as a _PyThreadStateImpl. The
5874
// PyThreadState fields are exposed as part of the C API, although most fields
5975
// are intended to be private. The _PyThreadStateImpl fields not exposed.
@@ -132,6 +148,7 @@ typedef struct _PyThreadStateImpl {
132148
#if _Py_TIER2
133149
_PyJitTracerState jit_tracer_state;
134150
#endif
151+
_PyPolicy policy;
135152
} _PyThreadStateImpl;
136153

137154
#ifdef __cplusplus
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Factor out tracing and optimization heuristics into a single object.
2+
Patch by Donghee Na.

Python/ceval.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,7 +1473,7 @@ stop_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame)
14731473
_tstate->jit_tracer_state.initial_state.jump_backward_instr[1].counter = restart_backoff_counter(counter);
14741474
}
14751475
else {
1476-
_tstate->jit_tracer_state.initial_state.jump_backward_instr[1].counter = initial_jump_backoff_counter();
1476+
_tstate->jit_tracer_state.initial_state.jump_backward_instr[1].counter = initial_jump_backoff_counter(&_tstate->policy);
14771477
}
14781478
}
14791479
else {
@@ -1483,7 +1483,7 @@ stop_tracing_and_jit(PyThreadState *tstate, _PyInterpreterFrame *frame)
14831483
exit->temperature = restart_backoff_counter(exit->temperature);
14841484
}
14851485
else {
1486-
exit->temperature = initial_temperature_backoff_counter();
1486+
exit->temperature = initial_temperature_backoff_counter(&_tstate->policy);
14871487
}
14881488
}
14891489
_PyJit_FinalizeTracing(tstate);

Python/optimizer.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ insert_executor(PyCodeObject *code, _Py_CODEUNIT *instr, int index, _PyExecutorO
113113
}
114114

115115
static _PyExecutorObject *
116-
make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFilter *dependencies, int chain_depth);
116+
make_executor_from_uops(_PyThreadStateImpl *tstate, _PyUOpInstruction *buffer, int length, const _PyBloomFilter *dependencies);
117117

118118
static int
119119
uop_optimize(_PyInterpreterFrame *frame, PyThreadState *tstate,
@@ -1328,7 +1328,7 @@ sanity_check(_PyExecutorObject *executor)
13281328
* and not a NOP.
13291329
*/
13301330
static _PyExecutorObject *
1331-
make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFilter *dependencies, int chain_depth)
1331+
make_executor_from_uops(_PyThreadStateImpl *tstate, _PyUOpInstruction *buffer, int length, const _PyBloomFilter *dependencies)
13321332
{
13331333
int exit_count = count_exits(buffer, length);
13341334
_PyExecutorObject *executor = allocate_executor(exit_count, length);
@@ -1337,12 +1337,13 @@ make_executor_from_uops(_PyUOpInstruction *buffer, int length, const _PyBloomFil
13371337
}
13381338

13391339
/* Initialize exits */
1340+
int chain_depth = tstate->jit_tracer_state.initial_state.chain_depth;
13401341
_PyExecutorObject *cold = _PyExecutor_GetColdExecutor();
13411342
_PyExecutorObject *cold_dynamic = _PyExecutor_GetColdDynamicExecutor();
13421343
cold->vm_data.chain_depth = chain_depth;
13431344
for (int i = 0; i < exit_count; i++) {
13441345
executor->exits[i].index = i;
1345-
executor->exits[i].temperature = initial_temperature_backoff_counter();
1346+
executor->exits[i].temperature = initial_temperature_backoff_counter(&tstate->policy);
13461347
}
13471348
int next_exit = exit_count-1;
13481349
_PyUOpInstruction *dest = (_PyUOpInstruction *)&executor->trace[length];
@@ -1510,7 +1511,7 @@ uop_optimize(
15101511
length = prepare_for_execution(buffer, length);
15111512
assert(length <= UOP_MAX_TRACE_LENGTH);
15121513
_PyExecutorObject *executor = make_executor_from_uops(
1513-
buffer, length, dependencies, _tstate->jit_tracer_state.initial_state.chain_depth);
1514+
_tstate, buffer, length, dependencies);
15141515
if (executor == NULL) {
15151516
return -1;
15161517
}

Python/pystate.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "Python.h"
55
#include "pycore_abstract.h" // _PyIndex_Check()
66
#include "pycore_audit.h" // _Py_AuditHookEntry
7+
#include "pycore_backoff.h" // JUMP_BACKWARD_INITIAL_VALUE, SIDE_EXIT_INITIAL_VALUE
78
#include "pycore_ceval.h" // _PyEval_AcquireLock()
89
#include "pycore_codecs.h" // _PyCodec_Fini()
910
#include "pycore_critical_section.h" // _PyCriticalSection_Resume()
@@ -1438,6 +1439,20 @@ decref_threadstate(_PyThreadStateImpl *tstate)
14381439
}
14391440
}
14401441

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

15241539
_tstate->asyncio_running_loop = NULL;
15251540
_tstate->asyncio_running_task = NULL;
1526-
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);
15271548
#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);
15281556
_tstate->jit_tracer_state.code_buffer = NULL;
15291557
#endif
15301558
tstate->delete_later = NULL;

Python/specialize.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters
4747
#if ENABLE_SPECIALIZATION_FT
4848
_Py_BackoffCounter jump_counter, adaptive_counter;
4949
if (enable_counters) {
50-
jump_counter = initial_jump_backoff_counter();
50+
PyThreadState *tstate = _PyThreadState_GET();
51+
_PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate;
52+
jump_counter = initial_jump_backoff_counter(&tstate_impl->policy);
5153
adaptive_counter = adaptive_counter_warmup();
5254
}
5355
else {

0 commit comments

Comments
 (0)