Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
f20fb34
time: use CLOCK_UPTIME_RAW for monotonic/perf_counter on Apple
moreal Sep 7, 2026
d427717
vm: derive f_lineno lazily instead of tracking prev_line per instruction
moreal Sep 7, 2026
e67cc57
vm: coalesce per-call thread-locals into one FRAME_SLOT_CACHE
moreal Sep 7, 2026
ae7ce96
vm: fold stop/finalizing checks into EVAL_BREAKER
moreal Sep 7, 2026
2f8eb35
gc: set the TRACKED bit with a plain store for freshly allocated objects
moreal Sep 7, 2026
364bb76
vm: skip thread exception publish when pushing/popping a None exc slot
moreal Sep 7, 2026
4a14b04
vm: allocation-free keyword-call fast path and super() MRO walk
moreal Sep 7, 2026
57c75d4
builtins: cheaper enumerate and zip iteration
moreal Sep 7, 2026
f5b2871
_asyncio: cache module objects and use native set/dict operations
moreal Sep 7, 2026
07c31da
perf: use mimalloc as global allocator for rustpython CLI
moreal Sep 7, 2026
6ed9a1a
vm: cap dict/set table shrink and compact holes on resize
moreal Sep 7, 2026
db9e765
_functools: add native _lru_cache_wrapper
moreal Sep 7, 2026
2a59cad
sre_engine: ASCII fast path for \d \s \w unicode category checks
moreal Sep 7, 2026
78cca65
vm: skip VM attach for no-op generator/coroutine __del__
moreal Sep 7, 2026
96018cf
_asyncio: skip full __import__ dispatch on module-cache hot path
moreal Sep 7, 2026
3f06556
vm: skip VM-attach section in with_vm when already on the current int…
moreal Sep 7, 2026
01619c2
vm: general sys.modules fast path for vm.import
moreal Sep 7, 2026
7573dde
_asyncio: bypass generic context.run dispatch in task_eager_start
moreal Sep 7, 2026
61f3684
vm: drop scopeguard::defer! from generator/coroutine resume hot path
moreal Sep 7, 2026
08d938d
vm: run generator bodies through the call trampoline
moreal Sep 7, 2026
580eab9
vm: run a `yield from` chain in one eval loop
moreal Sep 7, 2026
b3e46c1
vm: reset the oparg prefix when a signal unwinds a frame
moreal Sep 7, 2026
c41d05a
bytecode: fetch a code unit with one 16-bit acquire load
moreal Sep 7, 2026
4a008a2
vm: compute the cache-entry count after dispatch, not before
moreal Sep 7, 2026
8530edf
vm: move the eval-breaker body out of the dispatch loop
moreal Sep 7, 2026
654880e
vm: always inline the eval stack push/pop primitives
moreal Sep 7, 2026
66a38f2
vm: carry the instruction fetch state in loop locals
moreal Sep 7, 2026
1889376
bytecode: hoist the instruction array base out of the dispatch loop
moreal Sep 8, 2026
42aca22
common: an immortal bit in the refcount word
moreal Sep 8, 2026
fce1a8e
vm: make the singletons, small ints, interned strings and static type…
moreal Sep 8, 2026
bffef2e
codecs: fast-path utf-8 decode, skipping the codec registry round trip
moreal Sep 8, 2026
50934b9
codecs: fast-path utf-8/ascii/latin-1 encode and ascii/latin-1 decode
moreal Sep 8, 2026
ebf53f0
vm: make the runtime safe for borrowed stack entries
moreal Sep 8, 2026
54d7782
vm: read consumed stack entries in place instead of owning them
moreal Sep 8, 2026
0ad8ae3
vm: turn LOAD_FAST_BORROW into an actual borrow
moreal Sep 8, 2026
b95c00f
vm: exact-dict fast path for DICT_MERGE and CALL_FUNCTION_EX kwargs
moreal Sep 8, 2026
cbcc4b2
vm: skip the recursion guard hashing exact ints inside a tuple
moreal Sep 8, 2026
e308bde
vm: wire slot descriptors from __slots__ into C-level type slots
moreal Sep 8, 2026
ece684e
frame: stop allocating cold data to close a generator
moreal Sep 8, 2026
4508c40
function: bind exact args straight into a generator's frame
moreal Sep 8, 2026
00bb6a5
frame: drain a dead frame in place instead of moving it out
moreal Sep 8, 2026
be4d920
vm: don't raise a TypeError to ask an iterator for its length
moreal Sep 8, 2026
0c490ae
gc: track a generator and its frame in one pass
moreal Sep 8, 2026
13c0304
dict_inner: fast-path exact-int equality in key_eq
moreal Sep 8, 2026
0bf140b
vm: let the modulus type handle a three-argument pow
moreal Sep 8, 2026
000a972
vm: add a feature-gated opcode-pair histogram
moreal Sep 8, 2026
791e7a0
vm: let boolean producers run the branch that follows them
moreal Sep 8, 2026
5f48272
Adapt yield-from resume to optional generator frames
youknowone Sep 19, 2026
91928bb
Fix clippy and cr_origin after try_to_bool/f_lineno changes
youknowone Sep 19, 2026
7111aad
Convert fd filenames without the os ToPyObject impl
youknowone Sep 19, 2026
0abc5c9
Gate fd path helpers on the host_env feature
youknowone Sep 19, 2026
a8df5cd
Drop redundant pickle try_to_bool clones after rebase
youknowone Sep 19, 2026
11d6a20
Document why enumerate keeps a Counter beside BigInt
youknowone Sep 19, 2026
aa7a8b1
Move fd path helpers into ospath_fd under host_env
youknowone Sep 19, 2026
105ad02
Group threading eval-breaker bits in one module
youknowone Sep 19, 2026
9d8c910
Make InterpreterFrame construction unsafe
youknowone Sep 19, 2026
aa1298b
Move OsPathOrFd re-export to the top of ospath.rs
youknowone Sep 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
vm: exact-dict fast path for DICT_MERGE and CALL_FUNCTION_EX kwargs
`g(**d)` on an exact dict compiled to BUILD_MAP+DICT_MERGE+CALL_FUNCTION_EX
and paid for two Python-level `keys()`/`__getitem__` round trips per key at
both the DICT_MERGE handler and collect_ex_args' iterate_mapping_keys, even
though the source is a plain dict. Add an exact-dict (downcast + type
check, so subclasses still go through the mapping protocol) fast path to
both sites that iterates entries natively via PyDict::items_vec, matching
CPython's PyDict_Check fast paths in PyDict_Merge / CALL_FUNCTION_EX.

Measured (1M calls, release build): g(**d) 1320ns -> 665ns, f(**d)
1470ns -> 900ns, c.m(**d) 1690ns -> 1135ns, f(x=1,**d) 1600ns -> 1035ns,
f(**d1,**d2) 1700ns -> 1013ns. bm_django_template ~7% faster,
bm_argparse (subparsers) ~7% faster.

Assisted-by: Claude Code:claude-fable-5-1
  • Loading branch information
moreal authored and youknowone committed Sep 19, 2026
commit b95c00fd63fc533598957ee644b596026265b654
50 changes: 46 additions & 4 deletions crates/vm/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4343,6 +4343,33 @@ impl ExecutingFrame<'_> {
let callable = self.nth_value(idx + 2);
let func_str = Self::object_function_str(callable, vm);

// Fast path: source is an exact dict (not a subclass, which may
// override `keys`/`__getitem__`). Iterate its entries natively
// instead of going through the mapping protocol, mirroring
// CPython's `PyDict_Merge` fast path for `PyDict_Check(other)`.
let source = if source.class().is(vm.ctx.types.dict_type) {
let src_dict = source
.downcast_ref::<PyDict>()
.expect("exact dict must have a PyDict payload");
// Snapshot under a single read lock so a mutation of `source`
// triggered by `dict.set_item` (e.g. via a target subclass, or
// aliasing) can't be observed mid-iteration.
for (key, value) in src_dict.items_vec() {
if dict.contains_key(&*key, vm) {
let key_str = key.str(vm)?;
return Err(vm.new_type_error(format!(
"{} got multiple values for keyword argument '{}'",
func_str,
key_str.as_wtf8()
)));
}
dict.set_item(&*key, value, vm)?;
}
return Ok(None);
} else {
source
};

// Check if source is a mapping
if vm
.get_method(source.clone(), vm.ctx.intern_str("keys"))
Expand Down Expand Up @@ -8427,13 +8454,12 @@ impl ExecutingFrame<'_> {
let callable = self.nth_value(2);
let func_str = Self::object_function_str(callable, vm);

Self::iterate_mapping_keys(vm, &kw_obj, &func_str, |key| {
Self::iterate_mapping_keys(vm, &kw_obj, &func_str, |key, value| {
// `PyStr`, not `PyUtf8Str`: CPython only checks that the key is a
// `str`, not that it is valid UTF-8, so surrogate keys are accepted.
let key_str = key
.downcast_ref::<PyStr>()
.ok_or_else(|| vm.new_type_error("keywords must be strings"))?;
let value = kw_obj.get_item(&*key, vm)?;
kwargs.insert(key_str.as_wtf8().to_owned(), value);
Ok(())
})?
Expand Down Expand Up @@ -8500,8 +8526,23 @@ impl ExecutingFrame<'_> {
mut key_handler: F,
) -> PyResult<()>
where
F: FnMut(PyObjectRef) -> PyResult<()>,
F: FnMut(PyObjectRef, PyObjectRef) -> PyResult<()>,
{
// Fast path: exact dict (e.g. built by DICT_MERGE), not a subclass which
// may override `keys`/`__getitem__`. Iterate its entries natively,
// mirroring CPython's `PyDict_Check(kwargs)` fast path in `CALL_FUNCTION_EX`.
if mapping.class().is(vm.ctx.types.dict_type) {
let dict = mapping
.downcast_ref::<PyDict>()
.expect("exact dict must have a PyDict payload");
// Snapshot under a single read lock: safe against mutation of
// `mapping` from within `key_handler`.
for (key, value) in dict.items_vec() {
key_handler(key, value)?;
}
return Ok(());
}

let Some(keys_method) = vm.get_method(mapping.to_owned(), vm.ctx.intern_str("keys")) else {
return Err(vm.new_type_error(format!(
"{} argument after ** must be a mapping, not {}",
Expand All @@ -8512,7 +8553,8 @@ impl ExecutingFrame<'_> {

let keys = keys_method?.call((), vm)?.get_iter(vm)?;
while let PyIterReturn::Return(key) = keys.next(vm)? {
key_handler(key)?;
let value = mapping.get_item(&*key, vm)?;
key_handler(key, value)?;
}
Ok(())
}
Expand Down