Skip to content

Commit 0608fa9

Browse files
committed
reintroduce vectorcall optimization with new PyCallArgs trait
1 parent 77202aa commit 0608fa9

File tree

4 files changed

+401
-29
lines changed

4 files changed

+401
-29
lines changed

src/call.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
//! TODO
2+
//!
3+
//!
4+
5+
use crate::ffi_ptr_ext::FfiPtrExt as _;
6+
use crate::types::{PyAnyMethods as _, PyDict, PyString, PyTuple};
7+
use crate::{ffi, Borrowed, Bound, IntoPyObjectExt as _, Py, PyAny, PyResult};
8+
9+
pub(crate) mod private {
10+
use super::*;
11+
12+
pub trait Sealed {}
13+
14+
impl Sealed for () {}
15+
impl Sealed for Bound<'_, PyTuple> {}
16+
impl Sealed for Py<PyTuple> {}
17+
18+
pub struct Token;
19+
}
20+
21+
/// TODO
22+
pub trait PyCallArgs<'py>: Sized + private::Sealed {
23+
#[doc(hidden)]
24+
fn call(
25+
self,
26+
function: Borrowed<'_, 'py, PyAny>,
27+
kwargs: Borrowed<'_, 'py, PyDict>,
28+
token: private::Token,
29+
) -> PyResult<Bound<'py, PyAny>>;
30+
31+
#[doc(hidden)]
32+
fn call_positional(
33+
self,
34+
function: Borrowed<'_, 'py, PyAny>,
35+
token: private::Token,
36+
) -> PyResult<Bound<'py, PyAny>>;
37+
38+
#[doc(hidden)]
39+
fn call_method_positional(
40+
self,
41+
object: Borrowed<'_, 'py, PyAny>,
42+
method_name: Borrowed<'_, 'py, PyString>,
43+
_: private::Token,
44+
) -> PyResult<Bound<'py, PyAny>> {
45+
object
46+
.getattr(method_name)
47+
.and_then(|method| method.call1(self))
48+
}
49+
}
50+
51+
impl<'py> PyCallArgs<'py> for () {
52+
fn call(
53+
self,
54+
function: Borrowed<'_, 'py, PyAny>,
55+
kwargs: Borrowed<'_, 'py, PyDict>,
56+
token: private::Token,
57+
) -> PyResult<Bound<'py, PyAny>> {
58+
let args = self.into_pyobject_or_pyerr(function.py())?;
59+
args.call(function, kwargs, token)
60+
}
61+
62+
fn call_positional(
63+
self,
64+
function: Borrowed<'_, 'py, PyAny>,
65+
token: private::Token,
66+
) -> PyResult<Bound<'py, PyAny>> {
67+
let args = self.into_pyobject_or_pyerr(function.py())?;
68+
args.call_positional(function, token)
69+
}
70+
}
71+
72+
impl<'py> PyCallArgs<'py> for Bound<'py, PyTuple> {
73+
fn call(
74+
self,
75+
function: Borrowed<'_, 'py, PyAny>,
76+
kwargs: Borrowed<'_, '_, PyDict>,
77+
_: private::Token,
78+
) -> PyResult<Bound<'py, PyAny>> {
79+
unsafe {
80+
ffi::PyObject_Call(function.as_ptr(), self.as_ptr(), kwargs.as_ptr())
81+
.assume_owned_or_err(function.py())
82+
}
83+
}
84+
85+
fn call_positional(
86+
self,
87+
function: Borrowed<'_, 'py, PyAny>,
88+
_: private::Token,
89+
) -> PyResult<Bound<'py, PyAny>> {
90+
unsafe {
91+
ffi::PyObject_Call(function.as_ptr(), self.as_ptr(), std::ptr::null_mut())
92+
.assume_owned_or_err(function.py())
93+
}
94+
}
95+
}
96+
97+
impl<'py> PyCallArgs<'py> for Py<PyTuple> {
98+
fn call(
99+
self,
100+
function: Borrowed<'_, 'py, PyAny>,
101+
kwargs: Borrowed<'_, '_, PyDict>,
102+
_: private::Token,
103+
) -> PyResult<Bound<'py, PyAny>> {
104+
unsafe {
105+
ffi::PyObject_Call(function.as_ptr(), self.as_ptr(), kwargs.as_ptr())
106+
.assume_owned_or_err(function.py())
107+
}
108+
}
109+
110+
fn call_positional(
111+
self,
112+
function: Borrowed<'_, 'py, PyAny>,
113+
_: private::Token,
114+
) -> PyResult<Bound<'py, PyAny>> {
115+
unsafe {
116+
ffi::PyObject_Call(function.as_ptr(), self.as_ptr(), std::ptr::null_mut())
117+
.assume_owned_or_err(function.py())
118+
}
119+
}
120+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,7 @@ mod internal_tricks;
428428
mod internal;
429429

430430
pub mod buffer;
431+
pub mod call;
431432
pub mod conversion;
432433
mod conversions;
433434
#[cfg(feature = "experimental-async")]

