#[derive(Debug, Clone, PartialEq)]
pub struct RecurrentState {
pub conv: AlignedF32,
pub ssm: AlignedF32,
}
impl RecurrentState {
pub fn zeros(conv_len: usize, ssm_len: usize) -> Self {
Self {
conv: AlignedF32::zeros(conv_len),
ssm: AlignedF32::zeros(ssm_len),
}
}
pub fn bytes(&self) -> usize {
(self.conv.len() + self.ssm.len()) * std::mem::size_of::<f32>()
}
}
pub struct AlignedF32 {
ptr: std::ptr::NonNull<f32>,
len: usize,
bytes: usize,
}
pub const PAGE: usize = 16384;
unsafe impl Send for AlignedF32 {}
unsafe impl Sync for AlignedF32 {}
impl AlignedF32 {
pub fn zeros(len: usize) -> Self {
let bytes = (len * std::mem::size_of::<f32>()).max(1).div_ceil(PAGE) * PAGE;
let layout = std::alloc::Layout::from_size_align(bytes, PAGE).expect("page layout");
let raw = unsafe { std::alloc::alloc_zeroed(layout) } as *mut f32;
let ptr = std::ptr::NonNull::new(raw).expect("allocation failed");
Self { ptr, len, bytes }
}
pub fn alloc_bytes(&self) -> usize {
self.bytes
}
pub fn as_ptr(&self) -> *mut f32 {
self.ptr.as_ptr()
}
}
impl Drop for AlignedF32 {
fn drop(&mut self) {
let layout = std::alloc::Layout::from_size_align(self.bytes, PAGE)
.expect("the layout it was made with");
unsafe { std::alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout) };
}
}
impl std::ops::Deref for AlignedF32 {
type Target = [f32];
fn deref(&self) -> &[f32] {
unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
}
}
impl std::ops::DerefMut for AlignedF32 {
fn deref_mut(&mut self) -> &mut [f32] {
unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
}
}
impl Clone for AlignedF32 {
fn clone(&self) -> Self {
let mut copy = Self::zeros(self.len);
copy.copy_from_slice(self);
copy
}
}
impl std::fmt::Debug for AlignedF32 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "AlignedF32({} floats)", self.len)
}
}
impl PartialEq for AlignedF32 {
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl FromIterator<f32> for AlignedF32 {
fn from_iter<I: IntoIterator<Item = f32>>(iter: I) -> Self {
let v: Vec<f32> = iter.into_iter().collect();
let mut out = Self::zeros(v.len());
out.copy_from_slice(&v);
out
}
}
#[cfg(test)]
mod aligned_tests {
use super::*;
#[test]
fn an_aligned_buffer_is_page_aligned_in_pointer_and_length() {
for len in [1usize, 1024, 48 * 128 * 128] {
let b = AlignedF32::zeros(len);
assert_eq!(b.as_ptr() as usize % PAGE, 0, "pointer");
assert_eq!(b.alloc_bytes() % PAGE, 0, "length");
assert!(b.alloc_bytes() >= len * 4);
assert_eq!(b.len(), len);
assert!(b.iter().all(|v| *v == 0.0), "zeroed");
}
}
#[test]
fn it_clones_by_value_and_compares_by_contents() {
let mut a = AlignedF32::zeros(8);
a[3] = 1.5;
let b = a.clone();
assert_eq!(a, b);
assert_eq!(b[3], 1.5);
}
}