You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currently we project traces from the initial hotspot, but there are a number of places in the projection where we would benefit from having live values as well as the statistical information gathered by the interpreter.
We can combine the information from live values with the statistical information to give us a trace that should, in theory, be better than from either pure trace recording or from projection.
Design
Confidence
As a trace gets longer, it is less likely that a future execution will remain on trace. Our estimate of that future likelihood is our "confidence" in the trace at that point. All traces start with confidence at 100%, but that confidence drops with each guard in the trace.
The trace recording interpreter
The trace recording interpreter operates exactly as the normal tier 1 interpreter, while recording the instructions executed.
While trace recording we should maintain a confidence level, but this confidence level will not be as accurate as the optimizer can provide. Since the optimizer might be more confident, and we cannot optimize beyond the end of the recorded trace, the confidence limit for recording should be lower than for the final trace.
Possible implementations
We can:
Record the trace at the bytecode level, then lower to micro-ops as a separate pass
Lower as we go. This is more complicated, but may be faster as it saves recording the bytecode.
I think we should start with 1. We can move to 2. later without impacting the rest of the JIT.
To avoid doubling the size of the interpreter, each recording opcode implementation should call a common recording function then jump to the normal implementation of that opcode.
However, some specialized instructions could "deopt", executing the non-specialized instruction. This would result in a misleading trace, so we might want to somehow force re-specialization in that case. It is not obvious how to do that without duplicating all specialized instructions. We could force re-specialization of all instructions, that might be slow.
@Fidget-Spinner
We can keep the switch interpreter working with the tracing JIT using the same technique as we did for sys.settrace tracing in 3.11.
Change Tools/cases_generator/analyzer.py to change ENTER_EXECUTOR to 254, and lower the number of all the instrumented instructions by one, leaving 255 as reserved. Call opcode 255 INTERNAL_TRACING.
In PyEval_EvalDefault add a local variable tracing which is set to 0 if tracing is off and 255 if tracing is on.
The INTERNAL_TRACING instruction should jump to record_previous_inst which should dispatch using the plain opcode without tracing. It will be unused for computed gotos and tailcalling.
Dispatch is a bit more complicated. Instead of dispatching on opcode, dispatch on dispatch_opcode. Before the goto dispatch, the various DISPATCH macros need to set dispatch_opcode:
Non-tracing dispatch: dispatch_opcode = opcode
Normal dispatch: dispatch_opcode = opcode | tracing
Tracing dispatch: dispatch_opcode = 255
Removing the 3.11 scheme was worth about 1% speedup, so adding it back will slow the interpreter at least 1% as this is more complex than the 3.11 scheme. However, if we make the JIT faster, we should get that back and more.
Another action item: prime number table for the backoff counters. This will allow for a trace of a "better" iteration. Note that pypy also uses a "strange" number for warmup of loops.
I'd rather keep this open, otherwise it gives a false impression of being finished when there's some clean up to do.
Or, make new issues for the list above and then close this one.
Restore the optimization for stack size checks and its tests
I think we should abandon this and just clean up the code implementing this optimisation. It's generally very finnicky and I don't think it nets us much perf wins.
I think we should abandon this and just clean up the code implementing this optimisation. It's generally very finnicky and I don't think it nets us much perf wins.
Hi, @Fidget-Spinner
Refer: #144388. Should we convert _CHECK_STACK_SPACE to _CHECK_STACK_SPACE_OPERAND one by one? If we need this, i'll implement it.
Activity
gh-139109: Dynamic opcode targets (GH-139111)
markshannon commentedon Sep 23, 2025
Just to flesh this out a bit:
Motivation
Currently we project traces from the initial hotspot, but there are a number of places in the projection where we would benefit from having live values as well as the statistical information gathered by the interpreter.
We can combine the information from live values with the statistical information to give us a trace that should, in theory, be better than from either pure trace recording or from projection.
Design
Confidence
As a trace gets longer, it is less likely that a future execution will remain on trace. Our estimate of that future likelihood is our "confidence" in the trace at that point. All traces start with confidence at 100%, but that confidence drops with each guard in the trace.
The trace recording interpreter
The trace recording interpreter operates exactly as the normal tier 1 interpreter, while recording the instructions executed.
While trace recording we should maintain a confidence level, but this confidence level will not be as accurate as the optimizer can provide. Since the optimizer might be more confident, and we cannot optimize beyond the end of the recorded trace, the confidence limit for recording should be lower than for the final trace.
Possible implementations
We can:
I think we should start with 1. We can move to 2. later without impacting the rest of the JIT.
To avoid doubling the size of the interpreter, each recording opcode implementation should call a common recording function then jump to the normal implementation of that opcode.
However, some specialized instructions could "deopt", executing the non-specialized instruction. This would result in a misleading trace, so we might want to somehow force re-specialization in that case. It is not obvious how to do that without duplicating all specialized instructions. We could force re-specialization of all instructions, that might be slow.
markshannon commentedon Oct 29, 2025
@Fidget-Spinner
We can keep the switch interpreter working with the tracing JIT using the same technique as we did for
sys.settracetracing in 3.11.INTERNAL_TRACING.tracingwhich is set to 0 if tracing is off and 255 if tracing is on.INTERNAL_TRACINGinstruction should jump torecord_previous_instwhich should dispatch using the plainopcodewithouttracing. It will be unused for computed gotos and tailcalling.opcode, dispatch ondispatch_opcode. Before thegoto dispatch, the variousDISPATCHmacros need to setdispatch_opcode:dispatch_opcode = opcodedispatch_opcode = opcode | tracingdispatch_opcode = 255Removing the 3.11 scheme was worth about 1% speedup, so adding it back will slow the interpreter at least 1% as this is more complex than the 3.11 scheme. However, if we make the JIT faster, we should get that back and more.
markshannon commentedon Nov 11, 2025
Once #140310 is merged, there are a few more tasks to do:
NOPwhen entering tracing.Fidget-Spinner commentedon Nov 11, 2025
The only test we need to restore is the stack size optimization checks. That's all the tests that are disabled for now.
Fidget-Spinner commentedon Nov 13, 2025
Another action item: prime number table for the backoff counters. This will allow for a trace of a "better" iteration. Note that pypy also uses a "strange" number for warmup of loops.
markshannon commentedon Nov 13, 2025
#141498
gh-139109: A new tracing JIT compiler frontend for CPython (GH-140310)
Merge branch 'main' into fix-issue21914
Merge branch 'main' of github.com:python/cpython into fix-Issue18387
Merge branch 'main' of github.com:python/cpython into fix-issue24255
7 remaining items
Fidget-Spinner commentedon Dec 10, 2025
Yeah lets do follow ups separately
markshannon commentedon Dec 16, 2025
I'd rather keep this open, otherwise it gives a false impression of being finished when there's some clean up to do.
Or, make new issues for the list above and then close this one.
Fidget-Spinner commentedon Dec 16, 2025
I think we should abandon this and just clean up the code implementing this optimisation. It's generally very finnicky and I don't think it nets us much perf wins.
gh-139109: Add terminator to JIT code when halting due to invalid dep…
GH-139109: Partial reworking of JIT data structures (GH-144105)
cocolato commentedon Feb 2, 2026
Hi, @Fidget-Spinner
Refer: #144388. Should we convert
_CHECK_STACK_SPACEto_CHECK_STACK_SPACE_OPERANDone by one? If we need this, i'll implement it.gh-139109: Replace `_CHECK_STACK_SPACE` with `_CHECK_STACK_SPACE_OPER…
pythongh-139109: Replace `_CHECK_STACK_SPACE` with `_CHECK_STACK_SPAC…
pythonGH-139109: Partial reworking of JIT data structures (pythonGH-1…
pythongh-139109: Replace `_CHECK_STACK_SPACE` with `_CHECK_STACK_SPAC…
gh-139109: Document the trace recording interpreter in internaldocs (G…
pythongh-139109: Document the trace recording interpreter in internal…
pythongh-139109: Document the trace recording interpreter in internal…