/dlpackrs

DLPack safe Rust binding

Primary LanguageRustApache License 2.0Apache-2.0

dlpackrs

Build miri-checked crates.io docs.rs

This crate provides a safe idiomatic Rust binding to DLPack which is the standard in-memory, (mostly) hardware agnostic data format , recognized by major Deep Learning frameworks such as PyTorch, TensorFlow, MXNet, TVM and major array processing frameworks such as NumPy and CuPy. An important feature of this standard is to provide zero-cost tensor conversion across frameworks on a particular supported hardware.

The Minimum Supported Rust Version (MSRV) is the stable toolchain 1.57.0.

Usage

There are two main cases related to where the owner of the underlying data / storage of a tensor resides and what kind of operations are to be done.

Memory Managed Tensor

In this case, ManagedTensor is built from ManagedTensorProxy which is a safe proxy for the unsafe ffi::DLManagedTensor.

Plain Not-Memory-Managed Tensor

In this case, the (invariant) Rust wrapper Tensor can be used or if needed the unsafe ffi::DLTensor.

Example

When ownership is concerned, one can use the ManagedTensor. Here is an example on how the bi-directional conversion

ndarray::ArrayD <---> ManagedTensor

is done at zero-cost.

impl<'tensor, C> From<&'tensor mut ArrayD<f32>> for ManagedContext<'tensor, C> {
    fn from(t: &'tensor mut ArrayD<f32>) -> Self {
        let dlt: Tensor<'tensor> = Tensor::from(t);
        let inner = DLManagedTensor::new(dlt.0, None);
        ManagedContext(inner)
    }
}

impl<'tensor, C> From<&mut ManagedContext<'tensor, C>> for ArrayD<f32> {
    fn from(mt: &mut ManagedContext<'tensor, C>) -> Self {
        let dlt: DLTensor = mt.0.inner.dl_tensor.into();
        unsafe {
            let arr = RawArrayViewMut::from_shape_ptr(dlt.shape().unwrap(), dlt.data() as *mut f32);
            arr.deref_into_view_mut().into_dyn().to_owned()
        }
    }
}

And when ownership is not concerned, one can use Tensor as a view. Here is an example on how the bi-directional converion

ndarray::ArrayD <---> Tensor

is done at zero-cost.

impl<'tensor> From<&'tensor mut ArrayD<f32>> for Tensor<'tensor> {
    fn from(arr: &'tensor mut ArrayD<f32>) -> Self {
        let inner = DLTensor::new(
            arr.as_mut_ptr() as *mut c_void,
            Device::default(),
            arr.ndim() as i32,
            DataType::f32(),
            arr.shape().as_ptr() as *const _ as *mut i64,
            arr.strides().as_ptr() as *const _ as *mut i64,
            0,
        );
        Tensor(inner)
    }
}

impl<'tensor> From<&'tensor mut Tensor<'tensor>> for ArrayD<f32> {
    fn from(t: &'tensor mut Tensor<'tensor>) -> Self {
        unsafe {
            let arr = RawArrayViewMut::from_shape_ptr(t.0.shape().unwrap(), t.0.data() as *mut f32);
            arr.deref_into_view_mut().into_dyn().to_owned()
        }
    }
}

See the complete examples/sample where the above cases have been simulated for the Rust ndarray conversion.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.