use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use cudarc::driver::{CudaSlice, CudaStream, DeviceRepr};
use crate::io::device::GpuContext;
use crate::util::precision::PrecisionType;
static TENSOR_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
fn generate_unique_tensor_id() -> usize {
TENSOR_ID_COUNTER.fetch_add(1, Ordering::Relaxed)
}
#[allow(unused)]
pub(crate) fn check_all_equal<T: PartialEq>(vec: &Vec<T>) -> bool {
let t = &vec[0];
for x in vec.iter().skip(1) {
if t != x {
return false;
}
}
true
}
pub(crate) fn scramble_seed(step: u32, layer_id: u32) -> u32 {
let mut hash = step.wrapping_mul(1103515245).wrapping_add(layer_id);
hash = (hash ^ (hash >> 16)).wrapping_mul(1103515245);
hash ^ (hash >> 15)
}
pub fn download_cuda_slice<T: PrecisionType>(stream: &Arc<CudaStream>, c: &CudaSlice<T>) -> Vec<T> {
stream.clone_dtoh(c).unwrap()
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct Matrix<T: PrecisionType> {
pub rows: usize,
pub cols: usize,
pub v: Vec<T> }
#[allow(dead_code)]
impl<T: PrecisionType> Matrix<T> {
pub fn empty() -> Self { Self::new(0, 0) }
pub fn new(rows: usize, cols: usize) -> Self {
Self {
rows,
cols,
v: vec![T::zero(); rows * cols]
}
}
pub fn new_init<F>(rows: usize, cols: usize, mut init: F) -> Self
where
F: FnMut(usize, usize) -> T,
{
let mut v = Vec::with_capacity(rows * cols);
for r in 0..rows {
for c in 0..cols {
v.push(init(r, c));
}
}
Self { rows, cols, v }
}
pub fn get(&self, row: usize, col: usize) -> Option<&T> {
if row < self.rows && col < self.cols {
self.v.get(row * self.cols + col)
} else {
None
}
}
pub fn get_mut(&mut self, row: usize, col: usize) -> Option<&mut T> {
if row < self.rows && col < self.cols {
self.v.get_mut(row * self.cols + col)
} else {
None
}
}
pub fn set(&mut self, row: usize, col: usize, val: T) {
assert!(row < self.rows && col < self.cols, "Index out of bounds");
self.v[row * self.cols + col] = val;
}
pub fn set_v(&mut self, idx: usize, val: T) {
self.v[idx] = val;
}
pub fn is_empty(&self) -> bool { self.v.len() == 0 }
pub fn is_not_empty(&self) -> bool { self.v.len() != 0 }
}
#[derive(Debug)]
pub struct Tensor<T: PrecisionType> {
id: usize,
shape: [usize; 2],
data: CudaSlice<T>,
}
impl<T: PrecisionType + DeviceRepr> Tensor<T> {
pub fn from_cpu_vector(context: &GpuContext, cpu_data: &[T], shape: &[usize; 2]) -> Self {
assert_eq!(shape.len(), 2, "Shape must be length of 2 representing rows and columns respectively.");
let size = shape[0] * shape[1];
assert_eq!(size, cpu_data.len(), "Length of CPU data does not match shape given in tensor.");
let data_gpu = context.get_stream().clone_htod(cpu_data).expect("Cannot copy CPU data into VRAM for this tensor.");
context.get_stream().synchronize().unwrap();
Self {
id: generate_unique_tensor_id(),
shape: shape.clone(),
data: data_gpu
}
}
#[allow(unused)]
pub(crate) fn from_gpu_slice(gpu_slice: CudaSlice<T>, shape: &[usize; 2]) -> Self {
assert_eq!(shape.len(), 2, "Shape must be length of 2 representing rows and columns respectively.");
assert_eq!(shape.len(), gpu_slice.len(), "Shape and CUDA slice length mismatch.");
Self {
id: generate_unique_tensor_id(),
shape: shape.clone(),
data: gpu_slice
}
}
pub fn fill(context: &GpuContext, shape: &[usize; 2], value: T) -> Self {
assert_eq!(shape.len(), 2, "Shape must be length of 2 representing rows and columns respectively.");
let mut tensor = Self::zeros(context, shape);
tensor.broadcast(context, value);
tensor
}
pub fn zeros(context: &GpuContext, shape: &[usize; 2]) -> Self {
assert_eq!(shape.len(), 2, "Shape must be length of 2 representing rows and columns respectively.");
let size = shape[0] * shape[1];
let data_gpu = context.get_stream().alloc_zeros::<T>(size).expect("Cannot allocate memory in VRAM for this tensor.");
context.get_stream().synchronize().unwrap();
Self {
id: generate_unique_tensor_id(),
shape: shape.clone(),
data: data_gpu
}
}
#[inline]
pub fn rows(&self) -> usize {
self.shape[0]
}
#[inline]
pub fn cols(&self) -> usize {
self.shape[1]
}
pub fn get_id(&self) -> usize {
self.id
}
pub fn get_data(&self) -> &CudaSlice<T> {
&self.data
}
pub fn download(&self, context: &GpuContext) -> Matrix<T> {
let mut mat = Matrix::new(self.rows(), self.cols());
mat.v = download_cuda_slice(context.get_stream(), &self.data);
mat
}
pub fn broadcast(&mut self, context: &GpuContext, value: T) {
context.gpu_broadcast(&self.data, value);
}
pub fn clone(&self, context: &GpuContext) -> Tensor<T> {
Tensor::<T> {
id: generate_unique_tensor_id(),
shape: self.shape.clone(),
data: context.get_stream().clone_dtod(self.get_data()).expect("Tensor clone failed.")
}
}
}