Skip to content

Commit 9c14ed0

Browse files
gh-107674: Improve performance of sys.settrace (GH-117133)
* Check tracing in RESUME_CHECK * Only change to RESUME_CHECK if not tracing
1 parent 998c385 commit 9c14ed0

File tree

7 files changed

+63
-52
lines changed

7 files changed

+63
-52
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improved the performance of :func:`sys.settrace` significantly

Python/bytecodes.c

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -148,20 +148,18 @@ dummy_func(
148148

149149
tier1 inst(RESUME, (--)) {
150150
assert(frame == tstate->current_frame);
151-
uintptr_t global_version =
152-
_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) &
153-
~_PY_EVAL_EVENTS_MASK;
154-
PyCodeObject *code = _PyFrame_GetCode(frame);
155-
uintptr_t code_version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(code->_co_instrumentation_version);
156-
assert((code_version & 255) == 0);
157-
if (code_version != global_version) {
158-
int err = _Py_Instrument(code, tstate->interp);
159-
ERROR_IF(err, error);
160-
next_instr = this_instr;
161-
}
162-
else {
163-
if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) {
164-
CHECK_EVAL_BREAKER();
151+
if (tstate->tracing == 0) {
152+
uintptr_t global_version =
153+
_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) &
154+
~_PY_EVAL_EVENTS_MASK;
155+
PyCodeObject* code = _PyFrame_GetCode(frame);
156+
uintptr_t code_version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(code->_co_instrumentation_version);
157+
assert((code_version & 255) == 0);
158+
if (code_version != global_version) {
159+
int err = _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp);
160+
ERROR_IF(err, error);
161+
next_instr = this_instr;
162+
DISPATCH();
165163
}
166164
assert(this_instr->op.code == RESUME ||
167165
this_instr->op.code == RESUME_CHECK ||
@@ -173,6 +171,9 @@ dummy_func(
173171
#endif /* ENABLE_SPECIALIZATION */
174172
}
175173
}
174+
if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) {
175+
CHECK_EVAL_BREAKER();
176+
}
176177
}
177178

178179
inst(RESUME_CHECK, (--)) {
@@ -189,7 +190,7 @@ dummy_func(
189190
inst(INSTRUMENTED_RESUME, (--)) {
190191
uintptr_t global_version = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & ~_PY_EVAL_EVENTS_MASK;
191192
uintptr_t code_version = FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version);
192-
if (code_version != global_version) {
193+
if (code_version != global_version && tstate->tracing == 0) {
193194
if (_Py_Instrument(_PyFrame_GetCode(frame), tstate->interp)) {
194195
ERROR_NO_POP();
195196
}
@@ -4284,7 +4285,7 @@ dummy_func(
42844285
#endif
42854286
uintptr_t eval_breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker);
42864287
DEOPT_IF(eval_breaker & _PY_EVAL_EVENTS_MASK);
4287-
assert(eval_breaker == FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version));
4288+
assert(tstate->tracing || eval_breaker == FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version));
42884289
}
42894290

42904291
// END BYTECODES //

Python/ceval.c

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -808,17 +808,23 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
808808
{
809809
_Py_CODEUNIT *prev = frame->instr_ptr;
810810
_Py_CODEUNIT *here = frame->instr_ptr = next_instr;
811-
_PyFrame_SetStackPointer(frame, stack_pointer);
812-
int original_opcode = _Py_call_instrumentation_line(
813-
tstate, frame, here, prev);
814-
stack_pointer = _PyFrame_GetStackPointer(frame);
815-
if (original_opcode < 0) {
816-
next_instr = here+1;
817-
goto error;
818-
}
819-
next_instr = frame->instr_ptr;
820-
if (next_instr != here) {
821-
DISPATCH();
811+
int original_opcode = 0;
812+
if (tstate->tracing) {
813+
PyCodeObject *code = _PyFrame_GetCode(frame);
814+
original_opcode = code->_co_monitoring->lines[(int)(here - _PyCode_CODE(code))].original_opcode;
815+
} else {
816+
_PyFrame_SetStackPointer(frame, stack_pointer);
817+
original_opcode = _Py_call_instrumentation_line(
818+
tstate, frame, here, prev);
819+
stack_pointer = _PyFrame_GetStackPointer(frame);
820+
if (original_opcode < 0) {
821+
next_instr = here+1;
822+
goto error;
823+
}
824+
next_instr = frame->instr_ptr;
825+
if (next_instr != here) {
826+
DISPATCH();
827+
}
822828
}
823829
if (_PyOpcode_Caches[original_opcode]) {
824830
_PyBinaryOpCache *cache = (_PyBinaryOpCache *)(next_instr+1);

Python/ceval_macros.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -358,12 +358,16 @@ do { \
358358
// for an exception handler, displaying the traceback, and so on
359359
#define INSTRUMENTED_JUMP(src, dest, event) \
360360
do { \
361-
_PyFrame_SetStackPointer(frame, stack_pointer); \
362-
next_instr = _Py_call_instrumentation_jump(tstate, event, frame, src, dest); \
363-
stack_pointer = _PyFrame_GetStackPointer(frame); \
364-
if (next_instr == NULL) { \
365-
next_instr = (dest)+1; \
366-
goto error; \
361+
if (tstate->tracing) {\
362+
next_instr = dest; \
363+
} else { \
364+
_PyFrame_SetStackPointer(frame, stack_pointer); \
365+
next_instr = _Py_call_instrumentation_jump(tstate, event, frame, src, dest); \
366+
stack_pointer = _PyFrame_GetStackPointer(frame); \
367+
if (next_instr == NULL) { \
368+
next_instr = (dest)+1; \
369+
goto error; \
370+
} \
367371
} \
368372
} while (0);
369373

Python/executor_cases.c.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/generated_cases.c.h

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

Python/instrumentation.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,15 +1217,13 @@ int
12171217
_Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame, _Py_CODEUNIT *instr, _Py_CODEUNIT *prev)
12181218
{
12191219
PyCodeObject *code = _PyFrame_GetCode(frame);
1220+
assert(tstate->tracing == 0);
12201221
assert(is_version_up_to_date(code, tstate->interp));
12211222
assert(instrumentation_cross_checks(tstate->interp, code));
12221223
int i = (int)(instr - _PyCode_CODE(code));
12231224

12241225
_PyCoMonitoringData *monitoring = code->_co_monitoring;
12251226
_PyCoLineInstrumentationData *line_data = &monitoring->lines[i];
1226-
if (tstate->tracing) {
1227-
goto done;
1228-
}
12291227
PyInterpreterState *interp = tstate->interp;
12301228
int8_t line_delta = line_data->line_delta;
12311229
int line = 0;

0 commit comments

Comments
 (0)