use crate::device::Error as DeviceError;
use crate::device::{IDevice, MemorySync};
use std::any::Any;
use std::cell::{Cell, RefCell};
use std::marker::PhantomData;
use std::ops::Deref;
use std::{fmt, mem};
pub type TensorDesc = Vec<usize>;
type BitMap = u64;
const BIT_MAP_SIZE: usize = 64;
struct TensorLocation {
device: Box<dyn Any>,
mem_transfer: Box<dyn MemorySync>,
mem: Box<dyn Any>,
}
pub struct SharedTensor<T> {
desc: TensorDesc,
locations: RefCell<Vec<TensorLocation>>,
up_to_date: Cell<BitMap>,
phantom: PhantomData<T>,
}
pub trait ITensorDesc {
fn rank(&self) -> usize;
fn size(&self) -> usize;
fn dims(&self) -> &Vec<usize>;
fn dims_i32(&self) -> Vec<i32>;
fn default_stride(&self) -> Vec<usize> {
let mut strides: Vec<usize> = Vec::with_capacity(self.rank());
let dim_length = self.dims().len();
match dim_length {
0 => strides,
1 => {
strides.push(1);
strides
}
_ => {
let imp_dims = &self.dims()[1..dim_length];
for (i, _) in imp_dims.iter().enumerate() {
strides.push(imp_dims[i..imp_dims.len()].iter().product())
}
strides.push(1);
strides
}
}
}
fn default_stride_i32(&self) -> Vec<i32> {
self.default_stride().iter().map(|&e| e as i32).collect()
}
}
pub trait IntoTensorDesc {
fn into(&self) -> TensorDesc;
}
impl IntoTensorDesc for () {
fn into(&self) -> TensorDesc {
Vec::with_capacity(1)
}
}
impl IntoTensorDesc for usize {
fn into(&self) -> TensorDesc {
vec![*self]
}
}
impl IntoTensorDesc for u32 {
fn into(&self) -> TensorDesc {
vec![*self as usize]
}
}
impl IntoTensorDesc for isize {
fn into(&self) -> TensorDesc {
vec![*self as usize]
}
}
impl IntoTensorDesc for i32 {
fn into(&self) -> TensorDesc {
vec![*self as usize]
}
}
impl IntoTensorDesc for Vec<usize> {
fn into(&self) -> TensorDesc {
self.clone()
}
}
impl<'a> IntoTensorDesc for &'a [usize] {
fn into(&self) -> TensorDesc {
From::from(self.to_owned())
}
}
impl IntoTensorDesc for (usize, usize) {
fn into(&self) -> TensorDesc {
vec![self.0, self.1]
}
}
impl IntoTensorDesc for (usize, usize, usize) {
fn into(&self) -> TensorDesc {
vec![self.0, self.1, self.2]
}
}
impl IntoTensorDesc for (usize, usize, usize, usize) {
fn into(&self) -> TensorDesc {
vec![self.0, self.1, self.2, self.3]
}
}
impl IntoTensorDesc for (usize, usize, usize, usize, usize) {
fn into(&self) -> TensorDesc {
vec![self.0, self.1, self.2, self.3, self.4]
}
}
impl IntoTensorDesc for (usize, usize, usize, usize, usize, usize) {
fn into(&self) -> TensorDesc {
vec![self.0, self.1, self.2, self.3, self.4, self.5]
}
}
macro_rules! impl_array_into_tensor_desc {
($($N:expr)+) => {
$(
impl IntoTensorDesc for [usize; $N] {
fn into(&self) -> TensorDesc {
let slice: &[_] = self;
From::from(slice)
}
}
)+
}
}
impl_array_into_tensor_desc!(1 2 3 4 5 6);
impl ITensorDesc for TensorDesc {
fn rank(&self) -> usize {
self.len()
}
fn size(&self) -> usize {
match self.rank() {
0 => 1,
_ => self.iter().product(),
}
}
fn dims(&self) -> &Vec<usize> {
self
}
fn dims_i32(&self) -> Vec<i32> {
self.iter().map(|&e| e as i32).collect()
}
}
impl<T> fmt::Debug for SharedTensor<T> {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "SharedTensor desc={:?}", self.desc)
}
}
impl<T> SharedTensor<T> {
pub fn new<D: IntoTensorDesc>(desc: &D) -> SharedTensor<T> {
SharedTensor {
desc: desc.into(),
locations: RefCell::new(Vec::new()),
up_to_date: Cell::new(0),
phantom: PhantomData,
}
}
pub fn reshape<D: IntoTensorDesc>(&mut self, desc: &D) -> Result<(), Error> {
let new_desc: TensorDesc = desc.into();
if new_desc.size() == self.desc().size() {
self.desc = new_desc;
Ok(())
} else {
Err(Error::InvalidShape(
"Size of the provided shape is not equal to the old shape.",
))
}
}
pub fn resize<D: IntoTensorDesc>(&mut self, desc: &D) -> Result<(), Error> {
self.locations.borrow_mut().clear();
self.up_to_date.set(0);
self.desc = desc.into();
Ok(())
}
fn get_location_index<D: IDevice>(&self, device: &D) -> Option<usize> {
for (i, loc) in self.locations.borrow().iter().enumerate() {
match loc.device.deref().downcast_ref::<D>() {
Some(ref d) if *d == device => return Some(i),
_ => {}
}
}
None
}
fn get_or_create_location_index<D: IDevice>(&self, device: &D) -> Result<usize, Error> {
if let Some(i) = self.get_location_index(device) {
return Ok(i);
}
if self.locations.borrow().len() == BIT_MAP_SIZE {
return Err(Error::CapacityExceeded);
}
let bytes_n = Self::mem_size(self.desc().size());
self.locations.borrow_mut().push(TensorLocation {
device: Box::new(device.clone()),
mem_transfer: Box::new(device.clone()),
mem: Box::new(D::alloc_memory(device, bytes_n)?),
});
Ok(self.locations.borrow().len() - 1)
}
fn sync_if_needed(&self, dst_i: usize) -> Result<(), Error> {
if self.up_to_date.get() & (1 << dst_i) != 0 {
return Ok(());
}
let src_i = self.up_to_date.get().trailing_zeros() as usize;
assert!(src_i != BIT_MAP_SIZE);
assert!(src_i != dst_i);
let mut locs = self.locations.borrow_mut();
let (src_loc, dst_loc) = if src_i < dst_i {
let (left, right) = locs.split_at_mut(dst_i);
(&left[src_i], &mut right[0])
} else {
let (left, right) = locs.split_at_mut(src_i);
(&right[0], &mut left[dst_i])
};
match src_loc.mem_transfer.sync_out(
src_loc.mem.deref(),
dst_loc.device.deref(),
dst_loc.mem.as_mut(),
) {
Err(DeviceError::NoMemorySyncRoute) => {}
x => return x.map_err(|e| e.into()),
}
match dst_loc.mem_transfer.sync_in(
dst_loc.mem.as_mut(),
src_loc.device.deref(),
src_loc.mem.deref(),
) {
Err(DeviceError::NoMemorySyncRoute) => {}
x => return x.map_err(|e| e.into()),
}
if cfg!(feature = "native") {
use crate::framework::IFramework;
use crate::frameworks::native::Native;
let native_framework = Native::new();
let native_device = native_framework
.new_device(native_framework.hardwares())
.unwrap(); let mut native_mem = native_device.alloc_memory(self.desc.size()).unwrap(); match src_loc.mem_transfer.sync_out(
src_loc.mem.deref(),
&native_device,
&mut native_mem,
) {
Err(DeviceError::NoMemorySyncRoute) => {}
x => return x.map_err(|e| e.into()),
}
match dst_loc
.mem_transfer
.sync_in(dst_loc.mem.as_mut(), &native_device, &native_mem)
{
Err(DeviceError::NoMemorySyncRoute) => {}
x => return x.map_err(|e| e.into()),
}
Ok(())
} else {
Err(DeviceError::NoMemorySyncRoute.into())
}
}
pub fn read<'a, D: IDevice>(&'a self, device: &D) -> Result<&'a D::M, Error> {
if self.up_to_date.get() == 0 {
return Err(Error::UninitializedMemory);
}
let i = self.get_or_create_location_index(device)?;
self.sync_if_needed(i)?;
self.up_to_date.set(self.up_to_date.get() | (1 << i));
let locs = self.locations.borrow();
let mem: &D::M = &locs[i]
.mem
.deref()
.downcast_ref()
.expect("Broken invariant: wrong memory type");
let mem_a: &'a D::M = unsafe { ::std::mem::transmute(mem) };
Ok(mem_a)
}
pub fn read_write<'a, D: IDevice>(&'a mut self, device: &D) -> Result<&'a mut D::M, Error> {
if self.up_to_date.get() == 0 {
return Err(Error::UninitializedMemory);
}
let i = self.get_or_create_location_index(device)?;
self.sync_if_needed(i)?;
self.up_to_date.set(1 << i);
let mut locs = self.locations.borrow_mut();
let mem: &mut D::M = &mut locs[i]
.mem
.as_mut()
.downcast_mut()
.expect("Broken invariant: wrong memory type");
let mem_a: &'a mut D::M = unsafe { ::std::mem::transmute(mem) };
Ok(mem_a)
}
pub fn write_only<'a, D: IDevice>(&'a mut self, device: &D) -> Result<&'a mut D::M, Error> {
let i = self.get_or_create_location_index(device)?;
self.up_to_date.set(1 << i);
let mut locs = self.locations.borrow_mut();
let mem: &mut D::M = &mut locs[i]
.mem
.as_mut()
.downcast_mut()
.expect("Broken invariant: wrong memory type");
let mem_a: &'a mut D::M = unsafe { ::std::mem::transmute(mem) };
Ok(mem_a)
}
pub fn drop<D: IDevice>(&mut self, device: &D) -> Result<(), Error> {
match self.get_location_index(device) {
Some(i) => {
self.locations.borrow_mut().remove(i);
let up_to_date = self.up_to_date.get();
let mask = (1 << i) - 1;
let lower = up_to_date & mask;
let upper = (up_to_date >> 1) & (!mask);
self.up_to_date.set(lower | upper);
Ok(())
}
None => Err(Error::InvalidRemove(
"Memory isn't allocated on this device",
)),
}
}
fn sync<D: IDevice>(&mut self, device: &D) -> Result<(), Error> {
if self.up_to_date.get() == 0 {
return Err(Error::UninitializedMemory);
}
let i = self.get_or_create_location_index(device)?;
self.sync_if_needed(i)?;
self.up_to_date.set(1 << i);
Ok(())
}
pub fn capacity(&self) -> usize {
self.desc.size()
}
pub fn desc(&self) -> &TensorDesc {
&self.desc
}
pub fn mem_size(capacity: usize) -> usize {
mem::size_of::<T>() * capacity
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
pub enum Error {
#[error(transparent)]
DeviceError(#[from] DeviceError),
#[error("Invalid remove: {0}")]
InvalidRemove(&'static str),
#[error("Invalid shape: {0}")]
InvalidShape(&'static str),
#[error("Capacity exceeded")]
CapacityExceeded,
#[error("Attempt to read uninitialized memory")]
UninitializedMemory,
}