xlog_prob/compilation/
gpu_pir.rs1use std::sync::Arc;
6
7use xlog_core::{Result, XlogError};
8use xlog_cuda::memory::TrackedCudaSlice;
9use xlog_cuda::CudaKernelProvider;
10
11use crate::pir::{PirGraph, PirNode, PirNodeId};
12
13pub(crate) const PIR_CONST: u8 = 0;
15pub const PIR_LIT: u8 = 1;
17pub const PIR_NEG_LIT: u8 = 2;
19pub const PIR_AND: u8 = 3;
21pub const PIR_OR: u8 = 4;
23pub(crate) const PIR_DECISION: u8 = 5;
24
25pub struct GpuPirGraph {
27 pub node_type: TrackedCudaSlice<u8>,
29 pub child_offsets: TrackedCudaSlice<u32>,
31 pub children: TrackedCudaSlice<u32>,
33 pub leaf_id: TrackedCudaSlice<u32>,
35 pub decision_var: TrackedCudaSlice<u32>,
37 pub decision_child_false: TrackedCudaSlice<u32>,
39 pub decision_child_true: TrackedCudaSlice<u32>,
41}
42
43pub struct GpuPirRoots {
45 pub roots: TrackedCudaSlice<u32>,
47}
48
49impl GpuPirGraph {
50 pub fn from_host(pir: &PirGraph, provider: &Arc<CudaKernelProvider>) -> Result<Self> {
55 let num_nodes = pir.len();
56 let num_nodes_u32 = u32::try_from(num_nodes).map_err(|_| {
57 XlogError::Compilation("GpuPirGraph::from_host: node count overflow".to_string())
58 })?;
59
60 let mut node_type: Vec<u8> = Vec::with_capacity(num_nodes);
61 let mut child_offsets: Vec<u32> = Vec::with_capacity(num_nodes + 1);
62 let mut children: Vec<u32> = Vec::new();
63 let mut leaf_id: Vec<u32> = Vec::with_capacity(num_nodes);
64 let mut decision_var: Vec<u32> = Vec::with_capacity(num_nodes);
65 let mut decision_child_false: Vec<u32> = Vec::with_capacity(num_nodes);
66 let mut decision_child_true: Vec<u32> = Vec::with_capacity(num_nodes);
67
68 child_offsets.push(0);
69
70 for (idx, node) in pir.nodes().iter().enumerate() {
71 let node_id = u32::try_from(idx).map_err(|_| {
72 XlogError::Compilation("GpuPirGraph::from_host: node id overflow".to_string())
73 })?;
74
75 match node {
76 PirNode::Const(value) => {
77 node_type.push(PIR_CONST);
78 leaf_id.push(u32::from(*value));
79 decision_var.push(0);
80 decision_child_false.push(0);
81 decision_child_true.push(0);
82 }
83 PirNode::Lit { leaf } => {
84 node_type.push(PIR_LIT);
85 leaf_id.push(leaf.as_u32());
86 decision_var.push(0);
87 decision_child_false.push(0);
88 decision_child_true.push(0);
89 }
90 PirNode::NegLit { leaf } => {
91 node_type.push(PIR_NEG_LIT);
92 leaf_id.push(leaf.as_u32());
93 decision_var.push(0);
94 decision_child_false.push(0);
95 decision_child_true.push(0);
96 }
97 PirNode::And { children: kids } => {
98 validate_children_sorted(node_id, kids, num_nodes_u32)?;
99 node_type.push(PIR_AND);
100 leaf_id.push(0);
101 decision_var.push(0);
102 decision_child_false.push(0);
103 decision_child_true.push(0);
104 for &child in kids {
105 children.push(child.as_u32());
106 }
107 }
108 PirNode::Or { children: kids } => {
109 validate_children_sorted(node_id, kids, num_nodes_u32)?;
110 node_type.push(PIR_OR);
111 leaf_id.push(0);
112 decision_var.push(0);
113 decision_child_false.push(0);
114 decision_child_true.push(0);
115 for &child in kids {
116 children.push(child.as_u32());
117 }
118 }
119 PirNode::Decision {
120 var,
121 child_false,
122 child_true,
123 } => {
124 validate_child_id(node_id, *child_false, num_nodes_u32)?;
125 validate_child_id(node_id, *child_true, num_nodes_u32)?;
126 node_type.push(PIR_DECISION);
127 leaf_id.push(0);
128 decision_var.push(var.as_u32());
129 decision_child_false.push(child_false.as_u32());
130 decision_child_true.push(child_true.as_u32());
131 }
132 }
133
134 let next_off = u32::try_from(children.len()).map_err(|_| {
135 XlogError::Compilation(
136 "GpuPirGraph::from_host: children count exceeds u32".to_string(),
137 )
138 })?;
139 child_offsets.push(next_off);
140 }
141
142 if child_offsets.len() != num_nodes + 1 {
143 return Err(XlogError::Compilation(
144 "GpuPirGraph::from_host: child_offsets length mismatch".to_string(),
145 ));
146 }
147
148 let memory = provider.memory();
149
150 let mut d_node_type = memory.alloc::<u8>(node_type.len())?;
151 let mut d_child_offsets = memory.alloc::<u32>(child_offsets.len())?;
152 let mut d_children = memory.alloc::<u32>(children.len())?;
153 let mut d_leaf_id = memory.alloc::<u32>(leaf_id.len())?;
154 let mut d_decision_var = memory.alloc::<u32>(decision_var.len())?;
155 let mut d_decision_child_false = memory.alloc::<u32>(decision_child_false.len())?;
156 let mut d_decision_child_true = memory.alloc::<u32>(decision_child_true.len())?;
157
158 provider
159 .htod_sync_copy_into_tracked(&node_type, &mut d_node_type)
160 .map_err(|e| XlogError::Kernel(format!("GpuPirGraph upload node_type: {}", e)))?;
161 provider
162 .htod_sync_copy_into_tracked(&child_offsets, &mut d_child_offsets)
163 .map_err(|e| XlogError::Kernel(format!("GpuPirGraph upload child_offsets: {}", e)))?;
164 provider
165 .htod_sync_copy_into_tracked(&children, &mut d_children)
166 .map_err(|e| XlogError::Kernel(format!("GpuPirGraph upload children: {}", e)))?;
167 provider
168 .htod_sync_copy_into_tracked(&leaf_id, &mut d_leaf_id)
169 .map_err(|e| XlogError::Kernel(format!("GpuPirGraph upload leaf_id: {}", e)))?;
170 provider
171 .htod_sync_copy_into_tracked(&decision_var, &mut d_decision_var)
172 .map_err(|e| XlogError::Kernel(format!("GpuPirGraph upload decision_var: {}", e)))?;
173 provider
174 .htod_sync_copy_into_tracked(&decision_child_false, &mut d_decision_child_false)
175 .map_err(|e| {
176 XlogError::Kernel(format!("GpuPirGraph upload decision_child_false: {}", e))
177 })?;
178 provider
179 .htod_sync_copy_into_tracked(&decision_child_true, &mut d_decision_child_true)
180 .map_err(|e| {
181 XlogError::Kernel(format!("GpuPirGraph upload decision_child_true: {}", e))
182 })?;
183
184 Ok(Self {
185 node_type: d_node_type,
186 child_offsets: d_child_offsets,
187 children: d_children,
188 leaf_id: d_leaf_id,
189 decision_var: d_decision_var,
190 decision_child_false: d_decision_child_false,
191 decision_child_true: d_decision_child_true,
192 })
193 }
194
195 pub fn num_nodes(&self) -> usize {
197 self.node_type.len()
198 }
199}
200
201impl GpuPirRoots {
202 pub fn from_host(roots: &[PirNodeId], provider: &Arc<CudaKernelProvider>) -> Result<Self> {
204 let mut host: Vec<u32> = Vec::with_capacity(roots.len());
205 for &r in roots {
206 host.push(r.as_u32());
207 }
208
209 let memory = provider.memory();
210 let mut d_roots = memory.alloc::<u32>(host.len())?;
211 provider
212 .htod_sync_copy_into_tracked(&host, &mut d_roots)
213 .map_err(|e| XlogError::Kernel(format!("GpuPirRoots upload: {}", e)))?;
214
215 Ok(Self { roots: d_roots })
216 }
217}
218
219fn validate_child_id(parent: u32, child: PirNodeId, num_nodes: u32) -> Result<()> {
220 let id = child.as_u32();
221 if id >= num_nodes {
222 return Err(XlogError::Compilation(format!(
223 "GpuPirGraph::from_host: child {:?} out of bounds for parent {}",
224 child, parent
225 )));
226 }
227 Ok(())
228}
229
230fn validate_children_sorted(parent: u32, children: &[PirNodeId], num_nodes: u32) -> Result<()> {
231 let mut prev: Option<u32> = None;
232 for &child in children {
233 let id = child.as_u32();
234 if id >= num_nodes {
235 return Err(XlogError::Compilation(format!(
236 "GpuPirGraph::from_host: child {:?} out of bounds for parent {}",
237 child, parent
238 )));
239 }
240 if let Some(p) = prev {
241 if id <= p {
242 return Err(XlogError::Compilation(format!(
243 "GpuPirGraph::from_host: children of {} must be sorted and unique",
244 parent
245 )));
246 }
247 }
248 prev = Some(id);
249 }
250 Ok(())
251}