Skip to content

Commit

Permalink
Add vectorcall benchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
ChayimFriedman2 committed Aug 19, 2024
1 parent 06401a6 commit a8179a9
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 19 deletions.
146 changes: 145 additions & 1 deletion pyo3-benches/benches/bench_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ use std::hint::black_box;

use codspeed_criterion_compat::{criterion_group, criterion_main, Bencher, Criterion};

use pyo3::prelude::*;
use pyo3::ffi::c_str;
use pyo3::prelude::*;
use pyo3::types::IntoPyDict;

macro_rules! test_module {
($py:ident, $code:literal) => {
Expand All @@ -26,6 +27,62 @@ fn bench_call_0(b: &mut Bencher<'_>) {
})
}

fn bench_call_1(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
let module = test_module!(py, "def foo(a, b, c): pass");

let foo_module = &module.getattr("foo").unwrap();
let args = (
<_ as IntoPy<PyObject>>::into_py(1, py).into_bound(py),
<_ as IntoPy<PyObject>>::into_py("s", py).into_bound(py),
<_ as IntoPy<PyObject>>::into_py(1.23, py).into_bound(py),
);

b.iter(|| {
for _ in 0..1000 {
black_box(foo_module).call1(args.clone()).unwrap();
}
});
})
}

fn bench_call(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
let module = test_module!(py, "def foo(a, b, c, d, e): pass");

let foo_module = &module.getattr("foo").unwrap();
let args = (
<_ as IntoPy<PyObject>>::into_py(1, py).into_bound(py),
<_ as IntoPy<PyObject>>::into_py("s", py).into_bound(py),
<_ as IntoPy<PyObject>>::into_py(1.23, py).into_bound(py),
);
let kwargs = [("d", 1), ("e", 42)].into_py_dict(py);

b.iter(|| {
for _ in 0..1000 {
black_box(foo_module)
.call(args.clone(), Some(&kwargs))
.unwrap();
}
});
})
}

fn bench_call_one_arg(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
let module = test_module!(py, "def foo(a): pass");

let foo_module = &module.getattr("foo").unwrap();
let arg = <_ as IntoPy<PyObject>>::into_py(1, py).into_bound(py);

b.iter(|| {
for _ in 0..1000 {
black_box(foo_module).call1((arg.clone(),)).unwrap();
}
});
})
}

fn bench_call_method_0(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
let module = test_module!(
Expand All @@ -47,9 +104,96 @@ class Foo:
})
}

fn bench_call_method_1(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
let module = test_module!(
py,
"
class Foo:
def foo(self, a, b, c):
pass
"
);

let foo_module = &module.getattr("Foo").unwrap().call0().unwrap();
let args = (
<_ as IntoPy<PyObject>>::into_py(1, py).into_bound(py),
<_ as IntoPy<PyObject>>::into_py("s", py).into_bound(py),
<_ as IntoPy<PyObject>>::into_py(1.23, py).into_bound(py),
);

b.iter(|| {
for _ in 0..1000 {
black_box(foo_module)
.call_method1("foo", args.clone())
.unwrap();
}
});
})
}

fn bench_call_method(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
let module = test_module!(
py,
"
class Foo:
def foo(self, a, b, c, d, e):
pass
"
);

let foo_module = &module.getattr("Foo").unwrap().call0().unwrap();
let args = (
<_ as IntoPy<PyObject>>::into_py(1, py).into_bound(py),
<_ as IntoPy<PyObject>>::into_py("s", py).into_bound(py),
<_ as IntoPy<PyObject>>::into_py(1.23, py).into_bound(py),
);
let kwargs = [("d", 1), ("e", 42)].into_py_dict(py);

b.iter(|| {
for _ in 0..1000 {
black_box(foo_module)
.call_method("foo", args.clone(), Some(&kwargs))
.unwrap();
}
});
})
}

fn bench_call_method_one_arg(b: &mut Bencher<'_>) {
Python::with_gil(|py| {
let module = test_module!(
py,
"
class Foo:
def foo(self, a):
pass
"
);

let foo_module = &module.getattr("Foo").unwrap().call0().unwrap();
let arg = <_ as IntoPy<PyObject>>::into_py(1, py).into_bound(py);

b.iter(|| {
for _ in 0..1000 {
black_box(foo_module)
.call_method1("foo", (arg.clone(),))
.unwrap();
}
});
})
}

fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("call_0", bench_call_0);
c.bench_function("call_1", bench_call_1);
c.bench_function("call", bench_call);
c.bench_function("call_one_arg", bench_call_one_arg);
c.bench_function("call_method_0", bench_call_method_0);
c.bench_function("call_method_1", bench_call_method_1);
c.bench_function("call_method", bench_call_method);
c.bench_function("call_method_one_arg", bench_call_method_one_arg);
}

criterion_group!(benches, criterion_benchmark);
Expand Down
13 changes: 12 additions & 1 deletion src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ pub trait IntoPy<T>: Sized {
self,
py: Python<'py>,
function: Borrowed<'_, 'py, PyAny>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>>
where
Self: IntoPy<Py<PyTuple>>,
Expand Down Expand Up @@ -213,6 +214,7 @@ pub trait IntoPy<T>: Sized {
py: Python<'py>,
function: Borrowed<'_, 'py, PyAny>,
kwargs: Option<Borrowed<'_, '_, PyDict>>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>>
where
Self: IntoPy<Py<PyTuple>>,
Expand Down Expand Up @@ -248,6 +250,7 @@ pub trait IntoPy<T>: Sized {
_py: Python<'py>,
object: Borrowed<'_, 'py, PyAny>,
method_name: Bound<'py, PyString>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>>
where
Self: IntoPy<Py<PyTuple>>,
Expand Down Expand Up @@ -593,6 +596,7 @@ impl IntoPy<Py<PyTuple>> for () {
self,
py: Python<'py>,
function: Borrowed<'_, 'py, PyAny>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>> {
unsafe {
cfg_if::cfg_if! {
Expand All @@ -616,6 +620,7 @@ impl IntoPy<Py<PyTuple>> for () {
py: Python<'py>,
function: Borrowed<'_, 'py, PyAny>,
kwargs: Option<Borrowed<'_, '_, PyDict>>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>> {
match kwargs {
Some(kwargs) => unsafe {
Expand All @@ -626,7 +631,12 @@ impl IntoPy<Py<PyTuple>> for () {
)
.assume_owned_or_err(py)
},
None => <Self as IntoPy<Py<PyTuple>>>::__py_call_vectorcall1(self, py, function),
None => <Self as IntoPy<Py<PyTuple>>>::__py_call_vectorcall1(
self,
py,
function,
private::Token,
),
}
}

Expand All @@ -637,6 +647,7 @@ impl IntoPy<Py<PyTuple>> for () {
_py: Python<'py>,
object: Borrowed<'_, 'py, PyAny>,
method_name: Bound<'py, PyString>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>> {
cfg_if::cfg_if! {
if #[cfg(all(Py_3_9, not(any(Py_LIMITED_API, PyPy, GraalPy))))] {
Expand Down
6 changes: 4 additions & 2 deletions src/types/any.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::class::basic::CompareOp;
use crate::conversion::{AsPyPointer, FromPyObjectBound, IntoPy, ToPyObject};
use crate::conversion::{private, AsPyPointer, FromPyObjectBound, IntoPy, ToPyObject};
use crate::err::{DowncastError, DowncastIntoError, PyErr, PyResult};
use crate::exceptions::{PyAttributeError, PyTypeError};
use crate::ffi_ptr_ext::FfiPtrExt;
Expand Down Expand Up @@ -1164,6 +1164,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
self.py(),
self.as_borrowed(),
kwargs.map(Bound::as_borrowed),
private::Token,
)
}

Expand All @@ -1175,7 +1176,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {

#[inline]
fn call1(&self, args: impl IntoPy<Py<PyTuple>>) -> PyResult<Bound<'py, PyAny>> {
args.__py_call_vectorcall1(self.py(), self.as_borrowed())
args.__py_call_vectorcall1(self.py(), self.as_borrowed(), private::Token)
}

#[inline]
Expand Down Expand Up @@ -1217,6 +1218,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
self.py(),
self.as_borrowed(),
name.into_py(self.py()).into_bound(self.py()),
private::Token,
)
}

Expand Down
31 changes: 16 additions & 15 deletions src/types/tuple.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::iter::FusedIterator;

use crate::conversion::private;
use crate::ffi::{self, Py_ssize_t};
use crate::ffi_ptr_ext::FfiPtrExt;
#[cfg(feature = "experimental-inspect")]
Expand Down Expand Up @@ -543,6 +544,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
self,
py: Python<'py>,
function: Borrowed<'_, 'py, PyAny>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>> {
cfg_if::cfg_if! {
if #[cfg(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API))))] {
Expand Down Expand Up @@ -577,24 +579,22 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
py: Python<'py>,
function: Borrowed<'_, 'py, PyAny>,
kwargs: Option<Borrowed<'_, '_, PyDict>>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>> {
cfg_if::cfg_if! {
if #[cfg(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API))))] {
match kwargs {
Some(kwargs) => unsafe {
// We need this to drop the arguments correctly.
let args_bound = [$(self.$n.into_py(py).into_bound(py),)*];
// Prepend one null argument for `PY_VECTORCALL_ARGUMENTS_OFFSET`.
let mut args = [std::ptr::null_mut(), $(args_bound[$n].as_ptr()),*];
ffi::PyObject_VectorcallDict(
function.as_ptr(),
args.as_mut_ptr().add(1),
$length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
kwargs.as_ptr(),
)
.assume_owned_or_err(py)
},
None => <Self as IntoPy<Py<PyTuple>>>::__py_call_vectorcall1(self, py, function),
// We need this to drop the arguments correctly.
let args_bound = [$(self.$n.into_py(py).into_bound(py),)*];
// Prepend one null argument for `PY_VECTORCALL_ARGUMENTS_OFFSET`.
let mut args = [std::ptr::null_mut(), $(args_bound[$n].as_ptr()),*];
unsafe {
ffi::PyObject_VectorcallDict(
function.as_ptr(),
args.as_mut_ptr().add(1),
$length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
kwargs.map_or_else(std::ptr::null_mut, |kwargs| kwargs.as_ptr()),
)
.assume_owned_or_err(py)
}
} else {
function.call(<Self as IntoPy<Py<PyTuple>>>::into_py(self, py).into_bound(py), kwargs.as_deref())
Expand All @@ -608,6 +608,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
py: Python<'py>,
object: Borrowed<'_, 'py, PyAny>,
method_name: Bound<'py, PyString>,
_: private::Token,
) -> PyResult<Bound<'py, PyAny>> {
cfg_if::cfg_if! {
if #[cfg(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API))))] {
Expand Down

0 comments on commit a8179a9

Please sign in to comment.