Skip to content

Commit

Permalink
Avoid overhead of creating a PyErr during downcasting.
Browse files Browse the repository at this point in the history
  • Loading branch information
adamreichold committed Apr 25, 2022
1 parent fb8d706 commit 3795010
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 26 deletions.
59 changes: 33 additions & 26 deletions src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::cold;
use crate::convert::{ArrayExt, IntoPyArray, NpyIndex, ToNpyDims, ToPyArray};
use crate::dtype::{Element, PyArrayDescr};
use crate::error::{
BorrowError, DimensionalityError, FromVecError, NotContiguousError, TypeError,
BorrowError, DimensionalityError, FromVecError, IgnoreError, NotContiguousError, TypeError,
DIMENSIONALITY_MISMATCH_ERR, MAX_DIMENSIONALITY_ERR,
};
use crate::npyffi::{self, npy_intp, NPY_ORDER, PY_ARRAY_API};
Expand Down Expand Up @@ -131,7 +131,7 @@ unsafe impl<T: Element, D: Dimension> PyTypeInfo for PyArray<T, D> {
}

fn is_type_of(ob: &PyAny) -> bool {
<&Self>::extract(ob).is_ok()
Self::extract::<IgnoreError>(ob).is_ok()
}
}

Expand All @@ -145,30 +145,7 @@ impl<T, D> IntoPy<PyObject> for PyArray<T, D> {

impl<'py, T: Element, D: Dimension> FromPyObject<'py> for &'py PyArray<T, D> {
fn extract(ob: &'py PyAny) -> PyResult<Self> {
// Check if the object is an array.
let array = unsafe {
if npyffi::PyArray_Check(ob.py(), ob.as_ptr()) == 0 {
return Err(PyDowncastError::new(ob, PyArray::<T, D>::NAME).into());
}
&*(ob as *const PyAny as *const PyArray<T, D>)
};

// Check if the dimensionality matches `D`.
let src_ndim = array.ndim();
if let Some(dst_ndim) = D::NDIM {
if src_ndim != dst_ndim {
return Err(DimensionalityError::new(src_ndim, dst_ndim).into());
}
}

// Check if the element type matches `T`.
let src_dtype = array.dtype();
let dst_dtype = T::get_dtype(ob.py());
if !src_dtype.is_equiv_to(dst_dtype) {
return Err(TypeError::new(src_dtype, dst_dtype).into());
}

Ok(array)
PyArray::extract(ob)
}
}

Expand Down Expand Up @@ -390,6 +367,36 @@ impl<T, D> PyArray<T, D> {
}

impl<T: Element, D: Dimension> PyArray<T, D> {
fn extract<'py, E>(ob: &'py PyAny) -> Result<&'py Self, E>
where
E: From<PyDowncastError<'py>> + From<DimensionalityError> + From<TypeError<'py>>,
{
// Check if the object is an array.
let array = unsafe {
if npyffi::PyArray_Check(ob.py(), ob.as_ptr()) == 0 {
return Err(PyDowncastError::new(ob, Self::NAME).into());
}
&*(ob as *const PyAny as *const Self)
};

// Check if the dimensionality matches `D`.
let src_ndim = array.ndim();
if let Some(dst_ndim) = D::NDIM {
if src_ndim != dst_ndim {
return Err(DimensionalityError::new(src_ndim, dst_ndim).into());
}
}

// Check if the element type matches `T`.
let src_dtype = array.dtype();
let dst_dtype = T::get_dtype(ob.py());
if !src_dtype.is_equiv_to(dst_dtype) {
return Err(TypeError::new(src_dtype, dst_dtype).into());
}

Ok(array)
}

/// Same as [`shape`][Self::shape], but returns `D` insead of `&[usize]`.
#[inline(always)]
pub fn dims(&self) -> D {
Expand Down
15 changes: 15 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,18 @@ impl fmt::Display for BorrowError {
}

impl_pyerr!(BorrowError);

/// An internal type used to ignore certain error conditions
///
/// This is beneficial when those errors will never reach a public API anyway
/// but dropping them will improve performance.
pub(crate) struct IgnoreError;

impl<E> From<E> for IgnoreError
where
PyErr: From<E>,
{
fn from(_err: E) -> Self {
Self
}
}

0 comments on commit 3795010

Please sign in to comment.