From 09ebd198573810654aae071afec5cc724327e3d4 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 22 Sep 2020 10:33:21 +0900 Subject: [PATCH] optimize tp_hash --- derive/src/pyclass.rs | 47 ++++++++++++++++++++++++------------------- vm/src/obj/objtype.rs | 2 +- vm/src/slots.rs | 23 +++++++++++++++++---- vm/src/vm.rs | 18 ++++++----------- 4 files changed, 52 insertions(+), 38 deletions(-) diff --git a/derive/src/pyclass.rs b/derive/src/pyclass.rs index bc0869c14..4c4356d10 100644 --- a/derive/src/pyclass.rs +++ b/derive/src/pyclass.rs @@ -334,32 +334,37 @@ where let slot_ident = item_meta.slot_name()?; let slot_name = slot_ident.to_string(); let tokens = { - let transform = if ["new", "call"].contains(&slot_name.as_str()) { - quote! { ::rustpython_vm::function::IntoPyNativeFunc::into_func } + if slot_name == "new" { + let into_func = quote_spanned! {ident.span() => + ::rustpython_vm::function::IntoPyNativeFunc::into_func(Self::#ident) + }; + quote! { + slots.#slot_ident = Some(#into_func); + } + } else if slot_name == "cmp" { + let into_func = quote_spanned! {ident.span() => + ::std::boxed::Box::new(Self::#ident) + }; + quote! { + slots.#slot_ident = Some(#into_func); + } } else { - quote! { ::std::boxed::Box::new } - }; - let into_func = quote_spanned! {ident.span() => - #transform(Self::#ident) - }; - match slot_name.as_str() { - "call" => quote! { - slots.#slot_ident.store(Some( + let into_func = if slot_name == "call" { + quote_spanned! {ident.span() => |zelf: ::rustpython_vm::pyobject::PyObjectRef, args: ::rustpython_vm::function::PyFuncArgs, vm: &::rustpython_vm::VirtualMachine| -> ::rustpython_vm::pyobject::PyResult { use ::rustpython_vm::pyobject::TryFromObject; let zelf = PyRef::::try_from_object(vm, zelf)?; Self::#ident(zelf, args, vm) - } as _ - )); - }, - "descr_get" => quote! { - slots.#slot_ident.store(Some( - Self::#ident as _ - )) - }, - _ => quote! { - slots.#slot_ident = Some(#into_func); - }, + } + } + } else { + quote_spanned! {ident.span() => + Self::#ident + } + }; + quote! { + slots.#slot_ident.store(Some(#into_func as _)) + } } }; diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 6731352bc..e15341705 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -702,7 +702,7 @@ pub fn new( if base.slots.flags.has_feature(PyTpFlags::HAS_DICT) { slots.flags |= PyTpFlags::HAS_DICT } - for slot_name in ["__call__", "__get__"].iter() { + for slot_name in ["__call__", "__get__", "__hash__"].iter() { if attrs.contains_key(*slot_name) { slots.update_slot_func(*slot_name); } diff --git a/vm/src/slots.rs b/vm/src/slots.rs index c8b59e05f..80ed60a0e 100644 --- a/vm/src/slots.rs +++ b/vm/src/slots.rs @@ -3,9 +3,10 @@ use std::cmp::Ordering; use crate::common::cell::PyRwLock; use crate::common::hash::PyHash; use crate::function::{OptionalArg, PyFuncArgs, PyNativeFunc}; +use crate::obj::objint::PyInt; use crate::pyobject::{ - IdProtocol, PyComparisonValue, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, - TypeProtocol, + BorrowValue, IdProtocol, PyComparisonValue, PyObjectRef, PyRef, PyResult, PyValue, + TryFromObject, TypeProtocol, }; use crate::VirtualMachine; use crossbeam_utils::atomic::AtomicCell; @@ -44,7 +45,7 @@ impl Default for PyTpFlags { type GenericMethod = fn(PyObjectRef, PyFuncArgs, &VirtualMachine) -> PyResult; type DescrGetFunc = fn(PyObjectRef, Option, Option, &VirtualMachine) -> PyResult; -type HashFunc = Box PyResult)>; +type HashFunc = fn(PyObjectRef, &VirtualMachine) -> PyResult; #[derive(Default)] pub struct PyClassSlots { @@ -53,7 +54,7 @@ pub struct PyClassSlots { pub new: Option, pub call: AtomicCell>, pub descr_get: AtomicCell>, - pub hash: Option, + pub hash: AtomicCell>, pub cmp: Option, } @@ -97,6 +98,20 @@ impl PyClassSlots { } as _; self.descr_get.store(Some(func)) } + "__hash__" => { + let func: HashFunc = |zelf, vm| { + let magic = get_class_magic(&zelf, "__hash__"); + let hash_obj = vm.invoke(&magic, vec![zelf])?; + match hash_obj.payload_if_subclass::(vm) { + Some(py_int) => { + Ok(rustpython_common::hash::hash_bigint(py_int.borrow_value())) + } + None => Err(vm + .new_type_error("__hash__ method should return an integer".to_owned())), + } + } as _; + self.hash.store(Some(func)); + } _ => (), } } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index bcf2157fe..caacdca3c 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -28,7 +28,7 @@ use crate::import; use crate::obj::objbool; use crate::obj::objcode::{PyCode, PyCodeRef}; use crate::obj::objdict::PyDictRef; -use crate::obj::objint::{PyInt, PyIntRef}; +use crate::obj::objint::PyIntRef; use crate::obj::objiter; use crate::obj::objlist::PyList; use crate::obj::objmodule::{self, PyModule}; @@ -1453,17 +1453,11 @@ impl VirtualMachine { } pub fn _hash(&self, obj: &PyObjectRef) -> PyResult { - if let Some(ref hash) = obj.lease_class().slots.hash { - hash(obj.clone(), self) - } else { - let hash_obj = self.call_method(obj, "__hash__", vec![])?; - match hash_obj.payload_if_subclass::(self) { - Some(py_int) => Ok(rustpython_common::hash::hash_bigint(py_int.borrow_value())), - None => { - Err(self.new_type_error("__hash__ method should return an integer".to_owned())) - } - } - } + let hash = obj + .class() + .first_in_mro(|cls| cls.slots.hash.load()) + .unwrap(); // hash always exist + hash(obj.clone(), self) } // https://docs.python.org/3/reference/expressions.html#membership-test-operations