1use scirs2_core::ndarray::{ArrayBase, Data, Ix1};
7use std::marker::PhantomData;
8
9use super::parallel::ParallelConfig;
10use crate::error::{MetricsError, Result};
11
12pub trait StreamingMetric<T> {
17 type State;
19
20 fn init_state(&self) -> Self::State;
22
23 fn update_state(
25 &self,
26 state: &mut Self::State,
27 batch_true: &[T],
28 batch_pred: &[T],
29 ) -> Result<()>;
30
31 fn finalize(&self, state: &Self::State) -> Result<f64>;
33}
34
35#[derive(Debug, Clone)]
40pub struct ChunkedMetrics {
41 pub chunk_size: usize,
43 pub parallel_config: ParallelConfig,
45}
46
47impl Default for ChunkedMetrics {
48 fn default() -> Self {
49 ChunkedMetrics {
50 chunk_size: 10000,
51 parallel_config: ParallelConfig::default(),
52 }
53 }
54}
55
56impl ChunkedMetrics {
57 pub fn new() -> Self {
59 Default::default()
60 }
61
62 pub fn with_chunk_size(mut self, size: usize) -> Self {
64 self.chunk_size = size;
65 self
66 }
67
68 pub fn with_parallel_config(mut self, config: ParallelConfig) -> Self {
70 self.parallel_config = config;
71 self
72 }
73
74 pub fn compute_streaming<T, S1, S2, M>(
133 &self,
134 y_true: &ArrayBase<S1, Ix1>,
135 y_pred: &ArrayBase<S2, Ix1>,
136 metric: &M,
137 ) -> Result<f64>
138 where
139 T: Clone,
140 S1: Data<Elem = T>,
141 S2: Data<Elem = T>,
142 M: StreamingMetric<T>,
143 {
144 if y_true.len() != y_pred.len() {
146 return Err(MetricsError::DimensionMismatch(format!(
147 "y_true and y_pred must have the same length, got {} and {}",
148 y_true.len(),
149 y_pred.len()
150 )));
151 }
152
153 let y_true_vec: Vec<T> = y_true.iter().cloned().collect();
155 let y_pred_vec: Vec<T> = y_pred.iter().cloned().collect();
156
157 let mut state = metric.init_state();
159
160 for chunk_idx in 0..y_true_vec.len().div_ceil(self.chunk_size) {
162 let start = chunk_idx * self.chunk_size;
163 let end = (start + self.chunk_size).min(y_true_vec.len());
164
165 metric.update_state(&mut state, &y_true_vec[start..end], &y_pred_vec[start..end])?;
166 }
167
168 metric.finalize(&state)
170 }
171
172 pub fn compute_rowwise<T, R>(
186 &self,
187 data: &[T],
188 row_op: impl Fn(&[T]) -> Result<R>,
189 combine: impl Fn(&[R]) -> Result<R>,
190 ) -> Result<R>
191 where
192 T: Clone,
193 R: Clone,
194 {
195 if data.len() <= self.chunk_size {
196 return row_op(data);
198 }
199
200 let mut results = Vec::new();
202
203 for chunk_idx in 0..data.len().div_ceil(self.chunk_size) {
204 let start = chunk_idx * self.chunk_size;
205 let end = (start + self.chunk_size).min(data.len());
206
207 let result = row_op(&data[start..end])?;
208 results.push(result);
209 }
210
211 combine(&results)
213 }
214}
215
216#[derive(Debug, Clone)]
221pub struct IncrementalMetrics<T, S> {
222 state: S,
224 count: usize,
226 _marker: PhantomData<T>,
228}
229
230impl<T, S> Default for IncrementalMetrics<T, S>
231where
232 S: Default,
233{
234 fn default() -> Self {
235 Self::new()
236 }
237}
238
239impl<T, S> IncrementalMetrics<T, S>
240where
241 S: Default,
242{
243 pub fn new() -> Self {
245 IncrementalMetrics {
246 state: S::default(),
247 count: 0,
248 _marker: PhantomData,
249 }
250 }
251
252 pub fn with_state(state: S) -> Self {
254 IncrementalMetrics {
255 state,
256 count: 0,
257 _marker: PhantomData,
258 }
259 }
260
261 pub fn state(&self) -> &S {
263 &self.state
264 }
265
266 pub fn count(&self) -> usize {
268 self.count
269 }
270
271 pub fn update<F>(&mut self, y_true: T, y_pred: T, updatefn: F) -> Result<()>
283 where
284 F: FnOnce(&mut S, T, T) -> Result<()>,
285 {
286 updatefn(&mut self.state, y_true, y_pred)?;
287 self.count += 1;
288 Ok(())
289 }
290
291 pub fn update_batch<F>(&mut self, y_true: &[T], y_pred: &[T], updatefn: F) -> Result<()>
303 where
304 F: Fn(&mut S, &[T], &[T]) -> Result<()>,
305 {
306 if y_true.len() != y_pred.len() {
307 return Err(MetricsError::DimensionMismatch(
308 "y_true and y_pred must have the same length".to_string(),
309 ));
310 }
311
312 updatefn(&mut self.state, y_true, y_pred)?;
313 self.count += y_true.len();
314 Ok(())
315 }
316
317 pub fn finalize<F, R>(&self, finalizefn: F) -> Result<R>
327 where
328 F: FnOnce(&S, usize) -> Result<R>,
329 {
330 finalizefn(&self.state, self.count)
331 }
332}
333
334pub trait MemoryMappedMetric<T> {
339 type State;
341
342 fn init_state(&self) -> Self::State;
344
345 fn process_chunk(&self, state: &mut Self::State, chunkidx: usize, chunk: &[T]) -> Result<()>;
347
348 fn finalize(&self, state: &Self::State) -> Result<f64>;
350}
351
352use crossbeam_utils::CachePadded;
353use scirs2_core::ndarray::{Array1, Array2, ArrayView1, ArrayView2, ArrayViewMut1, ArrayViewMut2};
354use scirs2_core::numeric::Float;
355use std::alloc::{alloc, dealloc, GlobalAlloc, Layout, System};
360use std::cell::UnsafeCell;
361use std::collections::HashMap;
362use std::mem::{align_of, size_of, MaybeUninit};
363use std::ptr::{addr_of_mut, NonNull};
364use std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
365use std::sync::{Arc, Mutex, RwLock};
366
367#[derive(Debug)]
369pub struct ZeroCopyMemoryManager {
370 memory_pools: HashMap<usize, MemoryPool>,
372 simd_allocator: SimdAlignedAllocator,
374 arena_allocator: ArenaAllocator,
376 mmap_manager: MemoryMappingManager,
378 recycler: LockFreeRecycler,
380 stats: MemoryStats,
382}
383
384#[derive(Debug)]
386pub struct MemoryPool {
387 block_size: usize,
389 alignment: usize,
391 free_blocks: Arc<Mutex<Vec<NonNull<u8>>>>,
393 capacity: AtomicUsize,
395 allocated_count: AtomicUsize,
397 pool_stats: PoolStatistics,
399}
400
401#[derive(Debug)]
403pub struct SimdAlignedAllocator {
404 alignment_cache: HashMap<usize, Vec<NonNull<u8>>>,
406 simd_stats: SimdStats,
408}
409
410#[derive(Debug)]
412pub struct ArenaAllocator {
413 current_arena: Arc<Mutex<Arena>>,
415 arenas: Vec<Arc<Mutex<Arena>>>,
417 _default_arenasize: usize,
419 arena_stats: ArenaStats,
421}
422
423#[derive(Debug)]
425pub struct Arena {
426 memory: NonNull<u8>,
428 size: usize,
430 offset: usize,
432 alignment: usize,
434}
435
436#[derive(Debug)]
438pub struct MemoryMappingManager {
439 mappings: HashMap<String, MemoryMapping>,
441 mapping_stats: MappingStats,
443}
444
445#[derive(Debug)]
447pub struct MemoryMapping {
448 file_handle: i32,
450 memory_region: NonNull<u8>,
452 size: usize,
454 access_mode: AccessMode,
456 ref_count: AtomicUsize,
458}
459
460#[derive(Debug, Clone, Copy)]
462pub enum AccessMode {
463 ReadOnly,
464 ReadWrite,
465 WriteOnly,
466 Execute,
467}
468
469#[derive(Debug)]
471pub struct LockFreeRecycler {
472 free_lists: Vec<AtomicPtr<RecyclerNode>>,
474 hazard_pointers: Vec<AtomicPtr<RecyclerNode>>,
476 retired_nodes: CachePadded<Mutex<Vec<*mut RecyclerNode>>>,
478 recycler_stats: RecyclerStats,
480}
481
482#[repr(align(64))] #[derive(Debug)]
485pub struct RecyclerNode {
486 next: AtomicPtr<RecyclerNode>,
488 size: usize,
490 data: NonNull<u8>,
492 timestamp: AtomicUsize,
494}
495
496#[derive(Debug)]
498pub struct ZeroCopyArrayView<'a, T> {
499 data: NonNull<T>,
501 len: usize,
503 _lifetime: std::marker::PhantomData<&'a T>,
505 memory_manager: &'a ZeroCopyMemoryManager,
507}
508
509#[derive(Debug)]
511pub struct ZeroCopyArrayViewMut<'a, T> {
512 data: NonNull<T>,
514 len: usize,
516 _lifetime: std::marker::PhantomData<&'a mut T>,
518 memory_manager: &'a ZeroCopyMemoryManager,
520}
521
522pub struct ZeroCopyBuffer<T> {
524 data: NonNull<T>,
526 capacity: usize,
528 length: usize,
530 layout: Layout,
532 allocator: Arc<dyn CustomAllocator>,
534}
535
536pub trait CustomAllocator: Send + Sync + std::fmt::Debug {
538 fn allocate(&self, size: usize, alignment: usize) -> Result<NonNull<u8>>;
540
541 fn deallocate(&self, ptr: NonNull<u8>, size: usize, alignment: usize);
543
544 fn reallocate(
546 &self,
547 ptr: NonNull<u8>,
548 old_size: usize,
549 newsize: usize,
550 alignment: usize,
551 ) -> Result<NonNull<u8>>;
552
553 fn get_stats(&self) -> AllocatorStats;
555
556 fn reset(&self);
558}
559
560pub struct ThreadLocalAllocator {
562 local_pools: std::thread::LocalKey<UnsafeCell<HashMap<usize, Vec<NonNull<u8>>>>>,
564 global_fallback: Arc<dyn CustomAllocator>,
566 local_stats: std::thread::LocalKey<UnsafeCell<AllocatorStats>>,
568}
569
570#[derive(Debug)]
572pub struct SlabAllocator {
573 slab_size: usize,
575 object_size: usize,
577 objects_per_slab: usize,
579 slabs: Vec<Slab>,
581 free_objects: Vec<NonNull<u8>>,
583 slab_stats: SlabStats,
585}
586
587#[derive(Debug)]
589pub struct Slab {
590 memory: NonNull<u8>,
592 free_mask: Vec<u64>,
594 free_count: usize,
596 id: usize,
598}
599
600#[derive(Debug)]
602pub struct BuddyAllocator {
603 memory_block: NonNull<u8>,
605 block_size: usize,
607 free_lists: Vec<Vec<NonNull<u8>>>,
609 allocation_bitmap: Vec<u64>,
611 buddy_stats: BuddyStats,
613}
614
615#[derive(Debug)]
617pub struct MemoryStats {
618 pub total_allocated: AtomicUsize,
620 pub total_deallocated: AtomicUsize,
622 pub peak_usage: AtomicUsize,
624 pub current_usage: AtomicUsize,
626 pub allocation_count: AtomicUsize,
628 pub deallocation_count: AtomicUsize,
630 pub fragmentation_ratio: AtomicUsize, }
633
634#[derive(Debug)]
636pub struct PoolStatistics {
637 pub hits: AtomicUsize,
639 pub misses: AtomicUsize,
641 pub utilization: AtomicUsize,
643 pub avg_allocation_time: AtomicUsize,
645}
646
647#[derive(Debug)]
649pub struct SimdStats {
650 pub allocations_by_alignment: HashMap<usize, AtomicUsize>,
652 pub simd_memory_usage: AtomicUsize,
654 pub vectorization_efficiency: AtomicUsize,
656}
657
658#[derive(Debug)]
660pub struct ArenaStats {
661 pub arenas_created: AtomicUsize,
663 pub total_arena_memory: AtomicUsize,
665 pub arena_utilization: AtomicUsize,
667 pub fragmentation_waste: AtomicUsize,
669}
670
671#[derive(Debug)]
673pub struct MappingStats {
674 pub active_mappings: AtomicUsize,
676 pub total_mapped_memory: AtomicUsize,
678 pub cache_hits: AtomicUsize,
680 pub cache_misses: AtomicUsize,
682}
683
684#[derive(Debug)]
686pub struct RecyclerStats {
687 pub successful_recycles: AtomicUsize,
689 pub failed_recycles: AtomicUsize,
691 pub hazard_contentions: AtomicUsize,
693 pub memory_reclaimed: AtomicUsize,
695}
696
697#[derive(Debug)]
699pub struct AllocatorStats {
700 pub allocation_requests: AtomicUsize,
702 pub deallocation_requests: AtomicUsize,
704 pub bytes_allocated: AtomicUsize,
706 pub bytes_deallocated: AtomicUsize,
708 pub allocation_failures: AtomicUsize,
710}
711
712#[derive(Debug)]
714pub struct SlabStats {
715 pub slabs_allocated: AtomicUsize,
717 pub objects_allocated: AtomicUsize,
719 pub slab_utilization: AtomicUsize,
721 pub internal_fragmentation: AtomicUsize,
723}
724
725#[derive(Debug)]
727pub struct BuddyStats {
728 pub allocations_by_order: Vec<AtomicUsize>,
730 pub coalescing_operations: AtomicUsize,
732 pub splitting_operations: AtomicUsize,
734 pub external_fragmentation: AtomicUsize,
736}
737
738impl ZeroCopyMemoryManager {
739 pub fn new() -> Result<Self> {
741 Ok(Self {
742 memory_pools: HashMap::new(),
743 simd_allocator: SimdAlignedAllocator::new(),
744 arena_allocator: ArenaAllocator::new(1024 * 1024)?, mmap_manager: MemoryMappingManager::new(),
746 recycler: LockFreeRecycler::new(),
747 stats: MemoryStats::new(),
748 })
749 }
750
751 pub fn allocate_buffer<T>(&self, capacity: usize) -> Result<ZeroCopyBuffer<T>> {
753 let layout = Layout::array::<T>(capacity)
754 .map_err(|_| MetricsError::MemoryError("Invalid layout".to_string()))?;
755
756 let allocator = self.get_optimal_allocator(layout.size(), layout.align());
757 let ptr = allocator.allocate(layout.size(), layout.align())?;
758
759 self.stats
760 .total_allocated
761 .fetch_add(layout.size(), Ordering::Relaxed);
762 self.stats.allocation_count.fetch_add(1, Ordering::Relaxed);
763
764 Ok(ZeroCopyBuffer {
765 data: ptr.cast::<T>(),
766 capacity,
767 length: 0,
768 layout,
769 allocator,
770 })
771 }
772
773 pub fn create_view<'a, T>(&'a self, data: &'a [T]) -> ZeroCopyArrayView<'a, T> {
775 ZeroCopyArrayView {
776 data: NonNull::new(data.as_ptr() as *mut T).expect("Operation failed"),
777 len: data.len(),
778 _lifetime: std::marker::PhantomData,
779 memory_manager: self,
780 }
781 }
782
783 pub fn create_view_mut<'a, T>(&'a self, data: &'a mut [T]) -> ZeroCopyArrayViewMut<'a, T> {
785 ZeroCopyArrayViewMut {
786 data: NonNull::new(data.as_mut_ptr()).expect("Operation failed"),
787 len: data.len(),
788 _lifetime: std::marker::PhantomData,
789 memory_manager: self,
790 }
791 }
792
793 pub fn allocate_simd_aligned<T: Float>(
795 &mut self,
796 count: usize,
797 alignment: usize,
798 ) -> Result<ZeroCopyBuffer<T>> {
799 let size = count * size_of::<T>();
800 let ptr = self.simd_allocator.allocate_aligned(size, alignment)?;
801
802 Ok(ZeroCopyBuffer {
803 data: ptr.cast::<T>(),
804 capacity: count,
805 length: 0,
806 layout: Layout::from_size_align(size, alignment).expect("Operation failed"),
807 allocator: Arc::new(SystemAllocator),
808 })
809 }
810
811 pub fn map_file<T>(
813 &self,
814 file_path: &str,
815 access_mode: AccessMode,
816 ) -> Result<ZeroCopyArrayView<T>> {
817 let mapping = self.mmap_manager.map_file(file_path, access_mode)?;
818 let len = mapping.size / size_of::<T>();
819
820 Ok(ZeroCopyArrayView {
821 data: mapping.memory_region.cast::<T>(),
822 len,
823 _lifetime: std::marker::PhantomData,
824 memory_manager: self,
825 })
826 }
827
828 fn get_optimal_allocator(&self, size: usize, alignment: usize) -> Arc<dyn CustomAllocator> {
830 if size <= 4096 && alignment <= 64 {
832 Arc::new(PoolAllocator::new(size))
834 } else if alignment > 64 {
835 Arc::new(SimdAllocatorWrapper::new())
837 } else if size >= 1024 * 1024 {
838 Arc::new(ArenaAllocatorWrapper::new())
840 } else {
841 Arc::new(SystemAllocator)
843 }
844 }
845
846 pub fn get_stats(&self) -> &MemoryStats {
848 &self.stats
849 }
850
851 pub fn garbage_collect(&self) -> Result<usize> {
853 let mut reclaimed = 0;
854
855 reclaimed += self.recycler.reclaim_memory()?;
857
858 for pool in self.memory_pools.values() {
860 reclaimed += pool.compact()?;
861 }
862
863 reclaimed += self.arena_allocator.compact()?;
865
866 Ok(reclaimed)
867 }
868}
869
870impl MemoryPool {
871 pub fn new(_blocksize: usize, alignment: usize, initialcapacity: usize) -> Self {
873 Self {
874 block_size: _blocksize,
875 alignment,
876 free_blocks: Arc::new(Mutex::new(Vec::with_capacity(initialcapacity))),
877 capacity: AtomicUsize::new(0),
878 allocated_count: AtomicUsize::new(0),
879 pool_stats: PoolStatistics::new(),
880 }
881 }
882
883 pub fn allocate(&self) -> Result<NonNull<u8>> {
885 let start_time = std::time::Instant::now();
886
887 let mut free_blocks = self.free_blocks.lock().expect("Operation failed");
888 if let Some(ptr) = free_blocks.pop() {
889 self.pool_stats.hits.fetch_add(1, Ordering::Relaxed);
890 drop(free_blocks);
891
892 self.allocated_count.fetch_add(1, Ordering::Relaxed);
893 let allocation_time = start_time.elapsed().as_nanos() as usize;
894 self.update_avg_allocation_time(allocation_time);
895
896 Ok(ptr)
897 } else {
898 self.pool_stats.misses.fetch_add(1, Ordering::Relaxed);
899 drop(free_blocks);
900
901 let layout = Layout::from_size_align(self.block_size, self.alignment)
903 .map_err(|_| MetricsError::MemoryError("Invalid layout".to_string()))?;
904
905 let ptr = unsafe { alloc(layout) };
906 if ptr.is_null() {
907 return Err(MetricsError::MemoryError("Allocation failed".to_string()));
908 }
909
910 self.capacity.fetch_add(1, Ordering::Relaxed);
911 self.allocated_count.fetch_add(1, Ordering::Relaxed);
912
913 Ok(NonNull::new(ptr).expect("Operation failed"))
914 }
915 }
916
917 pub fn deallocate(&self, ptr: NonNull<u8>) {
919 self.free_blocks.lock().expect("Operation failed").push(ptr);
920 self.allocated_count.fetch_sub(1, Ordering::Relaxed);
921 }
922
923 pub fn compact(&self) -> Result<usize> {
925 let mut free_blocks = self.free_blocks.lock().expect("Operation failed");
926 let mut reclaimed = 0;
927
928 let keep_count = free_blocks.len() / 2;
930 let to_deallocate = free_blocks.split_off(keep_count);
931
932 for ptr in to_deallocate {
933 unsafe {
934 let layout = Layout::from_size_align(self.block_size, self.alignment)
935 .expect("Operation failed");
936 dealloc(ptr.as_ptr(), layout);
937 }
938 reclaimed += self.block_size;
939 }
940
941 self.capacity
942 .fetch_sub(reclaimed / self.block_size, Ordering::Relaxed);
943 Ok(reclaimed)
944 }
945
946 fn update_avg_allocation_time(&self, newtime: usize) {
947 let current_avg = self.pool_stats.avg_allocation_time.load(Ordering::Relaxed);
949 let new_avg = if current_avg == 0 {
950 newtime
951 } else {
952 (current_avg * 7 + newtime) / 8 };
954 self.pool_stats
955 .avg_allocation_time
956 .store(new_avg, Ordering::Relaxed);
957 }
958}
959
960impl SimdAlignedAllocator {
961 pub fn new() -> Self {
963 Self {
964 alignment_cache: HashMap::new(),
965 simd_stats: SimdStats::new(),
966 }
967 }
968
969 pub fn allocate_aligned(&mut self, size: usize, alignment: usize) -> Result<NonNull<u8>> {
971 let alignment = alignment.max(align_of::<usize>()).next_power_of_two();
973
974 let layout = Layout::from_size_align(size, alignment)
975 .map_err(|_| MetricsError::MemoryError("Invalid SIMD layout".to_string()))?;
976
977 let ptr = unsafe { alloc(layout) };
978 if ptr.is_null() {
979 return Err(MetricsError::MemoryError(
980 "SIMD allocation failed".to_string(),
981 ));
982 }
983
984 self.simd_stats
985 .simd_memory_usage
986 .fetch_add(size, Ordering::Relaxed);
987 self.simd_stats
988 .allocations_by_alignment
989 .entry(alignment)
990 .or_insert_with(|| AtomicUsize::new(0))
991 .fetch_add(1, Ordering::Relaxed);
992
993 Ok(NonNull::new(ptr).expect("Operation failed"))
994 }
995}
996
997impl ArenaAllocator {
998 pub fn new(_default_arenasize: usize) -> Result<Self> {
1000 let initial_arena = Arc::new(Mutex::new(Arena::new(_default_arenasize)?));
1001
1002 Ok(Self {
1003 current_arena: initial_arena.clone(),
1004 arenas: vec![initial_arena],
1005 _default_arenasize,
1006 arena_stats: ArenaStats::new(),
1007 })
1008 }
1009
1010 pub fn allocate(&mut self, size: usize, alignment: usize) -> Result<NonNull<u8>> {
1012 let mut arena = self.current_arena.lock().expect("Operation failed");
1013
1014 if let Ok(ptr) = arena.allocate(size, alignment) {
1015 Ok(ptr)
1016 } else {
1017 drop(arena);
1019
1020 let new_arena_size = self._default_arenasize.max(size * 2);
1021 let new_arena = Arc::new(Mutex::new(Arena::new(new_arena_size)?));
1022 self.arenas.push(new_arena.clone());
1023
1024 let mut arena = new_arena.lock().expect("Operation failed");
1025 arena.allocate(size, alignment)
1026 }
1027 }
1028
1029 pub fn reset(&self) {
1031 for arena in &self.arenas {
1032 arena.lock().expect("Operation failed").reset();
1033 }
1034 }
1035
1036 pub fn compact(&self) -> Result<usize> {
1038 self.arena_stats
1040 .fragmentation_waste
1041 .store(0, Ordering::Relaxed);
1042 Ok(0)
1043 }
1044}
1045
1046impl Arena {
1047 pub fn new(size: usize) -> Result<Self> {
1049 let layout = Layout::from_size_align(size, 64) .map_err(|_| MetricsError::MemoryError("Invalid arena layout".to_string()))?;
1051
1052 let ptr = unsafe { alloc(layout) };
1053 if ptr.is_null() {
1054 return Err(MetricsError::MemoryError(
1055 "Arena allocation failed".to_string(),
1056 ));
1057 }
1058
1059 Ok(Self {
1060 memory: NonNull::new(ptr).expect("Operation failed"),
1061 size,
1062 offset: 0,
1063 alignment: 64,
1064 })
1065 }
1066
1067 pub fn allocate(&mut self, size: usize, alignment: usize) -> Result<NonNull<u8>> {
1069 let aligned_offset = (self.offset + alignment - 1) & !(alignment - 1);
1071
1072 if aligned_offset + size > self.size {
1073 return Err(MetricsError::MemoryError("Arena exhausted".to_string()));
1074 }
1075
1076 let ptr = unsafe { self.memory.as_ptr().add(aligned_offset) };
1077 self.offset = aligned_offset + size;
1078
1079 Ok(NonNull::new(ptr).expect("Operation failed"))
1080 }
1081
1082 pub fn reset(&mut self) {
1084 self.offset = 0;
1085 }
1086}
1087
1088impl MemoryMappingManager {
1089 pub fn new() -> Self {
1091 Self {
1092 mappings: HashMap::new(),
1093 mapping_stats: MappingStats::new(),
1094 }
1095 }
1096
1097 pub fn map_file(&self, _file_path: &str, _accessmode: AccessMode) -> Result<&MemoryMapping> {
1099 Err(MetricsError::MemoryError(
1102 "Memory mapping not implemented".to_string(),
1103 ))
1104 }
1105}
1106
1107impl LockFreeRecycler {
1108 pub fn new() -> Self {
1110 const NUM_SIZE_CLASSES: usize = 64;
1111
1112 Self {
1113 free_lists: (0..NUM_SIZE_CLASSES)
1114 .map(|_| AtomicPtr::new(std::ptr::null_mut()))
1115 .collect(),
1116 hazard_pointers: (0..NUM_SIZE_CLASSES)
1117 .map(|_| AtomicPtr::new(std::ptr::null_mut()))
1118 .collect(),
1119 retired_nodes: CachePadded::new(Mutex::new(Vec::new())),
1120 recycler_stats: RecyclerStats::new(),
1121 }
1122 }
1123
1124 pub fn reclaim_memory(&self) -> Result<usize> {
1126 let mut reclaimed = 0;
1127 let mut retired = self.retired_nodes.lock().expect("Operation failed");
1128
1129 for node_ptr in retired.drain(..) {
1131 unsafe {
1132 let node = Box::from_raw(node_ptr);
1133 reclaimed += node.size;
1134 }
1135 }
1136
1137 self.recycler_stats
1138 .memory_reclaimed
1139 .fetch_add(reclaimed, Ordering::Relaxed);
1140 Ok(reclaimed)
1141 }
1142}
1143
1144#[derive(Debug)]
1147pub struct SystemAllocator;
1148
1149impl CustomAllocator for SystemAllocator {
1150 fn allocate(&self, size: usize, alignment: usize) -> Result<NonNull<u8>> {
1151 let layout = Layout::from_size_align(size, alignment)
1152 .map_err(|_| MetricsError::MemoryError("Invalid layout".to_string()))?;
1153
1154 let ptr = unsafe { alloc(layout) };
1155 if ptr.is_null() {
1156 return Err(MetricsError::MemoryError(
1157 "System allocation failed".to_string(),
1158 ));
1159 }
1160
1161 Ok(NonNull::new(ptr).expect("Operation failed"))
1162 }
1163
1164 fn deallocate(&self, ptr: NonNull<u8>, size: usize, alignment: usize) {
1165 let layout = Layout::from_size_align(size, alignment).expect("Operation failed");
1166 unsafe { dealloc(ptr.as_ptr(), layout) };
1167 }
1168
1169 fn reallocate(
1170 &self,
1171 ptr: NonNull<u8>,
1172 old_size: usize,
1173 newsize: usize,
1174 alignment: usize,
1175 ) -> Result<NonNull<u8>> {
1176 let new_ptr = self.allocate(newsize, alignment)?;
1177 unsafe {
1178 std::ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr(), old_size.min(newsize));
1179 }
1180 self.deallocate(ptr, old_size, alignment);
1181 Ok(new_ptr)
1182 }
1183
1184 fn get_stats(&self) -> AllocatorStats {
1185 AllocatorStats::new()
1186 }
1187
1188 fn reset(&self) {
1189 }
1191}
1192
1193#[derive(Debug)]
1194pub struct PoolAllocator {
1195 block_size: usize,
1196}
1197
1198impl PoolAllocator {
1199 pub fn new(_blocksize: usize) -> Self {
1200 Self {
1201 block_size: _blocksize,
1202 }
1203 }
1204}
1205
1206impl CustomAllocator for PoolAllocator {
1207 fn allocate(&self, size: usize, alignment: usize) -> Result<NonNull<u8>> {
1208 if size > self.block_size {
1209 return Err(MetricsError::MemoryError(
1210 "Size exceeds pool block size".to_string(),
1211 ));
1212 }
1213
1214 let layout = Layout::from_size_align(self.block_size, alignment)
1215 .map_err(|_| MetricsError::MemoryError("Invalid pool layout".to_string()))?;
1216
1217 let ptr = unsafe { alloc(layout) };
1218 if ptr.is_null() {
1219 return Err(MetricsError::MemoryError(
1220 "Pool allocation failed".to_string(),
1221 ));
1222 }
1223
1224 Ok(NonNull::new(ptr).expect("Operation failed"))
1225 }
1226
1227 fn deallocate(&self, ptr: NonNull<u8>, size: usize, alignment: usize) {
1228 let layout = Layout::from_size_align(self.block_size, alignment).expect("Operation failed");
1229 unsafe { dealloc(ptr.as_ptr(), layout) };
1230 }
1231
1232 fn reallocate(
1233 &self,
1234 ptr: NonNull<u8>,
1235 old_size: usize,
1236 newsize: usize,
1237 alignment: usize,
1238 ) -> Result<NonNull<u8>> {
1239 if newsize <= self.block_size {
1240 Ok(ptr) } else {
1242 let new_ptr = self.allocate(newsize, alignment)?;
1243 unsafe {
1244 std::ptr::copy_nonoverlapping(
1245 ptr.as_ptr(),
1246 new_ptr.as_ptr(),
1247 old_size.min(newsize),
1248 );
1249 }
1250 self.deallocate(ptr, old_size, alignment);
1251 Ok(new_ptr)
1252 }
1253 }
1254
1255 fn get_stats(&self) -> AllocatorStats {
1256 AllocatorStats::new()
1257 }
1258
1259 fn reset(&self) {
1260 }
1262}
1263
1264#[derive(Debug)]
1265pub struct SimdAllocatorWrapper;
1266
1267impl SimdAllocatorWrapper {
1268 pub fn new() -> Self {
1269 Self
1270 }
1271}
1272
1273impl CustomAllocator for SimdAllocatorWrapper {
1274 fn allocate(&self, size: usize, alignment: usize) -> Result<NonNull<u8>> {
1275 let simd_alignment = alignment.max(32).next_power_of_two(); let layout = Layout::from_size_align(size, simd_alignment)
1278 .map_err(|_| MetricsError::MemoryError("Invalid SIMD layout".to_string()))?;
1279
1280 let ptr = unsafe { alloc(layout) };
1281 if ptr.is_null() {
1282 return Err(MetricsError::MemoryError(
1283 "SIMD allocation failed".to_string(),
1284 ));
1285 }
1286
1287 Ok(NonNull::new(ptr).expect("Operation failed"))
1288 }
1289
1290 fn deallocate(&self, ptr: NonNull<u8>, size: usize, alignment: usize) {
1291 let simd_alignment = alignment.max(32).next_power_of_two();
1292 let layout = Layout::from_size_align(size, simd_alignment).expect("Operation failed");
1293 unsafe { dealloc(ptr.as_ptr(), layout) };
1294 }
1295
1296 fn reallocate(
1297 &self,
1298 ptr: NonNull<u8>,
1299 old_size: usize,
1300 newsize: usize,
1301 alignment: usize,
1302 ) -> Result<NonNull<u8>> {
1303 let new_ptr = self.allocate(newsize, alignment)?;
1304 unsafe {
1305 std::ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr(), old_size.min(newsize));
1306 }
1307 self.deallocate(ptr, old_size, alignment);
1308 Ok(new_ptr)
1309 }
1310
1311 fn get_stats(&self) -> AllocatorStats {
1312 AllocatorStats::new()
1313 }
1314
1315 fn reset(&self) {
1316 }
1318}
1319
1320#[derive(Debug)]
1321pub struct ArenaAllocatorWrapper;
1322
1323impl ArenaAllocatorWrapper {
1324 pub fn new() -> Self {
1325 Self
1326 }
1327}
1328
1329impl CustomAllocator for ArenaAllocatorWrapper {
1330 fn allocate(&self, size: usize, alignment: usize) -> Result<NonNull<u8>> {
1331 let layout = Layout::from_size_align(size, alignment)
1333 .map_err(|_| MetricsError::MemoryError("Invalid arena layout".to_string()))?;
1334
1335 let ptr = unsafe { alloc(layout) };
1336 if ptr.is_null() {
1337 return Err(MetricsError::MemoryError(
1338 "Arena allocation failed".to_string(),
1339 ));
1340 }
1341
1342 Ok(NonNull::new(ptr).expect("Operation failed"))
1343 }
1344
1345 fn deallocate(&self, ptr: NonNull<u8>, size: usize, alignment: usize) {
1346 let layout = Layout::from_size_align(size, alignment).expect("Operation failed");
1347 unsafe { dealloc(ptr.as_ptr(), layout) };
1348 }
1349
1350 fn reallocate(
1351 &self,
1352 ptr: NonNull<u8>,
1353 old_size: usize,
1354 newsize: usize,
1355 alignment: usize,
1356 ) -> Result<NonNull<u8>> {
1357 let new_ptr = self.allocate(newsize, alignment)?;
1358 unsafe {
1359 std::ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr(), old_size.min(newsize));
1360 }
1361 self.deallocate(ptr, old_size, alignment);
1362 Ok(new_ptr)
1363 }
1364
1365 fn get_stats(&self) -> AllocatorStats {
1366 AllocatorStats::new()
1367 }
1368
1369 fn reset(&self) {
1370 }
1372}
1373
1374impl MemoryStats {
1377 pub fn new() -> Self {
1378 Self {
1379 total_allocated: AtomicUsize::new(0),
1380 total_deallocated: AtomicUsize::new(0),
1381 peak_usage: AtomicUsize::new(0),
1382 current_usage: AtomicUsize::new(0),
1383 allocation_count: AtomicUsize::new(0),
1384 deallocation_count: AtomicUsize::new(0),
1385 fragmentation_ratio: AtomicUsize::new(0),
1386 }
1387 }
1388}
1389
1390impl PoolStatistics {
1391 pub fn new() -> Self {
1392 Self {
1393 hits: AtomicUsize::new(0),
1394 misses: AtomicUsize::new(0),
1395 utilization: AtomicUsize::new(0),
1396 avg_allocation_time: AtomicUsize::new(0),
1397 }
1398 }
1399}
1400
1401impl SimdStats {
1402 pub fn new() -> Self {
1403 Self {
1404 allocations_by_alignment: HashMap::new(),
1405 simd_memory_usage: AtomicUsize::new(0),
1406 vectorization_efficiency: AtomicUsize::new(0),
1407 }
1408 }
1409}
1410
1411impl ArenaStats {
1412 pub fn new() -> Self {
1413 Self {
1414 arenas_created: AtomicUsize::new(0),
1415 total_arena_memory: AtomicUsize::new(0),
1416 arena_utilization: AtomicUsize::new(0),
1417 fragmentation_waste: AtomicUsize::new(0),
1418 }
1419 }
1420}
1421
1422impl MappingStats {
1423 pub fn new() -> Self {
1424 Self {
1425 active_mappings: AtomicUsize::new(0),
1426 total_mapped_memory: AtomicUsize::new(0),
1427 cache_hits: AtomicUsize::new(0),
1428 cache_misses: AtomicUsize::new(0),
1429 }
1430 }
1431}
1432
1433impl RecyclerStats {
1434 pub fn new() -> Self {
1435 Self {
1436 successful_recycles: AtomicUsize::new(0),
1437 failed_recycles: AtomicUsize::new(0),
1438 hazard_contentions: AtomicUsize::new(0),
1439 memory_reclaimed: AtomicUsize::new(0),
1440 }
1441 }
1442}
1443
1444impl AllocatorStats {
1445 pub fn new() -> Self {
1446 Self {
1447 allocation_requests: AtomicUsize::new(0),
1448 deallocation_requests: AtomicUsize::new(0),
1449 bytes_allocated: AtomicUsize::new(0),
1450 bytes_deallocated: AtomicUsize::new(0),
1451 allocation_failures: AtomicUsize::new(0),
1452 }
1453 }
1454}
1455
1456impl SlabStats {
1457 pub fn new() -> Self {
1458 Self {
1459 slabs_allocated: AtomicUsize::new(0),
1460 objects_allocated: AtomicUsize::new(0),
1461 slab_utilization: AtomicUsize::new(0),
1462 internal_fragmentation: AtomicUsize::new(0),
1463 }
1464 }
1465}
1466
1467impl BuddyStats {
1468 pub fn new() -> Self {
1469 Self {
1470 allocations_by_order: (0..32).map(|_| AtomicUsize::new(0)).collect(),
1471 coalescing_operations: AtomicUsize::new(0),
1472 splitting_operations: AtomicUsize::new(0),
1473 external_fragmentation: AtomicUsize::new(0),
1474 }
1475 }
1476}
1477
1478impl<T> ZeroCopyBuffer<T> {
1481 pub fn len(&self) -> usize {
1483 self.length
1484 }
1485
1486 pub fn is_empty(&self) -> bool {
1488 self.length == 0
1489 }
1490
1491 pub fn capacity(&self) -> usize {
1493 self.capacity
1494 }
1495
1496 pub fn push(&mut self, value: T) -> Result<()> {
1498 if self.length >= self.capacity {
1499 return Err(MetricsError::MemoryError(
1500 "Buffer capacity exceeded".to_string(),
1501 ));
1502 }
1503
1504 unsafe {
1505 std::ptr::write(self.data.as_ptr().add(self.length), value);
1506 }
1507 self.length += 1;
1508 Ok(())
1509 }
1510
1511 pub fn as_slice(&self) -> &[T] {
1513 unsafe { std::slice::from_raw_parts(self.data.as_ptr(), self.length) }
1514 }
1515
1516 pub fn as_mut_slice(&mut self) -> &mut [T] {
1518 unsafe { std::slice::from_raw_parts_mut(self.data.as_ptr(), self.length) }
1519 }
1520
1521 pub fn resize(&mut self, newsize: usize) -> Result<()> {
1523 if newsize <= self.capacity {
1524 self.length = newsize;
1525 Ok(())
1526 } else {
1527 let new_ptr = self.allocator.reallocate(
1529 self.data.cast::<u8>(),
1530 self.layout.size(),
1531 newsize * size_of::<T>(),
1532 self.layout.align(),
1533 )?;
1534
1535 self.data = new_ptr.cast::<T>();
1536 self.capacity = newsize;
1537 self.length = newsize;
1538 Ok(())
1539 }
1540 }
1541}
1542
1543impl<T> Drop for ZeroCopyBuffer<T> {
1544 fn drop(&mut self) {
1545 for i in 0..self.length {
1547 unsafe {
1548 std::ptr::drop_in_place(self.data.as_ptr().add(i));
1549 }
1550 }
1551
1552 self.allocator.deallocate(
1554 self.data.cast::<u8>(),
1555 self.layout.size(),
1556 self.layout.align(),
1557 );
1558 }
1559}
1560
1561impl<'a, T> ZeroCopyArrayView<'a, T> {
1562 pub fn len(&self) -> usize {
1564 self.len
1565 }
1566
1567 pub fn is_empty(&self) -> bool {
1569 self.len == 0
1570 }
1571
1572 pub fn get(&self, index: usize) -> Option<&T> {
1574 if index < self.len {
1575 unsafe { Some(&*self.data.as_ptr().add(index)) }
1576 } else {
1577 None
1578 }
1579 }
1580
1581 pub fn as_slice(&self) -> &[T] {
1583 unsafe { std::slice::from_raw_parts(self.data.as_ptr(), self.len) }
1584 }
1585
1586 pub fn subview(&self, start: usize, len: usize) -> Result<ZeroCopyArrayView<'a, T>> {
1588 if start + len > self.len {
1589 return Err(MetricsError::IndexError(
1590 "Subview bounds exceed array".to_string(),
1591 ));
1592 }
1593
1594 Ok(ZeroCopyArrayView {
1595 data: unsafe { NonNull::new_unchecked(self.data.as_ptr().add(start)) },
1596 len,
1597 _lifetime: std::marker::PhantomData,
1598 memory_manager: self.memory_manager,
1599 })
1600 }
1601}
1602
1603impl<'a, T> ZeroCopyArrayViewMut<'a, T> {
1604 pub fn len(&self) -> usize {
1606 self.len
1607 }
1608
1609 pub fn is_empty(&self) -> bool {
1611 self.len == 0
1612 }
1613
1614 pub fn get(&self, index: usize) -> Option<&T> {
1616 if index < self.len {
1617 unsafe { Some(&*self.data.as_ptr().add(index)) }
1618 } else {
1619 None
1620 }
1621 }
1622
1623 pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
1625 if index < self.len {
1626 unsafe { Some(&mut *self.data.as_ptr().add(index)) }
1627 } else {
1628 None
1629 }
1630 }
1631
1632 pub fn as_slice(&self) -> &[T] {
1634 unsafe { std::slice::from_raw_parts(self.data.as_ptr(), self.len) }
1635 }
1636
1637 pub fn as_mut_slice(&mut self) -> &mut [T] {
1639 unsafe { std::slice::from_raw_parts_mut(self.data.as_ptr(), self.len) }
1640 }
1641}
1642
1643#[cfg(test)]
1644mod tests {
1645 use super::*;
1646 use scirs2_core::ndarray::Array1;
1647
1648 struct StreamingMAE;
1650
1651 impl StreamingMetric<f64> for StreamingMAE {
1652 type State = (f64, usize); fn init_state(&self) -> Self::State {
1655 (0.0, 0)
1656 }
1657
1658 fn update_state(
1659 &self,
1660 state: &mut Self::State,
1661 batch_true: &[f64],
1662 batch_pred: &[f64],
1663 ) -> Result<()> {
1664 for (y_t, y_p) in batch_true.iter().zip(batch_pred.iter()) {
1665 state.0 += (y_t - y_p).abs();
1666 state.1 += 1;
1667 }
1668 Ok(())
1669 }
1670
1671 fn finalize(&self, state: &Self::State) -> Result<f64> {
1672 if state.1 == 0 {
1673 return Err(MetricsError::DivisionByZero);
1674 }
1675 Ok(state.0 / state.1 as f64)
1676 }
1677 }
1678
1679 #[test]
1680 fn test_chunked_streaming_metric() {
1681 let y_true = Array1::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
1683 let y_pred = Array1::from_vec(vec![1.2, 2.3, 2.9, 4.1, 5.2]);
1684
1685 let chunked = ChunkedMetrics::new().with_chunk_size(2);
1687 let mae = chunked
1688 .compute_streaming(&y_true, &y_pred, &StreamingMAE)
1689 .expect("Operation failed");
1690
1691 let expected_mae = y_true
1693 .iter()
1694 .zip(y_pred.iter())
1695 .map(|(t, p)| (t - p).abs())
1696 .sum::<f64>()
1697 / y_true.len() as f64;
1698
1699 assert!((mae - expected_mae).abs() < 1e-10);
1700 }
1701
1702 #[test]
1703 fn test_compute_rowwise() {
1704 let data: Vec<f64> = (0..100).map(|x| x as f64).collect();
1706
1707 let row_op = |chunk: &[f64]| -> Result<f64> { Ok(chunk.iter().map(|x| x * x).sum()) };
1709
1710 let combine = |results: &[f64]| -> Result<f64> { Ok(results.iter().sum()) };
1712
1713 let chunked = ChunkedMetrics::new().with_chunk_size(10);
1715 let result = chunked
1716 .compute_rowwise(&data, row_op, combine)
1717 .expect("Operation failed");
1718
1719 let expected: f64 = data.iter().map(|x| x * x).sum();
1721
1722 assert!((result - expected).abs() < 1e-10);
1723 }
1724
1725 #[test]
1726 fn test_incremental_metrics() {
1727 let data = vec![(1.0, 1.2), (2.0, 1.8), (3.0, 3.1), (4.0, 4.2), (5.0, 4.9)];
1729
1730 let mse_update = |state: &mut f64, y_true: f64, y_pred: f64| -> Result<()> {
1732 *state += (y_true - y_pred).powi(2);
1733 Ok(())
1734 };
1735
1736 let mse_finalize = |state: &f64, count: usize| -> Result<f64> {
1738 if count == 0 {
1739 return Err(MetricsError::DivisionByZero);
1740 }
1741 Ok(*state / count as f64)
1742 };
1743
1744 let expected_mse =
1746 data.iter().map(|&(t, p)| (t - p) * (t - p)).sum::<f64>() / data.len() as f64;
1747
1748 let mut incremental = IncrementalMetrics::<f64, f64>::new();
1750
1751 for &(y_true, y_pred) in &data {
1752 incremental
1753 .update(y_true, y_pred, mse_update)
1754 .expect("Operation failed");
1755 }
1756
1757 let mse = incremental
1758 .finalize(mse_finalize)
1759 .expect("Operation failed");
1760 assert!((mse - expected_mse).abs() < 1e-10);
1761
1762 let (y_true, y_pred): (Vec<_>, Vec<_>) = data.iter().cloned().unzip();
1764
1765 let batch_update = |state: &mut f64, y_true: &[f64], y_pred: &[f64]| -> Result<()> {
1766 for (t, p) in y_true.iter().zip(y_pred.iter()) {
1767 *state += (t - p).powi(2);
1768 }
1769 Ok(())
1770 };
1771
1772 let mut incremental_batch = IncrementalMetrics::<f64, f64>::new();
1773 incremental_batch
1774 .update_batch(&y_true, &y_pred, batch_update)
1775 .expect("Operation failed");
1776
1777 let mse_batch = incremental_batch
1778 .finalize(mse_finalize)
1779 .expect("Operation failed");
1780 assert!((mse_batch - expected_mse).abs() < 1e-10);
1781 }
1782}