src/types/any.rs

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::call::PyCallArgs;
12
use crate::class::basic::CompareOp;
23
use crate::conversion::{AsPyPointer, FromPyObjectBound, IntoPyObject};
34
use crate::err::{DowncastError, DowncastIntoError, PyErr, PyResult};
@@ -10,7 +11,7 @@ use crate::py_result_ext::PyResultExt;
1011
use crate::type_object::{PyTypeCheck, PyTypeInfo};
1112
#[cfg(not(any(PyPy, GraalPy)))]
1213
use crate::types::PySuper;
13-
use crate::types::{PyDict, PyIterator, PyList, PyString, PyTuple, PyType};
14+
use crate::types::{PyDict, PyIterator, PyList, PyString, PyType};
1415
use crate::{err, ffi, Borrowed, BoundObject, IntoPyObjectExt, Python};
1516
use std::cell::UnsafeCell;
1617
use std::cmp::Ordering;
@@ -436,7 +437,7 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
436437
/// ```
437438
fn call<A>(&self, args: A, kwargs: Option<&Bound<'py, PyDict>>) -> PyResult<Bound<'py, PyAny>>
438439
where
439-
A: IntoPyObject<'py, Target = PyTuple>;
440+
A: PyCallArgs<'py>;
440441

441442
/// Calls the object without arguments.
442443
///
@@ -491,7 +492,7 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
491492
/// ```
492493
fn call1<A>(&self, args: A) -> PyResult<Bound<'py, PyAny>>
493494
where
494-
A: IntoPyObject<'py, Target = PyTuple>;
495+
A: PyCallArgs<'py>;
495496

496497
/// Calls a method on the object.
497498
///
@@ -538,7 +539,7 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
538539
) -> PyResult<Bound<'py, PyAny>>
539540
where
540541
N: IntoPyObject<'py, Target = PyString>,
541-
A: IntoPyObject<'py, Target = PyTuple>;
542+
A: PyCallArgs<'py>;
542543

543544
/// Calls a method on the object without arguments.
544545
///
@@ -614,7 +615,7 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
614615
fn call_method1<N, A>(&self, name: N, args: A) -> PyResult<Bound<'py, PyAny>>
615616
where
616617
N: IntoPyObject<'py, Target = PyString>,
617-
A: IntoPyObject<'py, Target = PyTuple>;
618+
A: PyCallArgs<'py>;
618619

619620
/// Returns whether the object is considered to be true.
620621
///
@@ -1209,25 +1210,17 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
12091210

12101211
fn call<A>(&self, args: A, kwargs: Option<&Bound<'py, PyDict>>) -> PyResult<Bound<'py, PyAny>>
12111212
where
1212-
A: IntoPyObject<'py, Target = PyTuple>,
1213+
A: PyCallArgs<'py>,
12131214
{
1214-
fn inner<'py>(
1215-
any: &Bound<'py, PyAny>,
1216-
args: Borrowed<'_, 'py, PyTuple>,
1217-
kwargs: Option<&Bound<'py, PyDict>>,
1218-
) -> PyResult<Bound<'py, PyAny>> {
1219-
unsafe {
1220-
ffi::PyObject_Call(
1221-
any.as_ptr(),
1222-
args.as_ptr(),
1223-
kwargs.map_or(std::ptr::null_mut(), |dict| dict.as_ptr()),
1224-
)
1225-
.assume_owned_or_err(any.py())
1226-
}
1215+
if let Some(kwargs) = kwargs {
1216+
args.call(
1217+
self.as_borrowed(),
1218+
kwargs.as_borrowed(),
1219+
crate::call::private::Token,
1220+
)
1221+
} else {
1222+
args.call_positional(self.as_borrowed(), crate::call::private::Token)
12271223
}
1228-
1229-
let py = self.py();
1230-
inner(self, args.into_pyobject_or_pyerr(py)?.as_borrowed(), kwargs)
12311224
}
12321225

12331226
#[inline]
@@ -1237,9 +1230,9 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
12371230

12381231
fn call1<A>(&self, args: A) -> PyResult<Bound<'py, PyAny>>
12391232
where
1240-
A: IntoPyObject<'py, Target = PyTuple>,
1233+
A: PyCallArgs<'py>,
12411234
{
1242-
self.call(args, None)
1235+
args.call_positional(self.as_borrowed(), crate::call::private::Token)
12431236
}
12441237

12451238
#[inline]
@@ -1251,10 +1244,14 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
12511244
) -> PyResult<Bound<'py, PyAny>>
12521245
where
12531246
N: IntoPyObject<'py, Target = PyString>,
1254-
A: IntoPyObject<'py, Target = PyTuple>,
1247+
A: PyCallArgs<'py>,
12551248
{
1256-
self.getattr(name)
1257-
.and_then(|method| method.call(args, kwargs))
1249+
if kwargs.is_none() {
1250+
self.call_method1(name, args)
1251+
} else {
1252+
self.getattr(name)
1253+
.and_then(|method| method.call(args, kwargs))
1254+
}
12581255
}
12591256

12601257
#[inline]
@@ -1273,9 +1270,14 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
12731270
fn call_method1<N, A>(&self, name: N, args: A) -> PyResult<Bound<'py, PyAny>>
12741271
where
12751272
N: IntoPyObject<'py, Target = PyString>,
1276-
A: IntoPyObject<'py, Target = PyTuple>,
1273+
A: PyCallArgs<'py>,
12771274
{
1278-
self.call_method(name, args, None)
1275+
let name = name.into_pyobject_or_pyerr(self.py())?;
1276+
args.call_method_positional(
1277+
self.as_borrowed(),
1278+
name.as_borrowed(),
1279+
crate::call::private::Token,
1280+
)
12791281
}
12801282

12811283
fn is_truthy(&self) -> PyResult<bool> {

0 commit comments

Comments
 (0)