optimize `float` -> `f64` conversions on non-abi3
This commit is contained in:
parent
b88e1ff713
commit
e2c88d5a4e
|
@ -2,7 +2,7 @@ use criterion::{black_box, criterion_group, criterion_main, Bencher, Criterion};
|
|||
|
||||
use pyo3::{
|
||||
prelude::*,
|
||||
types::{PyList, PyString},
|
||||
types::{PyFloat, PyList, PyString},
|
||||
};
|
||||
|
||||
#[derive(FromPyObject)]
|
||||
|
@ -79,6 +79,15 @@ fn not_a_list_via_extract_enum(b: &mut Bencher<'_>) {
|
|||
})
|
||||
}
|
||||
|
||||
fn f64_from_pyobject(b: &mut Bencher<'_>) {
|
||||
Python::with_gil(|py| {
|
||||
let obj = PyFloat::new(py, 1.234);
|
||||
b.iter(|| {
|
||||
let _: f64 = obj.extract().unwrap();
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
c.bench_function("enum_from_pyobject", enum_from_pyobject);
|
||||
c.bench_function("list_via_downcast", list_via_downcast);
|
||||
|
@ -86,6 +95,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
|||
c.bench_function("not_a_list_via_downcast", not_a_list_via_downcast);
|
||||
c.bench_function("not_a_list_via_extract", not_a_list_via_extract);
|
||||
c.bench_function("not_a_list_via_extract_enum", not_a_list_via_extract_enum);
|
||||
c.bench_function("f64_from_pyobject", f64_from_pyobject);
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Optimize conversion of `float` to `f64` (and `PyFloat::value`) on non-abi3 builds.
|
|
@ -0,0 +1,27 @@
|
|||
use crate::{PyFloat_Check, PyObject};
|
||||
use std::os::raw::c_double;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PyFloatObject {
|
||||
pub ob_base: PyObject,
|
||||
pub ob_fval: c_double,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn _PyFloat_CAST(op: *mut PyObject) -> *mut PyFloatObject {
|
||||
debug_assert_eq!(PyFloat_Check(op), 1);
|
||||
op.cast()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn PyFloat_AS_DOUBLE(op: *mut PyObject) -> c_double {
|
||||
(*_PyFloat_CAST(op)).ob_fval
|
||||
}
|
||||
|
||||
// skipped PyFloat_Pack2
|
||||
// skipped PyFloat_Pack4
|
||||
// skipped PyFloat_Pack8
|
||||
|
||||
// skipped PyFloat_Unpack2
|
||||
// skipped PyFloat_Unpack4
|
||||
// skipped PyFloat_Unpack8
|
|
@ -29,6 +29,7 @@ pub(crate) mod pymem;
|
|||
pub(crate) mod pystate;
|
||||
pub(crate) mod pythonrun;
|
||||
// skipped sysmodule.h
|
||||
pub(crate) mod floatobject;
|
||||
pub(crate) mod tupleobject;
|
||||
pub(crate) mod unicodeobject;
|
||||
pub(crate) mod weakrefobject;
|
||||
|
@ -42,6 +43,7 @@ pub use self::compile::*;
|
|||
pub use self::descrobject::*;
|
||||
#[cfg(not(PyPy))]
|
||||
pub use self::dictobject::*;
|
||||
pub use self::floatobject::*;
|
||||
pub use self::frameobject::*;
|
||||
pub use self::funcobject::*;
|
||||
pub use self::genobject::*;
|
||||
|
|
|
@ -6,13 +6,6 @@ use std::ptr::addr_of_mut;
|
|||
// TODO: remove (see https://github.com/PyO3/pyo3/pull/1341#issuecomment-751515985)
|
||||
opaque_struct!(PyFloatObject);
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[repr(C)]
|
||||
pub struct PyFloatObject {
|
||||
pub ob_base: PyObject,
|
||||
pub ob_fval: c_double,
|
||||
}
|
||||
|
||||
#[cfg_attr(windows, link(name = "pythonXY"))]
|
||||
extern "C" {
|
||||
#[cfg_attr(PyPy, link_name = "PyPyFloat_Type")]
|
||||
|
@ -44,12 +37,6 @@ extern "C" {
|
|||
pub fn PyFloat_AsDouble(arg1: *mut PyObject) -> c_double;
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[inline]
|
||||
pub unsafe fn PyFloat_AS_DOUBLE(op: *mut PyObject) -> c_double {
|
||||
(*(op as *mut PyFloatObject)).ob_fval
|
||||
}
|
||||
|
||||
// skipped non-limited _PyFloat_Pack2
|
||||
// skipped non-limited _PyFloat_Pack4
|
||||
// skipped non-limited _PyFloat_Pack8
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#[cfg(feature = "experimental-inspect")]
|
||||
use crate::inspect::types::TypeInfo;
|
||||
use crate::{
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject,
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult, Python,
|
||||
ToPyObject,
|
||||
};
|
||||
use std::os::raw::c_double;
|
||||
|
||||
|
@ -29,7 +30,16 @@ impl PyFloat {
|
|||
|
||||
/// Gets the value of this float.
|
||||
pub fn value(&self) -> c_double {
|
||||
unsafe { ffi::PyFloat_AsDouble(self.as_ptr()) }
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
unsafe {
|
||||
// Safety: self is PyFloat object
|
||||
ffi::PyFloat_AS_DOUBLE(self.as_ptr())
|
||||
}
|
||||
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
unsafe {
|
||||
ffi::PyFloat_AsDouble(self.as_ptr())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,6 +64,15 @@ impl<'source> FromPyObject<'source> for f64 {
|
|||
// PyFloat_AsDouble returns -1.0 upon failure
|
||||
#![allow(clippy::float_cmp)]
|
||||
fn extract(obj: &'source PyAny) -> PyResult<Self> {
|
||||
// On non-limited API, .value() uses PyFloat_AS_DOUBLE which
|
||||
// allows us to have an optimized fast path for the case when
|
||||
// we have exactly a `float` object (it's not worth going through
|
||||
// `isinstance` machinery for subclasses).
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
if let Ok(float) = obj.downcast_exact::<PyFloat>() {
|
||||
return Ok(float.value());
|
||||
}
|
||||
|
||||
let v = unsafe { ffi::PyFloat_AsDouble(obj.as_ptr()) };
|
||||
|
||||
if v == -1.0 {
|
||||
|
@ -101,11 +120,7 @@ impl<'source> FromPyObject<'source> for f32 {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
use crate::ffi::PyFloat_AS_DOUBLE;
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
use crate::AsPyPointer;
|
||||
use crate::{Python, ToPyObject};
|
||||
use crate::{types::PyFloat, Python, ToPyObject};
|
||||
|
||||
macro_rules! num_to_py_object_and_back (
|
||||
($func_name:ident, $t1:ty, $t2:ty) => (
|
||||
|
@ -127,15 +142,14 @@ mod tests {
|
|||
num_to_py_object_and_back!(to_from_f32, f32, f32);
|
||||
num_to_py_object_and_back!(int_to_float, i32, f64);
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[test]
|
||||
fn test_as_double_macro() {
|
||||
fn test_float_value() {
|
||||
use assert_approx_eq::assert_approx_eq;
|
||||
|
||||
Python::with_gil(|py| {
|
||||
let v = 1.23f64;
|
||||
let obj = v.to_object(py);
|
||||
assert_approx_eq!(v, unsafe { PyFloat_AS_DOUBLE(obj.as_ptr()) });
|
||||
let obj = PyFloat::new(py, 1.23);
|
||||
assert_approx_eq!(v, obj.value());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue