From 43a0c0e01e9356c6d80bd5d5ad54840090ab1726 Mon Sep 17 00:00:00 2001 From: "Gabriele N. Tornetta" Date: Fri, 22 May 2026 14:42:41 +0100 Subject: [PATCH] perf: bypass ConcreteInstr validation in concrete_instructions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two changes to the hot encode loop in _ConvertBytecodeToConcrete.concrete_instructions: 1. ConcreteInstr._from_trusted factory ConcreteInstr(instr_name, c_arg, location=location) at the bottom of the loop went through the full __init__ -> _check_arg -> _set chain on every instruction, even though name/opcode/arg/location are all derived from already-validated Instr objects. A new _from_trusted classmethod bypasses _check_arg entirely and replicates only the size computation from _set, using object.__new__ + direct slot assignment — the same pattern as the existing _from_opcode fast path. 2. Direct private slot access instr.location, instr.name, and instr.arg are properties whose bodies are each a single "return self._xxx". Accessing instr._location, instr._name, and instr._arg directly avoids the property descriptor lookup on every iteration. This is safe because instr is a validated Instr instance at this point (guarded by the assert isinstance check above). Profile data | Hotspot | Before | After | |---|---|---| | concrete_instructions own | 8.07% | 5.44% | | concrete_instructions total | 11.71% | 9.44% | | ConcreteInstr.__init__ (child) | 1.30% total | gone | Throughput (Bytecode.from_code().to_code() on dis module, 30 runs each) | | Avg | Stddev | |---|---|---| | Before | 223.7 r/s | ±4.7 | | After | 246.1 r/s | ±3.3 | +22.4 r/s / +10.0% throughput improvement. --- src/bytecode/concrete.py | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/bytecode/concrete.py b/src/bytecode/concrete.py index 193c4fd2..6f7f1c98 100644 --- a/src/bytecode/concrete.py +++ b/src/bytecode/concrete.py @@ -199,6 +199,30 @@ def _from_opcode( new._size = 2 return new + @classmethod + def _from_trusted( + cls: Type[T], + name: str, + opcode: int, + arg: int, + location: Optional[InstrLocation], + ) -> T: + """Fast path for concrete_instructions: skip validation, compute size from arg.""" + new = object.__new__(cls) + new._name = name + new._opcode = opcode + new._arg = arg + new._location = location + new._extended_args = None + size = 2 + if arg is not UNSET: + _arg = arg + while _arg > 0xFF: + size += 2 + _arg >>= 8 + new._size = size + return new + @classmethod def disassemble(cls: Type[T], lineno: Optional[int], code: bytes, offset: int) -> T: index = 2 * offset @@ -1145,12 +1169,14 @@ def concrete_instructions(self) -> None: assert isinstance(instr, Instr) - if instr.location is not UNSET and instr.location is not None: - location = instr.location + # Access private slots directly — avoids property descriptor overhead on + # every iteration; safe because instr is a validated Instr at this point. + if instr._location is not UNSET and instr._location is not None: + location = instr._location - instr_name = instr.name + instr_name = instr._name opcode = instr._opcode - arg = instr.arg + arg = instr._arg is_jump = False if isinstance(arg, Label): label = arg @@ -1234,7 +1260,7 @@ def concrete_instructions(self) -> None: c_arg = arg # The above should have performed all the necessary conversion - c_instr = ConcreteInstr(instr_name, c_arg, location=location) + c_instr = ConcreteInstr._from_trusted(instr_name, opcode, c_arg, location) if is_jump: self.jumps.append((len(self.instructions), label, c_instr))