Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Performance: close the gap with CPython (tracking issue) #38

Copy link
Copy link

Description

@youknowone
Issue body actions

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

  • Machine: Apple M4, macOS (Darwin 25.2.0)
  • RustPython: gc branch @ 2772a84e5, cargo build --release (thin LTO)
  • CPython: 3.14.2 (Homebrew)
  • Suite: ~140 small self-contained benchmark scripts (classic kernels: fannkuch, nbody, spectral_norm, fib variants + ~130 synthetic microbench scripts covering attribute access, calls, exceptions, comprehensions, unary/binary ops, etc.). Each script run 3 times per interpreter, best-of-3 wall clock, interpreter startup subtracted (startup itself is fine: 22.7ms vs 14.6ms).

Headline numbers

Across the 67 benchmarks where CPython execution takes >20ms:

  • geometric mean: 5.4x slower than CPython, median 6.0x

Worst and best cases:

benchmark ratio dominant cost
from math import pi, e in hot loop 17.5x import machinery + dict + call overhead
method call + instance attr (self.value) loop 16.4x attr specialization defeated (#41)
recursive fib 10.2x Python call overhead
call helper fn in loop 9.3x Python call overhead
fannkuch 6.8x dispatch loop + list ops
float loop 5.7x float heap alloc per op
int loop 5.2x int heap alloc per op + dispatch
exception raise/catch loop 6.4x exception path allocations
bigint fib loop 0.6x (faster!) malachite beats CPython longobject

Micro-measurements (best-of-loop, per-operation cost):

operation CPython RustPython ratio
instance attr read (c.x, plain class) overhead 1.9ns 91.5ns ~48x
Python→Python call overhead (f(1), 1 arg) 9.3ns 140.2ns ~15x
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:

  1. Per-instruction dispatch overheadOpcode::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.
  2. Python→Python call cost — per call: heap-allocated Frame PyObject (+GC tracking with a global lock), args Vec allocation, refcount churn, Rust-stack recursion.
  3. 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.
  4. Even specialized attribute ops do full dict probesLoadAttrWithHint doesn't use its hint; LoadAttrMethodWithValues does a whole dict lookup as its shadow check (no dict keys version).
  5. Arithmetic allocates — every int/float result outside the −5..256 cache is a fresh heap object with a ~48-byte header; the specialized BinaryOpAddInt even takes the full BigInt path.
  6. Allocation-time GC tracking + always-atomic refcountstrack_object takes a global generation-list RwLock + SeqCst atomics per tracked allocation; every incref/decref is an atomic RMW.
  7. Build config — release profile leaves codegen-units at 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_BORROW refcount elision, bigint arithmetic (faster than CPython).


This issue series was researched and written by Claude on behalf of @youknowone.

Reactions are currently unavailable

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    Morty Proxy This is a proxified and sanitized view of the page, visit original site.