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
Get RustPython interpreter performance close to (or beyond) CPython, while keeping the free-threading design: no GIL, and all chosen optimizations must remain sound under concurrent threads.
loop iteration baseline (acc += 1 in for _ in range)
14.0ns
63.9ns
~4.6x
Where the time goes (samply profiles)
Profiling the release binary over the benchmarks above consistently shows the same handful of structural costs; each has its own issue:
Per-instruction dispatch overhead — Opcode::cache_entries()/Opcode::deopt()/Instruction::as_opcode() chained match lookups + multiple atomic lasti updates per instruction consume 10–28% of total runtime depending on workload.
Python→Python call cost — per call: heap-allocated Frame PyObject (+GC tracking with a global lock), args Vec allocation, refcount churn, Rust-stack recursion.
LOAD_ATTR specialization is defeated for user-defined classes — their getattro slot is installed as getattro_wrapper, so the specializer bails and every attribute access goes through a full Python-level __getattribute__ call with MRO lookup + FuncArgs binding.
Even specialized attribute ops do full dict probes — LoadAttrWithHint doesn't use its hint; LoadAttrMethodWithValues does a whole dict lookup as its shadow check (no dict keys version).
Arithmetic allocates — every int/float result outside the −5..256 cache is a fresh heap object with a ~48-byte header; the specializedBinaryOpAddInt even takes the full BigInt path.
Allocation-time GC tracking + always-atomic refcounts — track_object takes a global generation-list RwLock + SeqCst atomics per tracked allocation; every incref/decref is an atomic RMW.
Build config — release profile leaves codegen-units at default and LTO at thin; low-hanging fruit.
What is already in good shape (verified during this analysis, no issue needed): adaptive specialization framework with CPython-compatible opcodes, inline caches with type/dict version tags, compact dict with interned-pointer fast path and cached string hashes, contiguous datastack for localsplus, LOAD_FAST_BORROW refcount elision, bigint arithmetic (faster than CPython).
This issue series was researched and written by Claude on behalf of @youknowone.
Goal
Get RustPython interpreter performance close to (or beyond) CPython, while keeping the free-threading design: no GIL, and all chosen optimizations must remain sound under concurrent threads.
Measurement
gcbranch @ 2772a84e5,cargo build --release(thin LTO)Headline numbers
Across the 67 benchmarks where CPython execution takes >20ms:
Worst and best cases:
from math import pi, ein hot loopself.value) loopMicro-measurements (best-of-loop, per-operation cost):
c.x, plain class) overheadf(1), 1 arg)acc += 1infor _ in range)Where the time goes (samply profiles)
Profiling the release binary over the benchmarks above consistently shows the same handful of structural costs; each has its own issue:
Opcode::cache_entries()/Opcode::deopt()/Instruction::as_opcode()chained match lookups + multiple atomiclastiupdates per instruction consume 10–28% of total runtime depending on workload.FramePyObject (+GC tracking with a global lock), argsVecallocation, refcount churn, Rust-stack recursion.LOAD_ATTRspecialization is defeated for user-defined classes — theirgetattroslot is installed asgetattro_wrapper, so the specializer bails and every attribute access goes through a full Python-level__getattribute__call with MRO lookup +FuncArgsbinding.LoadAttrWithHintdoesn't use its hint;LoadAttrMethodWithValuesdoes a whole dict lookup as its shadow check (no dict keys version).BinaryOpAddInteven takes the fullBigIntpath.track_objecttakes a global generation-listRwLock+ SeqCst atomics per tracked allocation; every incref/decref is an atomic RMW.codegen-unitsat default and LTO at thin; low-hanging fruit.Sub-issues:
What is already in good shape (verified during this analysis, no issue needed): adaptive specialization framework with CPython-compatible opcodes, inline caches with type/dict version tags, compact dict with interned-pointer fast path and cached string hashes, contiguous datastack for localsplus,
LOAD_FAST_BORROWrefcount elision, bigint arithmetic (faster than CPython).This issue series was researched and written by Claude on behalf of @youknowone.