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::{
|
use pyo3::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
types::{PyList, PyString},
|
types::{PyFloat, PyList, PyString},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(FromPyObject)]
|
#[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) {
|
fn criterion_benchmark(c: &mut Criterion) {
|
||||||
c.bench_function("enum_from_pyobject", enum_from_pyobject);
|
c.bench_function("enum_from_pyobject", enum_from_pyobject);
|
||||||
c.bench_function("list_via_downcast", list_via_downcast);
|
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_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", not_a_list_via_extract);
|
||||||
c.bench_function("not_a_list_via_extract_enum", not_a_list_via_extract_enum);
|
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);
|
criterion_group!(benches, criterion_benchmark);
|
||||||
|
|
1
newsfragments/3345.changed.md
Normal file
1
newsfragments/3345.changed.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Optimize conversion of `float` to `f64` (and `PyFloat::value`) on non-abi3 builds.
|
27
pyo3-ffi/src/cpython/floatobject.rs
Normal file
27
pyo3-ffi/src/cpython/floatobject.rs
Normal file
|
@ -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 pystate;
|
||||||
pub(crate) mod pythonrun;
|
pub(crate) mod pythonrun;
|
||||||
// skipped sysmodule.h
|
// skipped sysmodule.h
|
||||||
|
pub(crate) mod floatobject;
|
||||||
pub(crate) mod tupleobject;
|
pub(crate) mod tupleobject;
|
||||||
pub(crate) mod unicodeobject;
|
pub(crate) mod unicodeobject;
|
||||||
pub(crate) mod weakrefobject;
|
pub(crate) mod weakrefobject;
|
||||||
|
@ -42,6 +43,7 @@ pub use self::compile::*;
|
||||||
pub use self::descrobject::*;
|
pub use self::descrobject::*;
|
||||||
#[cfg(not(PyPy))]
|
#[cfg(not(PyPy))]
|
||||||
pub use self::dictobject::*;
|
pub use self::dictobject::*;
|
||||||
|
pub use self::floatobject::*;
|
||||||
pub use self::frameobject::*;
|
pub use self::frameobject::*;
|
||||||
pub use self::funcobject::*;
|
pub use self::funcobject::*;
|
||||||
pub use self::genobject::*;
|
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)
|
// TODO: remove (see https://github.com/PyO3/pyo3/pull/1341#issuecomment-751515985)
|
||||||
opaque_struct!(PyFloatObject);
|
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"))]
|
#[cfg_attr(windows, link(name = "pythonXY"))]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[cfg_attr(PyPy, link_name = "PyPyFloat_Type")]
|
#[cfg_attr(PyPy, link_name = "PyPyFloat_Type")]
|
||||||
|
@ -44,12 +37,6 @@ extern "C" {
|
||||||
pub fn PyFloat_AsDouble(arg1: *mut PyObject) -> c_double;
|
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_Pack2
|
||||||
// skipped non-limited _PyFloat_Pack4
|
// skipped non-limited _PyFloat_Pack4
|
||||||
// skipped non-limited _PyFloat_Pack8
|
// skipped non-limited _PyFloat_Pack8
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#[cfg(feature = "experimental-inspect")]
|
#[cfg(feature = "experimental-inspect")]
|
||||||
use crate::inspect::types::TypeInfo;
|
use crate::inspect::types::TypeInfo;
|
||||||
use crate::{
|
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;
|
use std::os::raw::c_double;
|
||||||
|
|
||||||
|
@ -29,7 +30,16 @@ impl PyFloat {
|
||||||
|
|
||||||
/// Gets the value of this float.
|
/// Gets the value of this float.
|
||||||
pub fn value(&self) -> c_double {
|
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
|
// PyFloat_AsDouble returns -1.0 upon failure
|
||||||
#![allow(clippy::float_cmp)]
|
#![allow(clippy::float_cmp)]
|
||||||
fn extract(obj: &'source PyAny) -> PyResult<Self> {
|
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()) };
|
let v = unsafe { ffi::PyFloat_AsDouble(obj.as_ptr()) };
|
||||||
|
|
||||||
if v == -1.0 {
|
if v == -1.0 {
|
||||||
|
@ -101,11 +120,7 @@ impl<'source> FromPyObject<'source> for f32 {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
use crate::{types::PyFloat, Python, ToPyObject};
|
||||||
use crate::ffi::PyFloat_AS_DOUBLE;
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
|
||||||
use crate::AsPyPointer;
|
|
||||||
use crate::{Python, ToPyObject};
|
|
||||||
|
|
||||||
macro_rules! num_to_py_object_and_back (
|
macro_rules! num_to_py_object_and_back (
|
||||||
($func_name:ident, $t1:ty, $t2:ty) => (
|
($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!(to_from_f32, f32, f32);
|
||||||
num_to_py_object_and_back!(int_to_float, i32, f64);
|
num_to_py_object_and_back!(int_to_float, i32, f64);
|
||||||
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_as_double_macro() {
|
fn test_float_value() {
|
||||||
use assert_approx_eq::assert_approx_eq;
|
use assert_approx_eq::assert_approx_eq;
|
||||||
|
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
let v = 1.23f64;
|
let v = 1.23f64;
|
||||||
let obj = v.to_object(py);
|
let obj = PyFloat::new(py, 1.23);
|
||||||
assert_approx_eq!(v, unsafe { PyFloat_AS_DOUBLE(obj.as_ptr()) });
|
assert_approx_eq!(v, obj.value());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue