From dd4ad34b30375b8d4133f00e102d22eb9f2657ae Mon Sep 17 00:00:00 2001 From: sharif Date: Sun, 14 Jun 2026 22:17:41 -0400 Subject: [PATCH] perf: look up instance __dict__ by the interned name (cached hash) in getattr generic_getattr_opt looked up the instance dict by name_str.as_wtf8() -- the &Wtf8 content, whose DictKey::key_hash recomputes the SipHash from the raw bytes on every attribute access. Look it up by name_str (the &Py) instead: its DictKey returns the hash PyStr already caches, and adds a pointer-equality fast path for the key compare. (generic_setattr already passed the PyStr, so writes were never affected.) Free -- no memory added; the hash cache already exists. Benchmarks (interleaved A/B vs main, median): attribute-read loop -17.3%, pystone -3.0%, OOP method loop -7.9%; neutral on non-attribute code. Verified: 12 attribute/descriptor/class/dataclass test modules pass; clippy clean. Co-Authored-By: Claude Opus 4.8 (1M context) Assisted-by: Claude:claude-opus-4 --- crates/vm/src/protocol/object.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/vm/src/protocol/object.rs b/crates/vm/src/protocol/object.rs index 37007422404..dc59653416e 100644 --- a/crates/vm/src/protocol/object.rs +++ b/crates/vm/src/protocol/object.rs @@ -230,7 +230,6 @@ impl PyObject { dict: Option, vm: &VirtualMachine, ) -> PyResult> { - let name = name_str.as_wtf8(); let obj_cls = self.class(); let cls_attr_name = vm.ctx.interned_str(name_str); let cls_attr = match cls_attr_name.and_then(|name| obj_cls.get_attr(name)) { @@ -251,7 +250,9 @@ impl PyObject { let dict = dict.or_else(|| self.dict()); let attr = if let Some(dict) = dict { - dict.get_item_opt(name, vm)? + // Look up by the `PyStr` object (cached hash + pointer-equality fast + // path) rather than its `&Wtf8` content, which re-hashes every call. + dict.get_item_opt(name_str, vm)? } else { None };