Skip to content

Commit 3a2ccda

Browse files
committed
Track the current executor on the thread-state, not the previous one. Batch executors for deallocation to avoid having to constantly incref executors; this is an ad-hoc form of deferred reference counting.
1 parent a4be3bc commit 3a2ccda

File tree

12 files changed

+143
-49
lines changed

12 files changed

+143
-49
lines changed

Include/cpython/pystate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ struct _ts {
194194
/* The thread's exception stack entry. (Always the last entry.) */
195195
_PyErr_StackItem exc_state;
196196

197-
PyObject *previous_executor;
197+
PyObject *current_executor;
198198

199199
uint64_t dict_global_version;
200200

Include/internal/pycore_interp_structs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,8 @@ struct _is {
923923
PyObject *common_consts[NUM_COMMON_CONSTANTS];
924924
bool jit;
925925
struct _PyExecutorObject *executor_list_head;
926+
struct _PyExecutorObject *executor_deletion_list_head;
927+
int executor_deletion_list_remaining_capacity;
926928
size_t trace_run_counter;
927929
_rare_events rare_events;
928930
PyDict_WatchCallback builtins_dict_watcher;

Include/internal/pycore_opcode_metadata.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_optimizer.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ typedef struct _PyExecutorObject {
8484
_PyExitData exits[1];
8585
} _PyExecutorObject;
8686

87+
/* If pending deletion list gets large enough, then scan,
88+
* and free any executors that aren't executing
89+
* i.e. any that aren't a thread's current_executor. */
90+
#define EXECUTOR_DELETE_LIST_MAX 100
8791

8892
// Export for '_opcode' shared extension (JIT compiler).
8993
PyAPI_FUNC(_PyExecutorObject*) _Py_GetExecutor(PyCodeObject *code, int offset);
@@ -304,6 +308,9 @@ static inline int is_terminator(const _PyUOpInstruction *uop)
304308
}
305309

306310
PyAPI_FUNC(int) _PyDumpExecutors(FILE *out);
311+
#ifdef _Py_TIER2
312+
extern void _Py_ClearExecutorDeletionList(PyInterpreterState *interp);
313+
#endif
307314

308315
#ifdef __cplusplus
309316
}

Include/internal/pycore_uop_metadata.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/bytecodes.c

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,6 +1169,12 @@ dummy_func(
11691169
tstate->current_frame = frame->previous;
11701170
assert(!_PyErr_Occurred(tstate));
11711171
PyObject *result = PyStackRef_AsPyObjectSteal(retval);
1172+
_PyStackRef executor = entry.frame.localsplus[0];
1173+
assert(tstate->current_executor == NULL);
1174+
if (!PyStackRef_IsNull(executor)) {
1175+
tstate->current_executor = PyStackRef_AsPyObjectBorrow(executor);
1176+
PyStackRef_CLOSE(executor);
1177+
}
11721178
LLTRACE_RESUME_FRAME();
11731179
return result;
11741180
}
@@ -2912,8 +2918,8 @@ dummy_func(
29122918
}
29132919
else {
29142920
this_instr[1].counter = initial_jump_backoff_counter();
2915-
assert(tstate->previous_executor == NULL);
2916-
tstate->previous_executor = Py_None;
2921+
assert(tstate->current_executor == NULL);
2922+
tstate->current_executor = (PyObject *)executor;
29172923
GOTO_TIER_TWO(executor);
29182924
}
29192925
}
@@ -2965,7 +2971,7 @@ dummy_func(
29652971
assert(executor->vm_data.index == INSTR_OFFSET() - 1);
29662972
assert(executor->vm_data.code == code);
29672973
assert(executor->vm_data.valid);
2968-
assert(tstate->previous_executor == NULL);
2974+
assert(tstate->current_executor == NULL);
29692975
/* If the eval breaker is set then stay in tier 1.
29702976
* This avoids any potentially infinite loops
29712977
* involving _RESUME_CHECK */
@@ -2978,8 +2984,7 @@ dummy_func(
29782984
}
29792985
DISPATCH_GOTO();
29802986
}
2981-
tstate->previous_executor = Py_None;
2982-
Py_INCREF(executor);
2987+
tstate->current_executor = (PyObject *)executor;
29832988
GOTO_TIER_TWO(executor);
29842989
#else
29852990
Py_FatalError("ENTER_EXECUTOR is not supported in this build");
@@ -5247,7 +5252,7 @@ dummy_func(
52475252
exit->temperature = initial_temperature_backoff_counter();
52485253
Py_CLEAR(exit->executor);
52495254
}
5250-
tstate->previous_executor = (PyObject *)current_executor;
5255+
tstate->current_executor = NULL;
52515256
if (exit->executor == NULL) {
52525257
_Py_BackoffCounter temperature = exit->temperature;
52535258
if (!backoff_counter_triggers(temperature)) {
@@ -5270,7 +5275,7 @@ dummy_func(
52705275
}
52715276
exit->executor = executor;
52725277
}
5273-
Py_INCREF(exit->executor);
5278+
tstate->current_executor = (PyObject *)exit->executor;
52745279
GOTO_TIER_TWO(exit->executor);
52755280
}
52765281

