Skip to content

Commit 7727acc

Browse files
authored
Reduce per-instruction overhead in interpreter loop (#7315)
* Reduce per-instruction overhead in interpreter loop - Defer prev_line update to when tracing is active - Inline skip_caches_if_fallthrough, compute cache_entries once per instruction * Unmark test_pdb_set_trace EXPECTED_FAILURE prev_line deferral fix corrects pdb line tracking, making these doctests pass.
1 parent 7eb1821 commit 7727acc

File tree

2 files changed

+40
-40
lines changed

2 files changed

+40
-40
lines changed

Lib/test/test_doctest/test_doctest.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2063,7 +2063,7 @@ def test_pdb_set_trace():
20632063
... 'continue', # stop debugging
20642064
... ''])
20652065
2066-
>>> try: runner.run(test) # TODO: RUSTPYTHON # doctest: +EXPECTED_FAILURE
2066+
>>> try: runner.run(test)
20672067
... finally: sys.stdin = real_stdin
20682068
> <doctest foo-bar@baz[2]>(1)<module>()
20692069
-> import pdb; pdb.set_trace()
@@ -2091,7 +2091,7 @@ def test_pdb_set_trace():
20912091
... 'continue', # stop debugging
20922092
... ''])
20932093
2094-
>>> try: # TODO: RUSTPYTHON # doctest: +EXPECTED_FAILURE
2094+
>>> try:
20952095
... runner.run(test)
20962096
... finally:
20972097
... sys.stdin = real_stdin
@@ -2209,7 +2209,7 @@ def test_pdb_set_trace_nested():
22092209
... 'continue', # stop debugging
22102210
... ''])
22112211
2212-
>>> try: # TODO: RUSTPYTHON # doctest: +EXPECTED_FAILURE
2212+
>>> try:
22132213
... runner.run(test)
22142214
... finally:
22152215
... sys.stdin = real_stdin

crates/vm/src/frame.rs

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -750,36 +750,43 @@ impl ExecutingFrame<'_> {
750750
let bytecode::CodeUnit { op, arg } = instructions[idx];
751751
let arg = arg_state.extend(arg);
752752
let mut do_extend_arg = false;
753+
let caches = op.cache_entries();
753754

754-
if !matches!(
755-
op,
756-
Instruction::Resume { .. }
757-
| Instruction::ExtendedArg
758-
| Instruction::InstrumentedLine
759-
) && let Some((loc, _)) = self.code.locations.get(idx)
760-
{
761-
self.state.prev_line = loc.line.get() as u32;
762-
}
763-
764-
// Fire 'opcode' trace event for sys.settrace when f_trace_opcodes
765-
// is set. Skip RESUME and ExtendedArg (matching CPython's exclusion
766-
// of these in _Py_call_instrumentation_instruction).
767-
if vm.use_tracing.get()
768-
&& !vm.is_none(&self.object.trace.lock())
769-
&& *self.object.trace_opcodes.lock()
770-
&& !matches!(
755+
// Update prev_line only when tracing or monitoring is active.
756+
// When neither is enabled, prev_line is stale but unused.
757+
if vm.use_tracing.get() {
758+
if !matches!(
771759
op,
772760
Instruction::Resume { .. }
773-
| Instruction::InstrumentedResume
774761
| Instruction::ExtendedArg
775-
)
776-
{
777-
vm.trace_event(crate::protocol::TraceEvent::Opcode, None)?;
762+
| Instruction::InstrumentedLine
763+
) && let Some((loc, _)) = self.code.locations.get(idx)
764+
{
765+
self.state.prev_line = loc.line.get() as u32;
766+
}
767+
768+
// Fire 'opcode' trace event for sys.settrace when f_trace_opcodes
769+
// is set. Skip RESUME and ExtendedArg (matching CPython's exclusion
770+
// of these in _Py_call_instrumentation_instruction).
771+
if !vm.is_none(&self.object.trace.lock())
772+
&& *self.object.trace_opcodes.lock()
773+
&& !matches!(
774+
op,
775+
Instruction::Resume { .. }
776+
| Instruction::InstrumentedResume
777+
| Instruction::ExtendedArg
778+
)
779+
{
780+
vm.trace_event(crate::protocol::TraceEvent::Opcode, None)?;
781+
}
778782
}
779783

780784
let lasti_before = self.lasti();
781785
let result = self.execute_instruction(op, arg, &mut do_extend_arg, vm);
782-
self.skip_caches_if_fallthrough(op, lasti_before);
786+
// Skip inline cache entries if instruction fell through (no jump).
787+
if caches > 0 && self.lasti() == lasti_before {
788+
self.update_lasti(|i| *i += caches as u32);
789+
}
783790
match result {
784791
Ok(None) => {}
785792
Ok(Some(value)) => {
@@ -3409,7 +3416,10 @@ impl ExecutingFrame<'_> {
34093416
let mut do_extend_arg = false;
34103417
self.execute_instruction(original_op, arg, &mut do_extend_arg, vm)
34113418
};
3412-
self.skip_caches_if_fallthrough(original_op, lasti_before_dispatch);
3419+
let orig_caches = original_op.to_base().unwrap_or(original_op).cache_entries();
3420+
if orig_caches > 0 && self.lasti() == lasti_before_dispatch {
3421+
self.update_lasti(|i| *i += orig_caches as u32);
3422+
}
34133423
result
34143424
}
34153425
Instruction::InstrumentedInstruction => {
@@ -3441,7 +3451,10 @@ impl ExecutingFrame<'_> {
34413451
let mut do_extend_arg = false;
34423452
self.execute_instruction(original_op, arg, &mut do_extend_arg, vm)
34433453
};
3444-
self.skip_caches_if_fallthrough(original_op, lasti_before_dispatch);
3454+
let orig_caches = original_op.to_base().unwrap_or(original_op).cache_entries();
3455+
if orig_caches > 0 && self.lasti() == lasti_before_dispatch {
3456+
self.update_lasti(|i| *i += orig_caches as u32);
3457+
}
34453458
result
34463459
}
34473460
_ => {
@@ -4106,19 +4119,6 @@ impl ExecutingFrame<'_> {
41064119
self.update_lasti(|i| *i = target);
41074120
}
41084121

4109-
/// Skip past CACHE code units after an instruction, but only if the
4110-
/// instruction did not modify lasti (i.e., it did not jump).
4111-
#[inline]
4112-
fn skip_caches_if_fallthrough(&mut self, op: Instruction, lasti_before: u32) {
4113-
if self.lasti() == lasti_before {
4114-
let base = op.to_base().unwrap_or(op);
4115-
let caches = base.cache_entries();
4116-
if caches > 0 {
4117-
self.update_lasti(|i| *i += caches as u32);
4118-
}
4119-
}
4120-
}
4121-
41224122
#[inline]
41234123
fn pop_jump_if_relative(
41244124
&mut self,

0 commit comments

Comments
 (0)