performance: use vectorcall for call0 and call_method0
This commit is contained in:
parent
d1248d5743
commit
87bacf1be5
|
@ -20,7 +20,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
- Add context.h functions (`PyContext_New`, etc) to FFI. [#1259](https://github.com/PyO3/pyo3/pull/1259)
|
||||
- Add `PyAny::is_instance()` method. [#1276](https://github.com/PyO3/pyo3/pull/1276)
|
||||
- Add support for conversion between `char` and `PyString`. [#1282](https://github.com/PyO3/pyo3/pull/1282)
|
||||
- Add FFI definitions for `PyBuffer_SizeFromFormat`, `PyObject_LengthHint`, `PyObject_CallNoArgs`, `PyObject_CallOneArg`, `PyObject_CallMethodNoArgs`, `PyObject_CallMethodOneArg`, `PyObject_VectorcallDict`, and `PyObject_VectorcallMethod`. [#1285](https://github.com/PyO3/pyo3/pull/1285)
|
||||
- Add FFI definitions for `PyBuffer_SizeFromFormat`, `PyObject_LengthHint`, `PyObject_CallNoArgs`, `PyObject_CallOneArg`, `PyObject_CallMethodNoArgs`, `PyObject_CallMethodOneArg`, `PyObject_VectorcallDict`, and `PyObject_VectorcallMethod`. [#1287](https://github.com/PyO3/pyo3/pull/1287)
|
||||
|
||||
### Changed
|
||||
- Change return type `PyType::name()` from `Cow<str>` to `PyResult<&str>`. [#1152](https://github.com/PyO3/pyo3/pull/1152)
|
||||
|
@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
- Change `Debug` and `Display` impls for `PyException` to be consistent with `PyAny`. [#1275](https://github.com/PyO3/pyo3/pull/1275)
|
||||
- Change `Debug` impl of `PyErr` to output more helpful information (acquiring the GIL if necessary). [#1275](https://github.com/PyO3/pyo3/pull/1275)
|
||||
- Rename `PyTypeInfo::is_instance` and `PyTypeInfo::is_exact_instance` to `PyTypeInfo::is_type_of` and `PyTypeInfo::is_exact_type_of`. [#1278](https://github.com/PyO3/pyo3/pull/1278)
|
||||
- Optimize `PyAny::call0`, `Py::call0` and `PyAny::call_method0` and `Py::call_method0` on Python 3.9 and up. [#1287](https://github.com/PyO3/pyo3/pull/1285)
|
||||
- Deprecate `Python::is_instance`, `Python::is_subclass`, `Python::release`, and `Python::xdecref`. [#1292](https://github.com/PyO3/pyo3/pull/1292)
|
||||
|
||||
### Removed
|
||||
|
@ -39,8 +40,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
### Fixed
|
||||
- Fix missing field in `PyCodeObject` struct (`co_posonlyargcount`) - caused invalid access to other fields in Python >3.7. [#1260](https://github.com/PyO3/pyo3/pull/1260)
|
||||
- Fix building for `x86_64-unknown-linux-musl` target from `x86_65-unknown-linux-gnu` host. [#1267](https://github.com/PyO3/pyo3/pull/1267)
|
||||
- Fix FFI definitions for `PyObject_Vectorcall` and `PyVectorcall_Call`. [#1285](https://github.com/PyO3/pyo3/pull/1285)
|
||||
- Fix `#[text_signature]` interacting badly with rust `r#raw_identifiers`. [#1286](https://github.com/PyO3/pyo3/pull/1286)
|
||||
- Fix FFI definitions for `PyObject_Vectorcall` and `PyVectorcall_Call`. [#1287](https://github.com/PyO3/pyo3/pull/1285)
|
||||
- Fix building with Anaconda python inside a virtualenv. [#1290](https://github.com/PyO3/pyo3/pull/1290)
|
||||
|
||||
## [0.12.3] - 2020-10-12
|
||||
|
|
|
@ -15,6 +15,7 @@ build = "build.rs"
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
cfg-if = { version = "1.0" }
|
||||
ctor = { version = "0.1", optional = true }
|
||||
indoc = { version = "1.0.3", optional = true }
|
||||
inventory = { version = "0.1.4", optional = true }
|
||||
|
|
|
@ -272,7 +272,18 @@ impl<T> Py<T> {
|
|||
///
|
||||
/// This is equivalent to the Python expression `self()`.
|
||||
pub fn call0(&self, py: Python) -> PyResult<PyObject> {
|
||||
self.call(py, (), None)
|
||||
cfg_if::cfg_if! {
|
||||
// TODO: Use PyObject_CallNoArgs instead after https://bugs.python.org/issue42415.
|
||||
// Once the issue is resolved, we can enable this optimization for limited API.
|
||||
if #[cfg(all(Py_3_9, not(Py_LIMITED_API)))] {
|
||||
// Optimized path on python 3.9+
|
||||
unsafe {
|
||||
PyObject::from_owned_ptr_or_err(py, ffi::_PyObject_CallNoArg(self.as_ptr()))
|
||||
}
|
||||
} else {
|
||||
self.call(py, (), None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls a method on the object.
|
||||
|
@ -316,7 +327,17 @@ impl<T> Py<T> {
|
|||
///
|
||||
/// This is equivalent to the Python expression `self.name()`.
|
||||
pub fn call_method0(&self, py: Python, name: &str) -> PyResult<PyObject> {
|
||||
self.call_method(py, name, (), None)
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(Py_3_9, not(Py_LIMITED_API)))] {
|
||||
// Optimized path on python 3.9+
|
||||
unsafe {
|
||||
let name = name.into_py(py);
|
||||
PyObject::from_owned_ptr_or_err(py, ffi::PyObject_CallMethodNoArgs(self.as_ptr(), name.as_ptr()))
|
||||
}
|
||||
} else {
|
||||
self.call_method(py, name, (), None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `Py<T>` instance by taking ownership of the given FFI pointer.
|
||||
|
|
|
@ -226,7 +226,18 @@ impl PyAny {
|
|||
///
|
||||
/// This is equivalent to the Python expression `self()`.
|
||||
pub fn call0(&self) -> PyResult<&PyAny> {
|
||||
self.call((), None)
|
||||
cfg_if::cfg_if! {
|
||||
// TODO: Use PyObject_CallNoArgs instead after https://bugs.python.org/issue42415.
|
||||
// Once the issue is resolved, we can enable this optimization for limited API.
|
||||
if #[cfg(all(Py_3_9, not(Py_LIMITED_API)))] {
|
||||
// Optimized path on python 3.9+
|
||||
unsafe {
|
||||
self.py().from_owned_ptr_or_err(ffi::_PyObject_CallNoArg(self.as_ptr()))
|
||||
}
|
||||
} else {
|
||||
self.call((), None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls the object with only positional arguments.
|
||||
|
@ -283,7 +294,17 @@ impl PyAny {
|
|||
///
|
||||
/// This is equivalent to the Python expression `self.name()`.
|
||||
pub fn call_method0(&self, name: &str) -> PyResult<&PyAny> {
|
||||
self.call_method(name, (), None)
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(Py_3_9, not(Py_LIMITED_API)))] {
|
||||
// Optimized path on python 3.9+
|
||||
unsafe {
|
||||
let name = name.into_py(self.py());
|
||||
self.py().from_owned_ptr_or_err(ffi::PyObject_CallMethodNoArgs(self.as_ptr(), name.as_ptr()))
|
||||
}
|
||||
} else {
|
||||
self.call_method(name, (), None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls a method on the object with only positional arguments.
|
||||
|
@ -464,8 +485,17 @@ impl PyAny {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::types::{IntoPyDict, PyList, PyLong};
|
||||
use crate::{Python, ToPyObject};
|
||||
use crate::{
|
||||
types::{IntoPyDict, PyList, PyLong, PyModule},
|
||||
Python, ToPyObject,
|
||||
};
|
||||
|
||||
macro_rules! test_module {
|
||||
($py:ident, $code:literal) => {
|
||||
PyModule::from_code($py, indoc::indoc!($code), file!(), "test_module")
|
||||
.expect("module creation failed")
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_call_for_non_existing_method() {
|
||||
|
@ -488,6 +518,30 @@ mod test {
|
|||
assert_eq!(list.extract::<Vec<i32>>(py).unwrap(), vec![7, 6, 5, 4, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_call_method0() {
|
||||
Python::with_gil(|py| {
|
||||
let module = test_module!(
|
||||
py,
|
||||
r#"
|
||||
class SimpleClass:
|
||||
def foo(self):
|
||||
return 42
|
||||
"#
|
||||
);
|
||||
|
||||
let simple_class = module.getattr("SimpleClass").unwrap().call0().unwrap();
|
||||
assert_eq!(
|
||||
simple_class
|
||||
.call_method0("foo")
|
||||
.unwrap()
|
||||
.extract::<u32>()
|
||||
.unwrap(),
|
||||
42
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
|
Loading…
Reference in New Issue