@@ -5309,7 +5314,6 @@ dummy_func(
53095314
}
53105315

53115316
tier2 op(_START_EXECUTOR, (executor/4 --)) {
5312-
Py_CLEAR(tstate->previous_executor);
53135317
#ifndef _Py_JIT
53145318
current_executor = (_PyExecutorObject*)executor;
53155319
#endif
@@ -5330,12 +5334,12 @@ dummy_func(
53305334
}
53315335

53325336
tier2 op(_DEOPT, (--)) {
5333-
tstate->previous_executor = (PyObject *)current_executor;
5337+
tstate->current_executor = NULL;
53345338
GOTO_TIER_ONE(_PyFrame_GetBytecode(frame) + CURRENT_TARGET());
53355339
}
53365340

53375341
tier2 op(_ERROR_POP_N, (target/2 --)) {
5338-
tstate->previous_executor = (PyObject *)current_executor;
5342+
tstate->current_executor = NULL;
53395343
assert(oparg == 0);
53405344
frame->instr_ptr = _PyFrame_GetBytecode(frame) + target;
53415345
SYNC_SP();

Python/ceval.c

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,11 @@ _PyObjectArray_Free(PyObject **array, PyObject **scratch)
990990
#define DONT_SLP_VECTORIZE
991991
#endif
992992

993+
typedef struct {
994+
_PyInterpreterFrame frame;
995+
_PyStackRef stack[1];
996+
} _PyEntryFrame;
997+
993998
PyObject* _Py_HOT_FUNCTION DONT_SLP_VECTORIZE
994999
_PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag)
9951000
{
@@ -1009,7 +1014,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
10091014
int oparg; /* Current opcode argument, if any */
10101015
assert(tstate->current_frame == NULL || tstate->current_frame->stackpointer != NULL);
10111016
#endif
1012-
_PyInterpreterFrame entry_frame;
1017+
_PyEntryFrame entry;
10131018

10141019
if (_Py_EnterRecursiveCallTstate(tstate, "")) {
10151020
assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
@@ -1021,31 +1026,39 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
10211026
* These are cached values from the frame and code object. */
10221027
_Py_CODEUNIT *next_instr;
10231028
_PyStackRef *stack_pointer;
1024-
entry_frame.localsplus[0] = PyStackRef_NULL;
1029+
entry.stack[0] = PyStackRef_NULL;
10251030
#ifdef Py_STACKREF_DEBUG
1026-
entry_frame.f_funcobj = PyStackRef_None;
1031+
entry.frame.f_funcobj = PyStackRef_None;
10271032
#elif defined(Py_DEBUG)
10281033
/* Set these to invalid but identifiable values for debugging. */
1029-
entry_frame.f_funcobj = (_PyStackRef){.bits = 0xaaa0};
1030-
entry_frame.f_locals = (PyObject*)0xaaa1;
1031-
entry_frame.frame_obj = (PyFrameObject*)0xaaa2;
1032-
entry_frame.f_globals = (PyObject*)0xaaa3;
1033-
entry_frame.f_builtins = (PyObject*)0xaaa4;
1034+
entry.frame.f_funcobj = (_PyStackRef){.bits = 0xaaa0};
1035+
entry.frame.f_locals = (PyObject*)0xaaa1;
1036+
entry.frame.frame_obj = (PyFrameObject*)0xaaa2;
1037+
entry.frame.f_globals = (PyObject*)0xaaa3;
1038+
entry.frame.f_builtins = (PyObject*)0xaaa4;
10341039
#endif
1035-
entry_frame.f_executable = PyStackRef_None;
1036-
entry_frame.instr_ptr = (_Py_CODEUNIT *)_Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS + 1;
1037-
entry_frame.stackpointer = entry_frame.localsplus;
1038-
entry_frame.owner = FRAME_OWNED_BY_INTERPRETER;
1039-
entry_frame.visited = 0;
1040-
entry_frame.return_offset = 0;
1040+
entry.frame.f_executable = PyStackRef_None;
1041+
entry.frame.instr_ptr = (_Py_CODEUNIT *)_Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS + 1;
1042+
entry.frame.stackpointer = entry.stack;
1043+
entry.frame.owner = FRAME_OWNED_BY_INTERPRETER;
1044+
entry.frame.visited = 0;
1045+
entry.frame.return_offset = 0;
10411046
#ifdef Py_DEBUG
1042-
entry_frame.lltrace = 0;
1047+
entry.frame.lltrace = 0;
10431048
#endif
10441049
/* Push frame */
1045-
entry_frame.previous = tstate->current_frame;
1046-
frame->previous = &entry_frame;
1050+
entry.frame.previous = tstate->current_frame;
1051+
frame->previous = &entry.frame;
10471052
tstate->current_frame = frame;
10481053

1054+
if (tstate->current_executor != NULL) {
1055+
entry.frame.localsplus[0] = PyStackRef_FromPyObjectNew(tstate->current_executor);
1056+
tstate->current_executor = NULL;
1057+
}
1058+
else {
1059+
entry.frame.localsplus[0] = PyStackRef_NULL;
1060+
}
1061+
10491062
/* support for generator.throw() */
10501063
if (throwflag) {
10511064
if (_Py_EnterRecursivePy(tstate)) {

Python/ceval_macros.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,12 +359,13 @@ _PyFrame_SetStackPointer(frame, stack_pointer)
359359
do { \
360360
OPT_STAT_INC(traces_executed); \
361361
_PyExecutorObject *_executor = (EXECUTOR); \
362+
assert(tstate->current_executor == _executor); \
362363
jit_func jitted = _executor->jit_code; \
363364
/* Keep the shim frame alive via the executor: */ \
364365
Py_INCREF(_executor); \
365366
next_instr = jitted(frame, stack_pointer, tstate); \
366367
Py_DECREF(_executor); \
367-
Py_CLEAR(tstate->previous_executor); \
368+
tstate->current_executor = NULL; \
368369
frame = tstate->current_frame; \
369370
stack_pointer = _PyFrame_GetStackPointer(frame); \
370371
if (next_instr == NULL) { \
@@ -387,9 +388,9 @@ do { \
387388
do \
388389
{ \
389390
next_instr = (TARGET); \
391+
assert(tstate->current_executor == NULL); \
390392
OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); \
391393
_PyFrame_SetStackPointer(frame, stack_pointer); \
392-
Py_CLEAR(tstate->previous_executor); \
393394
stack_pointer = _PyFrame_GetStackPointer(frame); \
394395
if (next_instr == NULL) \
395396
{ \

Python/executor_cases.c.h

Lines changed: 4 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

Lines changed: 15 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)