#![allow(clippy::undocumented_unsafe_blocks)] #![allow(unsafe_code)]
use std::alloc::{alloc, dealloc, Layout};
use std::mem;
use std::ptr::NonNull;
use std::slice;
pub const SIMD_ALIGNMENT: usize = 64;
pub struct SimdAlignedBuffer<T> {
ptr: NonNull<T>,
len: usize,
layout: Layout,
}
impl<T> SimdAlignedBuffer<T> {
pub fn new(len: usize) -> Self {
assert!(len > 0, "Buffer length must be > 0");
let size = len * mem::size_of::<T>();
let layout = Layout::from_size_align(size, SIMD_ALIGNMENT).expect("Invalid layout");
unsafe {
let ptr = alloc(layout) as *mut T;
if ptr.is_null() {
std::alloc::handle_alloc_error(layout);
}
Self {
ptr: NonNull::new_unchecked(ptr),
len,
layout,
}
}
}
pub fn zeroed(len: usize) -> Self
where
T: Copy,
{
let mut buffer = Self::new(len);
unsafe {
std::ptr::write_bytes(buffer.ptr.as_ptr(), 0, len);
}
buffer
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn as_ptr(&self) -> *const T {
self.ptr.as_ptr()
}
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut T {
self.ptr.as_ptr()
}
#[inline]
pub fn as_slice(&self) -> &[T] {
unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [T] {
unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
}
#[inline]
pub fn is_aligned(&self) -> bool {
(self.ptr.as_ptr() as usize).is_multiple_of(SIMD_ALIGNMENT)
}
}
impl<T> Drop for SimdAlignedBuffer<T> {
fn drop(&mut self) {
unsafe {
dealloc(self.ptr.as_ptr() as *mut u8, self.layout);
}
}
}
#[allow(clippy::non_send_fields_in_send_ty)]
#[allow(unsafe_code)]
unsafe impl<T: Send> Send for SimdAlignedBuffer<T> {}
#[allow(unsafe_code)]
unsafe impl<T: Sync> Sync for SimdAlignedBuffer<T> {}
impl<T> std::ops::Index<usize> for SimdAlignedBuffer<T> {
type Output = T;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
&self.as_slice()[index]
}
}
impl<T> std::ops::IndexMut<usize> for SimdAlignedBuffer<T> {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.as_mut_slice()[index]
}
}
pub struct SimdAllocator<T> {
pool: Vec<SimdAlignedBuffer<T>>,
max_pool_size: usize,
total_allocations: u64,
pool_hits: u64,
}
impl<T> SimdAllocator<T> {
pub fn new(max_pool_size: usize) -> Self {
Self {
pool: Vec::with_capacity(max_pool_size),
max_pool_size,
total_allocations: 0,
pool_hits: 0,
}
}
pub fn allocate(&mut self, len: usize) -> SimdAlignedBuffer<T> {
self.total_allocations += 1;
if let Some(buffer) = self.pool.pop() {
if buffer.len() == len {
self.pool_hits += 1;
return buffer;
}
}
SimdAlignedBuffer::new(len)
}
pub fn allocate_zeroed(&mut self, len: usize) -> SimdAlignedBuffer<T>
where
T: Copy,
{
let mut buffer = self.allocate(len);
unsafe {
std::ptr::write_bytes(buffer.as_mut_ptr(), 0, len);
}
buffer
}
pub fn deallocate(&mut self, buffer: SimdAlignedBuffer<T>) {
if self.pool.len() < self.max_pool_size {
self.pool.push(buffer);
}
}
pub fn hit_rate(&self) -> f64 {
if self.total_allocations == 0 {
0.0
} else {
self.pool_hits as f64 / self.total_allocations as f64
}
}
pub fn clear(&mut self) {
self.pool.clear();
}
}
impl<T> Default for SimdAllocator<T> {
fn default() -> Self {
Self::new(16)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_alignment() {
let buffer = SimdAlignedBuffer::<f32>::new(1024);
assert!(buffer.is_aligned());
assert_eq!(buffer.as_ptr() as usize % SIMD_ALIGNMENT, 0);
}
#[test]
fn test_zeroed() {
let buffer = SimdAlignedBuffer::<f32>::zeroed(256);
for &val in buffer.as_slice() {
assert_eq!(val, 0.0);
}
}
#[test]
fn test_allocator_pool() {
let mut allocator = SimdAllocator::<f32>::new(4);
let buf1 = allocator.allocate(512);
allocator.deallocate(buf1);
let _buf2 = allocator.allocate(512);
assert_eq!(allocator.pool_hits, 1);
assert!(allocator.hit_rate() > 0.0);
}
#[test]
fn test_allocator_different_sizes() {
let mut allocator = SimdAllocator::<f32>::new(4);
let buf1 = allocator.allocate(512);
allocator.deallocate(buf1);
let _buf2 = allocator.allocate(1024);
assert_eq!(allocator.pool_hits, 0);
}
#[test]
fn test_slice_access() {
let mut buffer = SimdAlignedBuffer::<f32>::new(16);
for (i, val) in buffer.as_mut_slice().iter_mut().enumerate() {
*val = i as f32;
}
for (i, &val) in buffer.as_slice().iter().enumerate() {
assert_eq!(val, i as f32);
}
}
#[test]
fn test_index_access() {
let mut buffer = SimdAlignedBuffer::<f32>::new(16);
buffer[0] = 1.0;
buffer[15] = 15.0;
assert_eq!(buffer[0], 1.0);
assert_eq!(buffer[15], 15.0);
}
}