1use std::ffi::c_void;
6use std::sync::Arc;
7
8use cudarc::driver::LaunchConfig;
9use xlog_core::{Result, XlogError};
10use xlog_cuda::memory::TrackedCudaSlice;
11use xlog_cuda::provider::{pir_kernels, scan_kernels, RadixSortScratch, PIR_MODULE, SCAN_MODULE};
12use xlog_cuda::{AsKernelParam, CudaKernelProvider, LaunchAsync};
13
14use crate::compilation::gpu_pir::{GpuPirGraph, PIR_AND, PIR_CONST};
15
16#[derive(Debug, Clone)]
18pub struct PirBatch {
19 pub node_type: Vec<u8>,
21 pub leaf_id: Vec<u32>,
23 pub decision_var: Vec<u32>,
25 pub decision_child_false: Vec<u32>,
27 pub decision_child_true: Vec<u32>,
29 pub child_offsets: Vec<u32>,
31 pub children: Vec<u32>,
33}
34
35impl PirBatch {
36 pub fn len(&self) -> usize {
38 self.node_type.len()
39 }
40
41 pub fn is_empty(&self) -> bool {
43 self.node_type.is_empty()
44 }
45
46 pub fn and_or_batch(children: Vec<Vec<u32>>) -> Self {
50 let num_nodes = children.len();
51 let mut node_type = vec![PIR_AND; num_nodes];
52 let leaf_id = vec![0u32; num_nodes];
53 let decision_var = vec![0u32; num_nodes];
54 let decision_child_false = vec![0u32; num_nodes];
55 let decision_child_true = vec![0u32; num_nodes];
56
57 let mut child_offsets = Vec::with_capacity(num_nodes + 1);
58 let mut flat_children = Vec::new();
59 child_offsets.push(0);
60 for kids in children {
61 flat_children.extend(kids);
62 child_offsets.push(flat_children.len() as u32);
63 }
64
65 if node_type.is_empty() {
66 node_type = Vec::new();
67 }
68
69 Self {
70 node_type,
71 leaf_id,
72 decision_var,
73 decision_child_false,
74 decision_child_true,
75 child_offsets,
76 children: flat_children,
77 }
78 }
79
80 pub fn to_device(&self, provider: &Arc<CudaKernelProvider>) -> Result<GpuPirBatch> {
82 let num_nodes = self.node_type.len();
83 if self.leaf_id.len() != num_nodes
84 || self.decision_var.len() != num_nodes
85 || self.decision_child_false.len() != num_nodes
86 || self.decision_child_true.len() != num_nodes
87 {
88 return Err(XlogError::Compilation(
89 "PirBatch: array length mismatch".to_string(),
90 ));
91 }
92 if self.child_offsets.len() != num_nodes + 1 {
93 return Err(XlogError::Compilation(
94 "PirBatch: child_offsets must be len num_nodes+1".to_string(),
95 ));
96 }
97 if let Some(&last) = self.child_offsets.last() {
98 if last as usize != self.children.len() {
99 return Err(XlogError::Compilation(
100 "PirBatch: child_offsets last entry must equal children len".to_string(),
101 ));
102 }
103 }
104
105 let memory = provider.memory();
106
107 let mut d_node_type = memory.alloc::<u8>(num_nodes)?;
108 let mut d_leaf_id = memory.alloc::<u32>(num_nodes)?;
109 let mut d_decision_var = memory.alloc::<u32>(num_nodes)?;
110 let mut d_decision_child_false = memory.alloc::<u32>(num_nodes)?;
111 let mut d_decision_child_true = memory.alloc::<u32>(num_nodes)?;
112 let mut d_child_offsets = memory.alloc::<u32>(self.child_offsets.len())?;
113 let mut d_children = memory.alloc::<u32>(self.children.len())?;
114
115 provider
116 .htod_sync_copy_into_tracked(&self.node_type, &mut d_node_type)
117 .map_err(|e| XlogError::Kernel(format!("PirBatch upload node_type: {}", e)))?;
118 provider
119 .htod_sync_copy_into_tracked(&self.leaf_id, &mut d_leaf_id)
120 .map_err(|e| XlogError::Kernel(format!("PirBatch upload leaf_id: {}", e)))?;
121 provider
122 .htod_sync_copy_into_tracked(&self.decision_var, &mut d_decision_var)
123 .map_err(|e| XlogError::Kernel(format!("PirBatch upload decision_var: {}", e)))?;
124 provider
125 .htod_sync_copy_into_tracked(&self.decision_child_false, &mut d_decision_child_false)
126 .map_err(|e| {
127 XlogError::Kernel(format!("PirBatch upload decision_child_false: {}", e))
128 })?;
129 provider
130 .htod_sync_copy_into_tracked(&self.decision_child_true, &mut d_decision_child_true)
131 .map_err(|e| {
132 XlogError::Kernel(format!("PirBatch upload decision_child_true: {}", e))
133 })?;
134 provider
135 .htod_sync_copy_into_tracked(&self.child_offsets, &mut d_child_offsets)
136 .map_err(|e| XlogError::Kernel(format!("PirBatch upload child_offsets: {}", e)))?;
137 provider
138 .htod_sync_copy_into_tracked(&self.children, &mut d_children)
139 .map_err(|e| XlogError::Kernel(format!("PirBatch upload children: {}", e)))?;
140
141 Ok(GpuPirBatch {
142 node_type: d_node_type,
143 leaf_id: d_leaf_id,
144 decision_var: d_decision_var,
145 decision_child_false: d_decision_child_false,
146 decision_child_true: d_decision_child_true,
147 child_offsets: d_child_offsets,
148 children: d_children,
149 })
150 }
151}
152
153pub struct GpuPirBatch {
155 pub node_type: TrackedCudaSlice<u8>,
157 pub leaf_id: TrackedCudaSlice<u32>,
159 pub decision_var: TrackedCudaSlice<u32>,
161 pub decision_child_false: TrackedCudaSlice<u32>,
163 pub decision_child_true: TrackedCudaSlice<u32>,
165 pub child_offsets: TrackedCudaSlice<u32>,
167 pub children: TrackedCudaSlice<u32>,
169}
170
171impl GpuPirBatch {
172 pub fn num_nodes(&self) -> usize {
174 self.node_type.len()
175 }
176
177 pub fn num_children(&self) -> usize {
179 self.children.len()
180 }
181}
182
183pub struct GpuPirInterner {
185 provider: Arc<CudaKernelProvider>,
186 node_cap: u32,
187 child_cap: u32,
188 graph: GpuPirGraph,
189 graph_hashes: TrackedCudaSlice<u64>,
190 num_nodes: TrackedCudaSlice<u32>,
191 num_children: TrackedCudaSlice<u32>,
192}
193
194impl GpuPirInterner {
195 pub fn new(provider: &Arc<CudaKernelProvider>, node_cap: u32, child_cap: u32) -> Result<Self> {
197 if node_cap < 2 {
198 return Err(XlogError::Compilation(
199 "GpuPirInterner requires node_cap >= 2 for const nodes".to_string(),
200 ));
201 }
202 let memory = provider.memory();
203 let device = provider.device().inner();
204
205 let mut node_type = memory.alloc::<u8>(node_cap as usize)?;
206 let mut child_offsets = memory.alloc::<u32>((node_cap as usize) + 1)?;
207 let mut children = memory.alloc::<u32>(child_cap as usize)?;
208 let mut leaf_id = memory.alloc::<u32>(node_cap as usize)?;
209 let mut decision_var = memory.alloc::<u32>(node_cap as usize)?;
210 let mut decision_child_false = memory.alloc::<u32>(node_cap as usize)?;
211 let mut decision_child_true = memory.alloc::<u32>(node_cap as usize)?;
212 let mut graph_hashes = memory.alloc::<u64>(node_cap as usize)?;
213
214 device
215 .memset_zeros(&mut node_type)
216 .map_err(|e| XlogError::Kernel(format!("GpuPirInterner init node_type: {}", e)))?;
217 device
218 .memset_zeros(&mut child_offsets)
219 .map_err(|e| XlogError::Kernel(format!("GpuPirInterner init child_offsets: {}", e)))?;
220 device
221 .memset_zeros(&mut children)
222 .map_err(|e| XlogError::Kernel(format!("GpuPirInterner init children: {}", e)))?;
223 device
224 .memset_zeros(&mut decision_var)
225 .map_err(|e| XlogError::Kernel(format!("GpuPirInterner init decision_var: {}", e)))?;
226 device
227 .memset_zeros(&mut decision_child_false)
228 .map_err(|e| {
229 XlogError::Kernel(format!("GpuPirInterner init decision_child_false: {}", e))
230 })?;
231 device.memset_zeros(&mut decision_child_true).map_err(|e| {
232 XlogError::Kernel(format!("GpuPirInterner init decision_child_true: {}", e))
233 })?;
234 device
235 .memset_zeros(&mut graph_hashes)
236 .map_err(|e| XlogError::Kernel(format!("GpuPirInterner init graph_hashes: {}", e)))?;
237
238 let mut leaf_id_host = vec![0u32; node_cap as usize];
239 if node_cap > 1 {
240 leaf_id_host[1] = 1;
241 }
242 provider
243 .htod_launch_metadata_sync_copy_into(&leaf_id_host, &mut leaf_id)
244 .map_err(|e| XlogError::Kernel(format!("GpuPirInterner init leaf_id: {}", e)))?;
245
246 let hash_fn = device
247 .get_func(PIR_MODULE, pir_kernels::PIR_HASH_KEYS)
248 .ok_or_else(|| XlogError::Kernel("pir_hash_keys not found".to_string()))?;
249 let num_const = 2u32;
250 let block_size = 256u32;
251 let grid_const = num_const.div_ceil(block_size);
252 unsafe {
254 hash_fn.clone().launch(
255 LaunchConfig {
256 grid_dim: (grid_const.max(1), 1, 1),
257 block_dim: (block_size, 1, 1),
258 shared_mem_bytes: 0,
259 },
260 (
261 &node_type,
262 &leaf_id,
263 &decision_var,
264 &decision_child_false,
265 &decision_child_true,
266 &child_offsets,
267 &children,
268 num_const,
269 &mut graph_hashes,
270 ),
271 )
272 }
273 .map_err(|e| XlogError::Kernel(format!("GpuPirInterner init hash: {}", e)))?;
274
275 let mut num_nodes = memory.alloc::<u32>(1)?;
276 let mut num_children = memory.alloc::<u32>(1)?;
277 provider
278 .htod_launch_metadata_sync_copy_into(&[2u32], &mut num_nodes)
279 .map_err(|e| XlogError::Kernel(format!("GpuPirInterner init num_nodes: {}", e)))?;
280 provider
281 .htod_launch_metadata_sync_copy_into(&[0u32], &mut num_children)
282 .map_err(|e| XlogError::Kernel(format!("GpuPirInterner init num_children: {}", e)))?;
283
284 Ok(Self {
285 provider: Arc::clone(provider),
286 node_cap,
287 child_cap,
288 graph: GpuPirGraph {
289 node_type,
290 child_offsets,
291 children,
292 leaf_id,
293 decision_var,
294 decision_child_false,
295 decision_child_true,
296 },
297 graph_hashes,
298 num_nodes,
299 num_children,
300 })
301 }
302
303 pub fn graph(&self) -> &GpuPirGraph {
305 &self.graph
306 }
307
308 pub fn intern_batch(&mut self, batch: &PirBatch) -> Result<TrackedCudaSlice<u32>> {
310 if batch.node_type.contains(&PIR_CONST) {
311 return Err(XlogError::Compilation(
312 "GpuPirInterner does not accept PIR_CONST in batches".to_string(),
313 ));
314 }
315 let mut device_batch = batch.to_device(&self.provider)?;
316 self.intern_device_batch(&mut device_batch)
317 }
318
319 pub fn intern_device_batch(
321 &mut self,
322 batch: &mut GpuPirBatch,
323 ) -> Result<TrackedCudaSlice<u32>> {
324 let num_nodes = batch.num_nodes();
325 if num_nodes == 0 {
326 return self.provider.memory().alloc::<u32>(0);
327 }
328 let num_nodes_u32 = u32::try_from(num_nodes).map_err(|_| {
329 XlogError::Compilation("GpuPirInterner: num_nodes overflow".to_string())
330 })?;
331 let num_children = batch.num_children();
332 let num_children_u32 = u32::try_from(num_children).map_err(|_| {
333 XlogError::Compilation("GpuPirInterner: num_children overflow".to_string())
334 })?;
335
336 let device = self.provider.device().inner();
337 let memory = self.provider.memory();
338 let block_size = 256u32;
339
340 let mut canon_child_offsets = memory.alloc::<u32>(num_nodes + 1)?;
342 let mut canon_children = memory.alloc::<u32>(num_children)?;
343
344 if num_children_u32 == 0 {
345 device.memset_zeros(&mut canon_child_offsets).map_err(|e| {
346 XlogError::Kernel(format!("GpuPirInterner zero child_offsets: {}", e))
347 })?;
348 } else {
349 let mut parent_ids = memory.alloc::<u32>(num_children)?;
350 let fill_fn = device
351 .get_func(PIR_MODULE, pir_kernels::PIR_FILL_CHILD_PARENTS)
352 .ok_or_else(|| XlogError::Kernel("pir_fill_child_parents not found".to_string()))?;
353 let grid_nodes = num_nodes_u32.div_ceil(block_size);
354 unsafe {
356 fill_fn.clone().launch(
357 LaunchConfig {
358 grid_dim: (grid_nodes, 1, 1),
359 block_dim: (block_size, 1, 1),
360 shared_mem_bytes: 0,
361 },
362 (&batch.child_offsets, num_nodes_u32, &mut parent_ids),
363 )
364 }
365 .map_err(|e| XlogError::Kernel(format!("pir_fill_child_parents failed: {}", e)))?;
366
367 let mut sort_scratch = RadixSortScratch::new(&self.provider, num_children_u32)?;
368 self.provider.radix_sort_u32_pairs(
369 &mut batch.children,
370 &mut parent_ids,
371 num_children_u32,
372 &mut sort_scratch,
373 )?;
374 self.provider.radix_sort_u32_pairs(
375 &mut parent_ids,
376 &mut batch.children,
377 num_children_u32,
378 &mut sort_scratch,
379 )?;
380
381 let mut pair_unique_mask = memory.alloc::<u8>(num_children)?;
382 let mark_pairs = device
383 .get_func(PIR_MODULE, pir_kernels::PIR_MARK_UNIQUE_PAIRS)
384 .ok_or_else(|| XlogError::Kernel("pir_mark_unique_pairs not found".to_string()))?;
385 let grid_pairs = num_children_u32.div_ceil(block_size);
386 unsafe {
388 mark_pairs.clone().launch(
389 LaunchConfig {
390 grid_dim: (grid_pairs, 1, 1),
391 block_dim: (block_size, 1, 1),
392 shared_mem_bytes: 0,
393 },
394 (
395 &parent_ids,
396 &batch.children,
397 num_children_u32,
398 &mut pair_unique_mask,
399 ),
400 )
401 }
402 .map_err(|e| XlogError::Kernel(format!("pir_mark_unique_pairs failed: {}", e)))?;
403
404 let pair_prefix = self
405 .provider
406 .scan_u8_mask_device(&pair_unique_mask, num_children_u32)?;
407
408 let mut unique_pairs_total = memory.alloc::<u32>(1)?;
409 device
410 .memset_zeros(&mut unique_pairs_total)
411 .map_err(|e| XlogError::Kernel(format!("zero unique_pairs_total: {}", e)))?;
412 let count_mask = device
413 .get_func(SCAN_MODULE, scan_kernels::COUNT_MASK)
414 .ok_or_else(|| XlogError::Kernel("count_mask not found".to_string()))?;
415 unsafe {
417 count_mask.clone().launch(
418 LaunchConfig {
419 grid_dim: (grid_pairs, 1, 1),
420 block_dim: (block_size, 1, 1),
421 shared_mem_bytes: 0,
422 },
423 (&pair_unique_mask, num_children_u32, &mut unique_pairs_total),
424 )
425 }
426 .map_err(|e| XlogError::Kernel(format!("count_mask (pairs) failed: {}", e)))?;
427
428 let mut canon_parent = memory.alloc::<u32>(num_children)?;
429 let compact_pairs = device
430 .get_func(PIR_MODULE, pir_kernels::PIR_COMPACT_PAIRS)
431 .ok_or_else(|| XlogError::Kernel("pir_compact_pairs not found".to_string()))?;
432 unsafe {
434 compact_pairs.clone().launch(
435 LaunchConfig {
436 grid_dim: (grid_pairs, 1, 1),
437 block_dim: (block_size, 1, 1),
438 shared_mem_bytes: 0,
439 },
440 (
441 &parent_ids,
442 &batch.children,
443 &pair_unique_mask,
444 &pair_prefix,
445 num_children_u32,
446 &mut canon_parent,
447 &mut canon_children,
448 ),
449 )
450 }
451 .map_err(|e| XlogError::Kernel(format!("pir_compact_pairs failed: {}", e)))?;
452
453 let mut child_counts = memory.alloc::<u32>(num_nodes)?;
454 device
455 .memset_zeros(&mut child_counts)
456 .map_err(|e| XlogError::Kernel(format!("zero child_counts: {}", e)))?;
457 let count_children = device
458 .get_func(PIR_MODULE, pir_kernels::PIR_COUNT_CHILDREN)
459 .ok_or_else(|| XlogError::Kernel("pir_count_children not found".to_string()))?;
460 unsafe {
462 count_children.clone().launch(
463 LaunchConfig {
464 grid_dim: (grid_pairs, 1, 1),
465 block_dim: (block_size, 1, 1),
466 shared_mem_bytes: 0,
467 },
468 (
469 &canon_parent,
470 &unique_pairs_total,
471 num_nodes_u32,
472 &mut child_counts,
473 ),
474 )
475 }
476 .map_err(|e| XlogError::Kernel(format!("pir_count_children failed: {}", e)))?;
477
478 self.provider
479 .exclusive_scan_u32_inplace(&mut child_counts, num_nodes_u32)?;
480
481 let write_offsets = device
482 .get_func(PIR_MODULE, pir_kernels::PIR_WRITE_CHILD_OFFSETS)
483 .ok_or_else(|| {
484 XlogError::Kernel("pir_write_child_offsets not found".to_string())
485 })?;
486 let grid_nodes = num_nodes_u32.div_ceil(block_size);
487 unsafe {
489 write_offsets.clone().launch(
490 LaunchConfig {
491 grid_dim: (grid_nodes.max(1), 1, 1),
492 block_dim: (block_size, 1, 1),
493 shared_mem_bytes: 0,
494 },
495 (
496 &child_counts,
497 num_nodes_u32,
498 &unique_pairs_total,
499 &mut canon_child_offsets,
500 ),
501 )
502 }
503 .map_err(|e| XlogError::Kernel(format!("pir_write_child_offsets failed: {}", e)))?;
504 }
505
506 let mut hashes = memory.alloc::<u64>(num_nodes)?;
508 let hash_fn = device
509 .get_func(PIR_MODULE, pir_kernels::PIR_HASH_KEYS)
510 .ok_or_else(|| XlogError::Kernel("pir_hash_keys not found".to_string()))?;
511 let grid_nodes = num_nodes_u32.div_ceil(block_size);
512 unsafe {
514 hash_fn.clone().launch(
515 LaunchConfig {
516 grid_dim: (grid_nodes, 1, 1),
517 block_dim: (block_size, 1, 1),
518 shared_mem_bytes: 0,
519 },
520 (
521 &batch.node_type,
522 &batch.leaf_id,
523 &batch.decision_var,
524 &batch.decision_child_false,
525 &batch.decision_child_true,
526 &canon_child_offsets,
527 &canon_children,
528 num_nodes_u32,
529 &mut hashes,
530 ),
531 )
532 }
533 .map_err(|e| XlogError::Kernel(format!("pir_hash_keys failed: {}", e)))?;
534
535 let mut key_tag = memory.alloc::<u32>(num_nodes)?;
536 let mut key_payload = memory.alloc::<u32>(num_nodes)?;
537 let mut key_child_len = memory.alloc::<u32>(num_nodes)?;
538 let pack_fn = device
539 .get_func(PIR_MODULE, pir_kernels::PIR_PACK_KEYS)
540 .ok_or_else(|| XlogError::Kernel("pir_pack_keys not found".to_string()))?;
541 unsafe {
543 pack_fn.clone().launch(
544 LaunchConfig {
545 grid_dim: (grid_nodes, 1, 1),
546 block_dim: (block_size, 1, 1),
547 shared_mem_bytes: 0,
548 },
549 (
550 &batch.node_type,
551 &batch.leaf_id,
552 &batch.decision_var,
553 &batch.decision_child_false,
554 &batch.decision_child_true,
555 &canon_child_offsets,
556 num_nodes_u32,
557 &mut key_tag,
558 &mut key_payload,
559 &mut key_child_len,
560 ),
561 )
562 }
563 .map_err(|e| XlogError::Kernel(format!("pir_pack_keys failed: {}", e)))?;
564
565 let mut indices = memory.alloc::<u32>(num_nodes)?;
567 self.provider.init_indices(&mut indices, num_nodes_u32)?;
568 let mut keys = memory.alloc::<u32>(num_nodes)?;
569 let mut node_sort = RadixSortScratch::new(&self.provider, num_nodes_u32)?;
570
571 self.provider
572 .gather_u32_by_indices(&key_child_len, &indices, &mut keys, num_nodes_u32)?;
573 self.provider.radix_sort_u32_pairs(
574 &mut keys,
575 &mut indices,
576 num_nodes_u32,
577 &mut node_sort,
578 )?;
579
580 self.provider
581 .gather_u32_by_indices(&key_payload, &indices, &mut keys, num_nodes_u32)?;
582 self.provider.radix_sort_u32_pairs(
583 &mut keys,
584 &mut indices,
585 num_nodes_u32,
586 &mut node_sort,
587 )?;
588
589 self.provider
590 .gather_u32_by_indices(&key_tag, &indices, &mut keys, num_nodes_u32)?;
591 self.provider.radix_sort_u32_pairs(
592 &mut keys,
593 &mut indices,
594 num_nodes_u32,
595 &mut node_sort,
596 )?;
597
598 self.provider
599 .gather_u64_lo_by_indices(&hashes, &indices, &mut keys, num_nodes_u32)?;
600 self.provider.radix_sort_u32_pairs(
601 &mut keys,
602 &mut indices,
603 num_nodes_u32,
604 &mut node_sort,
605 )?;
606
607 self.provider
608 .gather_u64_hi_by_indices(&hashes, &indices, &mut keys, num_nodes_u32)?;
609 self.provider.radix_sort_u32_pairs(
610 &mut keys,
611 &mut indices,
612 num_nodes_u32,
613 &mut node_sort,
614 )?;
615
616 let mut sorted_node_type = memory.alloc::<u8>(num_nodes)?;
618 let mut sorted_leaf_id = memory.alloc::<u32>(num_nodes)?;
619 let mut sorted_decision_var = memory.alloc::<u32>(num_nodes)?;
620 let mut sorted_decision_child_false = memory.alloc::<u32>(num_nodes)?;
621 let mut sorted_decision_child_true = memory.alloc::<u32>(num_nodes)?;
622
623 self.provider.gather_u8_by_indices(
624 &batch.node_type,
625 &indices,
626 &mut sorted_node_type,
627 num_nodes_u32,
628 )?;
629 self.provider.gather_u32_by_indices(
630 &batch.leaf_id,
631 &indices,
632 &mut sorted_leaf_id,
633 num_nodes_u32,
634 )?;
635 self.provider.gather_u32_by_indices(
636 &batch.decision_var,
637 &indices,
638 &mut sorted_decision_var,
639 num_nodes_u32,
640 )?;
641 self.provider.gather_u32_by_indices(
642 &batch.decision_child_false,
643 &indices,
644 &mut sorted_decision_child_false,
645 num_nodes_u32,
646 )?;
647 self.provider.gather_u32_by_indices(
648 &batch.decision_child_true,
649 &indices,
650 &mut sorted_decision_child_true,
651 num_nodes_u32,
652 )?;
653
654 let mut sorted_child_len = memory.alloc::<u32>(num_nodes)?;
656 self.provider.gather_u32_by_indices(
657 &key_child_len,
658 &indices,
659 &mut sorted_child_len,
660 num_nodes_u32,
661 )?;
662 self.provider
663 .exclusive_scan_u32_inplace(&mut sorted_child_len, num_nodes_u32)?;
664
665 let mut sorted_child_offsets = memory.alloc::<u32>(num_nodes + 1)?;
666 let write_offsets = device
667 .get_func(PIR_MODULE, pir_kernels::PIR_WRITE_CHILD_OFFSETS)
668 .ok_or_else(|| XlogError::Kernel("pir_write_child_offsets not found".to_string()))?;
669 let total_children_view = canon_child_offsets.slice(num_nodes..(num_nodes + 1));
670 unsafe {
672 write_offsets.clone().launch(
673 LaunchConfig {
674 grid_dim: (grid_nodes.max(1), 1, 1),
675 block_dim: (block_size, 1, 1),
676 shared_mem_bytes: 0,
677 },
678 (
679 &sorted_child_len,
680 num_nodes_u32,
681 &total_children_view,
682 &mut sorted_child_offsets,
683 ),
684 )
685 }
686 .map_err(|e| XlogError::Kernel(format!("pir_write_child_offsets(sorted) failed: {}", e)))?;
687
688 let mut sorted_children = memory.alloc::<u32>(num_children)?;
689 let gather_children = device
690 .get_func(PIR_MODULE, pir_kernels::PIR_GATHER_CHILDREN)
691 .ok_or_else(|| XlogError::Kernel("pir_gather_children not found".to_string()))?;
692 unsafe {
694 gather_children.clone().launch(
695 LaunchConfig {
696 grid_dim: (grid_nodes, 1, 1),
697 block_dim: (block_size, 1, 1),
698 shared_mem_bytes: 0,
699 },
700 (
701 &indices,
702 &canon_child_offsets,
703 &canon_children,
704 &sorted_child_offsets,
705 num_nodes_u32,
706 &mut sorted_children,
707 ),
708 )
709 }
710 .map_err(|e| XlogError::Kernel(format!("pir_gather_children failed: {}", e)))?;
711
712 let mut sorted_hashes = memory.alloc::<u64>(num_nodes)?;
714 let hash_fn = device
715 .get_func(PIR_MODULE, pir_kernels::PIR_HASH_KEYS)
716 .ok_or_else(|| XlogError::Kernel("pir_hash_keys not found".to_string()))?;
717 unsafe {
719 hash_fn.clone().launch(
720 LaunchConfig {
721 grid_dim: (grid_nodes, 1, 1),
722 block_dim: (block_size, 1, 1),
723 shared_mem_bytes: 0,
724 },
725 (
726 &sorted_node_type,
727 &sorted_leaf_id,
728 &sorted_decision_var,
729 &sorted_decision_child_false,
730 &sorted_decision_child_true,
731 &sorted_child_offsets,
732 &sorted_children,
733 num_nodes_u32,
734 &mut sorted_hashes,
735 ),
736 )
737 }
738 .map_err(|e| XlogError::Kernel(format!("pir_hash_keys(sorted) failed: {}", e)))?;
739
740 let hash_table = self
741 .provider
742 .build_hash_table_u64(&self.graph_hashes, self.node_cap)?;
743 let existing_id = memory.alloc::<u32>(num_nodes)?;
744 let find_existing = device
745 .get_func(PIR_MODULE, pir_kernels::PIR_FIND_EXISTING)
746 .ok_or_else(|| XlogError::Kernel("pir_find_existing not found".to_string()))?;
747 let mut find_params: Vec<*mut c_void> = vec![
748 (&sorted_hashes).as_kernel_param(),
749 (&sorted_node_type).as_kernel_param(),
750 (&sorted_leaf_id).as_kernel_param(),
751 (&sorted_decision_var).as_kernel_param(),
752 (&sorted_decision_child_false).as_kernel_param(),
753 (&sorted_decision_child_true).as_kernel_param(),
754 (&sorted_child_offsets).as_kernel_param(),
755 (&sorted_children).as_kernel_param(),
756 num_nodes_u32.as_kernel_param(),
757 (&self.graph.node_type).as_kernel_param(),
758 (&self.graph.child_offsets).as_kernel_param(),
759 (&self.graph.children).as_kernel_param(),
760 (&self.graph.leaf_id).as_kernel_param(),
761 (&self.graph.decision_var).as_kernel_param(),
762 (&self.graph.decision_child_false).as_kernel_param(),
763 (&self.graph.decision_child_true).as_kernel_param(),
764 (&self.num_nodes).as_kernel_param(),
765 (&hash_table.bucket_offsets).as_kernel_param(),
766 (&hash_table.bucket_counts).as_kernel_param(),
767 (&hash_table.bucket_entries).as_kernel_param(),
768 (&hash_table.bucket_entry_hashes).as_kernel_param(),
769 hash_table.bucket_mask.as_kernel_param(),
770 (&existing_id).as_kernel_param(),
771 ];
772 unsafe {
774 find_existing.clone().launch(
775 LaunchConfig {
776 grid_dim: (grid_nodes, 1, 1),
777 block_dim: (block_size, 1, 1),
778 shared_mem_bytes: 0,
779 },
780 &mut find_params,
781 )
782 }
783 .map_err(|e| XlogError::Kernel(format!("pir_find_existing failed: {}", e)))?;
784
785 let mut unique_mask = memory.alloc::<u8>(num_nodes)?;
787 let mark_unique = device
788 .get_func(PIR_MODULE, pir_kernels::PIR_MARK_UNIQUE)
789 .ok_or_else(|| XlogError::Kernel("pir_mark_unique not found".to_string()))?;
790 unsafe {
792 mark_unique.clone().launch(
793 LaunchConfig {
794 grid_dim: (grid_nodes, 1, 1),
795 block_dim: (block_size, 1, 1),
796 shared_mem_bytes: 0,
797 },
798 (
799 &sorted_hashes,
800 &sorted_node_type,
801 &sorted_leaf_id,
802 &sorted_decision_var,
803 &sorted_decision_child_false,
804 &sorted_decision_child_true,
805 &sorted_child_offsets,
806 &sorted_children,
807 num_nodes_u32,
808 &mut unique_mask,
809 ),
810 )
811 }
812 .map_err(|e| XlogError::Kernel(format!("pir_mark_unique failed: {}", e)))?;
813
814 let mut new_mask = memory.alloc::<u8>(num_nodes)?;
815 let mark_new = device
816 .get_func(PIR_MODULE, pir_kernels::PIR_MARK_NEW_GROUPS)
817 .ok_or_else(|| XlogError::Kernel("pir_mark_new_groups not found".to_string()))?;
818 unsafe {
820 mark_new.clone().launch(
821 LaunchConfig {
822 grid_dim: (grid_nodes, 1, 1),
823 block_dim: (block_size, 1, 1),
824 shared_mem_bytes: 0,
825 },
826 (&unique_mask, &existing_id, num_nodes_u32, &mut new_mask),
827 )
828 }
829 .map_err(|e| XlogError::Kernel(format!("pir_mark_new_groups failed: {}", e)))?;
830
831 let unique_prefix = self
832 .provider
833 .scan_u8_mask_device(&unique_mask, num_nodes_u32)?;
834
835 let new_prefix = self
836 .provider
837 .scan_u8_mask_device(&new_mask, num_nodes_u32)?;
838
839 let mut new_nodes_total = memory.alloc::<u32>(1)?;
840 device
841 .memset_zeros(&mut new_nodes_total)
842 .map_err(|e| XlogError::Kernel(format!("zero new_nodes_total: {}", e)))?;
843 let count_mask = device
844 .get_func(SCAN_MODULE, scan_kernels::COUNT_MASK)
845 .ok_or_else(|| XlogError::Kernel("count_mask not found".to_string()))?;
846 unsafe {
848 count_mask.clone().launch(
849 LaunchConfig {
850 grid_dim: (grid_nodes, 1, 1),
851 block_dim: (block_size, 1, 1),
852 shared_mem_bytes: 0,
853 },
854 (&new_mask, num_nodes_u32, &mut new_nodes_total),
855 )
856 }
857 .map_err(|e| XlogError::Kernel(format!("count_mask (new nodes) failed: {}", e)))?;
858
859 let mut group_node_id = memory.alloc::<u32>(num_nodes)?;
860 let build_group = device
861 .get_func(PIR_MODULE, pir_kernels::PIR_BUILD_GROUP_IDS)
862 .ok_or_else(|| XlogError::Kernel("pir_build_group_ids not found".to_string()))?;
863 unsafe {
865 build_group.clone().launch(
866 LaunchConfig {
867 grid_dim: (grid_nodes, 1, 1),
868 block_dim: (block_size, 1, 1),
869 shared_mem_bytes: 0,
870 },
871 (
872 &unique_mask,
873 &unique_prefix,
874 &existing_id,
875 &new_prefix,
876 &self.num_nodes,
877 num_nodes_u32,
878 &mut group_node_id,
879 ),
880 )
881 }
882 .map_err(|e| XlogError::Kernel(format!("pir_build_group_ids failed: {}", e)))?;
883
884 let mut graph_child_counts = memory.alloc::<u32>(num_nodes)?;
885 let build_counts = device
886 .get_func(PIR_MODULE, pir_kernels::PIR_BUILD_GRAPH_CHILD_COUNTS)
887 .ok_or_else(|| {
888 XlogError::Kernel("pir_build_graph_child_counts not found".to_string())
889 })?;
890 unsafe {
892 build_counts.clone().launch(
893 LaunchConfig {
894 grid_dim: (grid_nodes, 1, 1),
895 block_dim: (block_size, 1, 1),
896 shared_mem_bytes: 0,
897 },
898 (
899 &sorted_node_type,
900 &sorted_child_offsets,
901 &new_mask,
902 num_nodes_u32,
903 &mut graph_child_counts,
904 ),
905 )
906 }
907 .map_err(|e| XlogError::Kernel(format!("pir_build_graph_child_counts failed: {}", e)))?;
908
909 let mut graph_children_total = memory.alloc::<u32>(1)?;
910 device
911 .memset_zeros(&mut graph_children_total)
912 .map_err(|e| XlogError::Kernel(format!("zero graph_children_total: {}", e)))?;
913 let sum_counts = device
914 .get_func(PIR_MODULE, pir_kernels::PIR_SUM_COUNTS)
915 .ok_or_else(|| XlogError::Kernel("pir_sum_counts not found".to_string()))?;
916 unsafe {
918 sum_counts.clone().launch(
919 LaunchConfig {
920 grid_dim: (grid_nodes, 1, 1),
921 block_dim: (block_size, 1, 1),
922 shared_mem_bytes: 0,
923 },
924 (
925 &graph_child_counts,
926 num_nodes_u32,
927 &mut graph_children_total,
928 ),
929 )
930 }
931 .map_err(|e| XlogError::Kernel(format!("pir_sum_counts failed: {}", e)))?;
932
933 self.provider
934 .exclusive_scan_u32_inplace(&mut graph_child_counts, num_nodes_u32)?;
935
936 let out_ids = memory.alloc::<u32>(num_nodes)?;
937 let emit = device
938 .get_func(PIR_MODULE, pir_kernels::PIR_EMIT_NODES_AND_IDS)
939 .ok_or_else(|| XlogError::Kernel("pir_emit_nodes_and_ids not found".to_string()))?;
940 let mut emit_params: Vec<*mut c_void> = vec![
941 (&sorted_node_type).as_kernel_param(),
942 (&sorted_leaf_id).as_kernel_param(),
943 (&sorted_decision_var).as_kernel_param(),
944 (&sorted_decision_child_false).as_kernel_param(),
945 (&sorted_decision_child_true).as_kernel_param(),
946 (&sorted_child_offsets).as_kernel_param(),
947 (&sorted_children).as_kernel_param(),
948 (&unique_mask).as_kernel_param(),
949 (&unique_prefix).as_kernel_param(),
950 (&group_node_id).as_kernel_param(),
951 (&graph_child_counts).as_kernel_param(),
952 (&indices).as_kernel_param(),
953 num_nodes_u32.as_kernel_param(),
954 (&self.num_nodes).as_kernel_param(),
955 (&self.num_children).as_kernel_param(),
956 self.node_cap.as_kernel_param(),
957 self.child_cap.as_kernel_param(),
958 (&self.graph.node_type).as_kernel_param(),
959 (&self.graph.child_offsets).as_kernel_param(),
960 (&self.graph.children).as_kernel_param(),
961 (&self.graph.leaf_id).as_kernel_param(),
962 (&self.graph.decision_var).as_kernel_param(),
963 (&self.graph.decision_child_false).as_kernel_param(),
964 (&self.graph.decision_child_true).as_kernel_param(),
965 (&new_mask).as_kernel_param(),
966 (&sorted_hashes).as_kernel_param(),
967 (&self.graph_hashes).as_kernel_param(),
968 (&out_ids).as_kernel_param(),
969 ];
970 unsafe {
972 emit.clone().launch(
973 LaunchConfig {
974 grid_dim: (grid_nodes, 1, 1),
975 block_dim: (block_size, 1, 1),
976 shared_mem_bytes: 0,
977 },
978 &mut emit_params,
979 )
980 }
981 .map_err(|e| XlogError::Kernel(format!("pir_emit_nodes_and_ids failed: {}", e)))?;
982
983 let update_counts = device
984 .get_func(PIR_MODULE, pir_kernels::PIR_UPDATE_COUNTS)
985 .ok_or_else(|| XlogError::Kernel("pir_update_counts not found".to_string()))?;
986 unsafe {
988 update_counts.clone().launch(
989 LaunchConfig {
990 grid_dim: (1, 1, 1),
991 block_dim: (1, 1, 1),
992 shared_mem_bytes: 0,
993 },
994 (
995 &new_nodes_total,
996 &graph_children_total,
997 self.node_cap,
998 self.child_cap,
999 &mut self.num_nodes,
1000 &mut self.num_children,
1001 ),
1002 )
1003 }
1004 .map_err(|e| XlogError::Kernel(format!("pir_update_counts failed: {}", e)))?;
1005 Ok(out_ids)
1007 }
1008}