From 41a956a914ca92ebb5f8a482cf348efb9dd43f2e Mon Sep 17 00:00:00 2001 From: Gianmaria Rovelli Date: Mon, 18 Aug 2025 21:38:57 +0100 Subject: [PATCH 1/3] fast-path hash() for exact PyStr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit str.__hash__ can’t be overridden, so for exact PyStr instances we skip the attribute lookup and MRO walk and call the string hash directly. This reduces overhead on hot paths (e.g. dict/set key hashing) while keeping subclass semantics intact (downcast_ref_if_exact). --- vm/src/protocol/object.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vm/src/protocol/object.rs b/vm/src/protocol/object.rs index d398da0237c..53fcc4c1bd7 100644 --- a/vm/src/protocol/object.rs +++ b/vm/src/protocol/object.rs @@ -642,6 +642,11 @@ impl PyObject { } pub fn hash(&self, vm: &VirtualMachine) -> PyResult { + // __hash__ function in str cannot be overwritten + if let Some(pystr) = self.downcast_ref_if_exact::(vm) { + return Ok(pystr.hash(vm)); + } + let hash = self.get_class_attr(identifier!(vm, __hash__)).unwrap(); if vm.is_none(&hash) { return Err(vm.new_exception_msg( From 6a29c4c8894a67ab1fb16e77fdcd72b985b8788a Mon Sep 17 00:00:00 2001 From: Gianmaria Rovelli Date: Wed, 20 Aug 2025 20:23:36 +0100 Subject: [PATCH 2/3] avoid get_class_attr for __hash__; read hash slot via mro_find_map Reduce calls and lock acquisitions on hot paths by bypassing get_class_attr(__hash__) and directly resolving the hash implementation with mro_find_map(|cls| cls.slots.hash.load()). --- vm/src/protocol/object.rs | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/vm/src/protocol/object.rs b/vm/src/protocol/object.rs index 53fcc4c1bd7..037895bb891 100644 --- a/vm/src/protocol/object.rs +++ b/vm/src/protocol/object.rs @@ -642,25 +642,14 @@ impl PyObject { } pub fn hash(&self, vm: &VirtualMachine) -> PyResult { - // __hash__ function in str cannot be overwritten - if let Some(pystr) = self.downcast_ref_if_exact::(vm) { - return Ok(pystr.hash(vm)); + if let Some(hash) = self.class().mro_find_map(|cls| cls.slots.hash.load()) { + return hash(self, vm); } - let hash = self.get_class_attr(identifier!(vm, __hash__)).unwrap(); - if vm.is_none(&hash) { - return Err(vm.new_exception_msg( - vm.ctx.exceptions.type_error.to_owned(), - format!("unhashable type: '{}'", self.class().name()), - )); - } - - let hash = self - .class() - .mro_find_map(|cls| cls.slots.hash.load()) - .unwrap(); - - hash(self, vm) + return Err(vm.new_exception_msg( + vm.ctx.exceptions.type_error.to_owned(), + format!("unhashable type: '{}'", self.class().name()), + )); } // type protocol From 32c74b83346d0538a16b07ea9b2b7d811e98a0d0 Mon Sep 17 00:00:00 2001 From: Gianmaria Rovelli Date: Wed, 20 Aug 2025 21:27:04 +0100 Subject: [PATCH 3/3] fix linting in hash function --- vm/src/protocol/object.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/src/protocol/object.rs b/vm/src/protocol/object.rs index 037895bb891..4dc382fb781 100644 --- a/vm/src/protocol/object.rs +++ b/vm/src/protocol/object.rs @@ -646,10 +646,10 @@ impl PyObject { return hash(self, vm); } - return Err(vm.new_exception_msg( + Err(vm.new_exception_msg( vm.ctx.exceptions.type_error.to_owned(), format!("unhashable type: '{}'", self.class().name()), - )); + )) } // type protocol