xlog_prob/
neural_fast_path.rs1use xlog_core::{Result, XlogError};
7use xlog_cuda::memory::{DeviceMemoryView, TrackedCudaSlice};
8use xlog_cuda::CudaKernelProvider;
9
10#[derive(Debug, Clone, Copy)]
15#[non_exhaustive]
16pub struct NeuralFastPathConfig {
17 pub eps: f64,
19 pub min_p: f64,
21}
22
23impl Default for NeuralFastPathConfig {
24 fn default() -> Self {
25 Self {
26 eps: 1e-7,
27 min_p: 1e-12,
28 }
29 }
30}
31
32pub struct GpuWeightSlots {
38 group_offsets_host: Vec<u32>,
39 group_offsets: TrackedCudaSlice<u32>, slot_cnf_var: TrackedCudaSlice<u32>, }
42
43impl GpuWeightSlots {
44 pub fn upload(provider: &CudaKernelProvider, groups: &[Vec<u32>]) -> Result<Self> {
48 if groups.len() > u32::MAX as usize {
49 return Err(XlogError::Compilation(
50 "Neural fast-path group count exceeds GPU u32 index space".to_string(),
51 ));
52 }
53 let offset_count = groups.len().checked_add(1).ok_or_else(|| {
54 XlogError::Compilation("Neural fast-path group offset count overflow".to_string())
55 })?;
56 let total_slots = groups.iter().try_fold(0usize, |acc, group| {
57 acc.checked_add(group.len()).ok_or_else(|| {
58 XlogError::Compilation("Neural fast-path slot count overflow".to_string())
59 })
60 })?;
61 if total_slots > u32::MAX as usize {
62 return Err(XlogError::Compilation(
63 "Neural fast-path slot count exceeds GPU u32 index space".to_string(),
64 ));
65 }
66
67 let mut offsets: Vec<u32> = Vec::with_capacity(offset_count);
68 offsets.push(0);
69
70 let mut flat: Vec<u32> = Vec::with_capacity(total_slots);
71 for g in groups {
72 flat.extend_from_slice(g);
73 let next_offset = u32::try_from(flat.len()).map_err(|_| {
74 XlogError::Compilation(
75 "Neural fast-path slot offset exceeds GPU u32 index space".to_string(),
76 )
77 })?;
78 offsets.push(next_offset);
79 }
80
81 let memory = provider.memory().clone();
82
83 let mut d_offsets = memory.alloc::<u32>(offsets.len())?;
84 provider
85 .htod_sync_copy_into_tracked(&offsets, &mut d_offsets)
86 .map_err(|e| {
87 XlogError::Kernel(format!("Failed to upload weight slot offsets: {}", e))
88 })?;
89
90 let mut d_vars = memory.alloc::<u32>(flat.len())?;
91 provider
92 .htod_sync_copy_into_tracked(&flat, &mut d_vars)
93 .map_err(|e| XlogError::Kernel(format!("Failed to upload weight slot vars: {}", e)))?;
94
95 Ok(Self {
96 group_offsets_host: offsets,
97 group_offsets: d_offsets,
98 slot_cnf_var: d_vars,
99 })
100 }
101
102 pub fn num_groups(&self) -> u32 {
104 debug_assert!(!self.group_offsets_host.is_empty());
105 (self.group_offsets_host.len() - 1) as u32
106 }
107
108 pub fn num_groups_usize(&self) -> usize {
110 debug_assert!(!self.group_offsets_host.is_empty());
111 self.group_offsets_host.len() - 1
112 }
113
114 pub fn total_slots(&self) -> u32 {
116 debug_assert!(!self.group_offsets_host.is_empty());
117 self.group_offsets_host[self.group_offsets_host.len() - 1]
118 }
119
120 pub fn group_offsets(&self) -> &TrackedCudaSlice<u32> {
122 &self.group_offsets
123 }
124
125 pub fn slot_cnf_var(&self) -> &TrackedCudaSlice<u32> {
127 &self.slot_cnf_var
128 }
129
130 pub fn group_slot_cnf_var(&self, group_idx: usize) -> Result<DeviceMemoryView<u32>> {
132 let start = *self
133 .group_offsets_host
134 .get(group_idx)
135 .ok_or_else(|| XlogError::Compilation("Group index out of bounds".to_string()))?
136 as usize;
137 let end = *self
138 .group_offsets_host
139 .get(group_idx + 1)
140 .ok_or_else(|| XlogError::Compilation("Group index out of bounds".to_string()))?
141 as usize;
142 if end < start || end > self.slot_cnf_var.len() {
143 return Err(XlogError::Compilation(
144 "Invalid group slot range in GpuWeightSlots".to_string(),
145 ));
146 }
147 Ok(self.slot_cnf_var.slice(start..end))
148 }
149}