1use std::sync::Arc;
4
5use cudarc::driver::LaunchConfig;
6use xlog_core::{Result, XlogError};
7use xlog_cuda::memory::TrackedCudaSlice;
8use xlog_cuda::provider::{cache_kernels, CACHE_MODULE};
9use xlog_cuda::{AsKernelParam, CudaKernelProvider, LaunchAsync};
10use xlog_solve::GpuCnf;
11
12use super::disk_cache;
13use crate::gpu::GpuXgcf;
14
15#[derive(Debug, Clone, Copy)]
22#[non_exhaustive]
23pub struct GpuCircuitCacheConfig {
24 pub num_slots: u32,
26 pub table_size: u32,
28 pub node_cap: u32,
30 pub edge_cap: u32,
32 pub level_cap: u32,
34 pub var_cap: u32,
36}
37
38impl Default for GpuCircuitCacheConfig {
39 fn default() -> Self {
43 Self {
44 num_slots: 4,
45 table_size: 8,
46 node_cap: 65_536,
47 edge_cap: 131_072,
48 level_cap: 65_536,
49 var_cap: 128,
50 }
51 }
52}
53
54fn cache_grid_dim_for_u32_count(context: &str, count: u32, block_dim: u32) -> Result<u32> {
55 if count == 0 {
56 return Ok(0);
57 }
58 if block_dim == 0 {
59 return Err(XlogError::Compilation(format!(
60 "{context}: GPU cache block size must be nonzero"
61 )));
62 }
63 let padded = count
64 .checked_add(block_dim - 1)
65 .ok_or_else(|| XlogError::Compilation(format!("{context}: GPU cache grid overflow")))?;
66 Ok(padded / block_dim)
67}
68
69fn cache_grid_dim_for_u64_count(context: &str, count: u64, block_dim: u32) -> Result<u32> {
70 if count == 0 {
71 return Ok(0);
72 }
73 if block_dim == 0 {
74 return Err(XlogError::Compilation(format!(
75 "{context}: GPU cache block size must be nonzero"
76 )));
77 }
78 let block = block_dim as u64;
79 let grid = count
80 .checked_add(block - 1)
81 .map(|padded| padded / block)
82 .ok_or_else(|| XlogError::Compilation(format!("{context}: GPU cache grid overflow")))?;
83 u32::try_from(grid)
84 .map_err(|_| XlogError::Compilation(format!("{context}: GPU cache grid exceeds u32")))
85}
86
87pub struct GpuCircuitCache {
89 provider: Arc<CudaKernelProvider>,
90 table_size: u32,
91 num_slots: u32,
92 node_cap: u32,
93 edge_cap: u32,
94 level_cap: u32,
95 var_cap: u32,
96 keys: TrackedCudaSlice<u64>,
97 slots: TrackedCudaSlice<u32>,
98 state: TrackedCudaSlice<u32>,
99 last_used: TrackedCudaSlice<u64>,
100 slot_states: TrackedCudaSlice<u32>,
101 clock: TrackedCudaSlice<u64>,
102 node_type: TrackedCudaSlice<u8>,
103 child_offsets: TrackedCudaSlice<u32>,
104 child_indices: TrackedCudaSlice<u32>,
105 lit: TrackedCudaSlice<i32>,
106 decision_var: TrackedCudaSlice<u32>,
107 decision_child_false: TrackedCudaSlice<u32>,
108 decision_child_true: TrackedCudaSlice<u32>,
109 level_nodes: TrackedCudaSlice<u32>,
110 level_offsets: TrackedCudaSlice<u32>,
111 var_log_true: TrackedCudaSlice<f64>,
112 var_log_false: TrackedCudaSlice<f64>,
113 values: TrackedCudaSlice<f64>,
114 adj: TrackedCudaSlice<f64>,
115 grad_true: TrackedCudaSlice<f64>,
116 grad_false: TrackedCudaSlice<f64>,
117 meta_num_nodes: TrackedCudaSlice<u32>,
118 meta_num_levels: TrackedCudaSlice<u32>,
119 meta_root: TrackedCudaSlice<u32>,
120 meta_max_var: TrackedCudaSlice<u32>,
121 always_on: TrackedCudaSlice<u32>,
122 zero_f64: TrackedCudaSlice<f64>,
123 one_f64: TrackedCudaSlice<f64>,
124 free_var_mask: TrackedCudaSlice<u8>,
125 has_free_var_mask: Vec<bool>,
126}
127
128pub struct GpuCacheLookup {
130 provider: Arc<CudaKernelProvider>,
131 slot: TrackedCudaSlice<u32>,
132 compile_needed: TrackedCudaSlice<u32>,
133}
134
135impl GpuCacheLookup {
136 pub fn slot_device(&self) -> &TrackedCudaSlice<u32> {
138 &self.slot
139 }
140
141 pub fn compile_needed_device(&self) -> &TrackedCudaSlice<u32> {
143 &self.compile_needed
144 }
145
146 pub fn provider(&self) -> &Arc<CudaKernelProvider> {
148 &self.provider
149 }
150
151 pub fn into_handle(self) -> Result<GpuCircuitCacheHandle> {
153 let slot_host_vec: Vec<u32> = self
154 .provider
155 .device()
156 .inner()
157 .dtoh_sync_copy(&self.slot)
158 .map_err(|e| XlogError::Kernel(format!("dtoh slot index: {}", e)))?;
159 Ok(GpuCircuitCacheHandle {
160 provider: self.provider,
161 slot: self.slot,
162 compile_needed: self.compile_needed,
163 slot_host: slot_host_vec[0],
164 num_nodes: 0,
165 num_levels: 0,
166 root: 0,
167 max_var: 0,
168 })
169 }
170}
171
172pub struct GpuCircuitCacheHandle {
174 provider: Arc<CudaKernelProvider>,
175 slot: TrackedCudaSlice<u32>,
176 compile_needed: TrackedCudaSlice<u32>,
177 slot_host: u32,
178 num_nodes: u32,
179 num_levels: u32,
180 root: u32,
181 max_var: u32,
182}
183
184impl GpuCircuitCacheHandle {
185 pub fn slot_device(&self) -> &TrackedCudaSlice<u32> {
187 &self.slot
188 }
189
190 pub fn compile_needed_device(&self) -> &TrackedCudaSlice<u32> {
192 &self.compile_needed
193 }
194
195 pub fn provider(&self) -> &Arc<CudaKernelProvider> {
197 &self.provider
198 }
199
200 pub fn num_nodes(&self) -> u32 {
202 self.num_nodes
203 }
204
205 pub fn num_levels(&self) -> u32 {
207 self.num_levels
208 }
209
210 pub fn root(&self) -> u32 {
212 self.root
213 }
214
215 pub fn max_var(&self) -> u32 {
217 self.max_var
218 }
219
220 pub(crate) fn slot_index(&self) -> u32 {
221 self.slot_host
222 }
223}
224
225pub fn hash_cnf_gpu(
230 cnf: &GpuCnf,
231 provider: &Arc<CudaKernelProvider>,
232) -> Result<TrackedCudaSlice<u64>> {
233 let memory = provider.memory();
234 let mut out_hash = memory.alloc::<u64>(1)?;
235
236 let func = provider
237 .device()
238 .inner()
239 .get_func(CACHE_MODULE, cache_kernels::CACHE_CNF_HASH)
240 .ok_or_else(|| XlogError::Kernel("cache_cnf_hash kernel not found".to_string()))?;
241
242 unsafe {
244 func.clone().launch(
245 LaunchConfig {
246 grid_dim: (1, 1, 1),
247 block_dim: (1, 1, 1),
248 shared_mem_bytes: 0,
249 },
250 (
251 &cnf.num_vars,
252 &cnf.num_clauses,
253 &cnf.num_lits,
254 &cnf.clause_offsets,
255 &cnf.literals,
256 &mut out_hash,
257 ),
258 )
259 }
260 .map_err(|e| XlogError::Kernel(format!("cache_cnf_hash launch failed: {}", e)))?;
261 Ok(out_hash)
263}
264
265impl GpuCircuitCache {
266 pub fn provider(&self) -> &Arc<CudaKernelProvider> {
268 &self.provider
269 }
270
271 pub fn var_log_weights_mut(
277 &mut self,
278 ) -> (&mut TrackedCudaSlice<f64>, &mut TrackedCudaSlice<f64>) {
279 (&mut self.var_log_true, &mut self.var_log_false)
280 }
281
282 pub fn grad_true(&self) -> &TrackedCudaSlice<f64> {
284 &self.grad_true
285 }
286
287 pub fn grad_false(&self) -> &TrackedCudaSlice<f64> {
289 &self.grad_false
290 }
291
292 pub fn values(&self) -> &TrackedCudaSlice<f64> {
294 &self.values
295 }
296
297 pub fn meta_num_nodes_device(&self) -> &TrackedCudaSlice<u32> {
299 &self.meta_num_nodes
300 }
301
302 pub fn meta_num_levels_device(&self) -> &TrackedCudaSlice<u32> {
304 &self.meta_num_levels
305 }
306
307 pub fn meta_root_device(&self) -> &TrackedCudaSlice<u32> {
309 &self.meta_root
310 }
311
312 pub fn meta_max_var_device(&self) -> &TrackedCudaSlice<u32> {
314 &self.meta_max_var
315 }
316
317 pub fn num_slots(&self) -> u32 {
319 self.num_slots
320 }
321
322 pub(crate) fn has_any_free_var_mask(&self) -> bool {
323 self.has_free_var_mask.iter().any(|&v| v)
324 }
325
326 pub(crate) fn has_free_var_mask_for_slot(&self, slot: u32) -> bool {
327 self.has_free_var_mask
328 .get(slot as usize)
329 .copied()
330 .unwrap_or(false)
331 }
332
333 pub(crate) fn var_stride(&self) -> Result<u32> {
334 self.var_cap
335 .checked_add(1)
336 .ok_or_else(|| XlogError::Compilation("GpuCircuitCache var_cap overflow".to_string()))
337 }
338
339 pub(crate) fn node_stride(&self) -> u32 {
340 self.node_cap
341 }
342
343 pub(crate) fn copy_slot_weights_to_batch(
344 &mut self,
345 handle: &GpuCircuitCacheHandle,
346 out_true_batch: &mut TrackedCudaSlice<f64>,
347 out_false_batch: &mut TrackedCudaSlice<f64>,
348 batch_size: u32,
349 ) -> Result<()> {
350 if batch_size == 0 {
351 return Ok(());
352 }
353 let var_stride = self.var_stride()?;
354 let expected = (batch_size as usize)
355 .checked_mul(var_stride as usize)
356 .ok_or_else(|| {
357 XlogError::Compilation("GpuCircuitCache batch weight size overflow".to_string())
358 })?;
359 if out_true_batch.len() != expected || out_false_batch.len() != expected {
360 return Err(XlogError::Compilation(format!(
361 "GpuCircuitCache batched weight buffers must both have len {}, got {} and {}",
362 expected,
363 out_true_batch.len(),
364 out_false_batch.len()
365 )));
366 }
367
368 let device = self.provider.device().inner();
369 let func = device
370 .get_func(
371 xlog_cuda::provider::WEIGHTS_MODULE,
372 xlog_cuda::provider::weights_kernels::WEIGHTS_COPY_SLOT_TO_BATCH,
373 )
374 .ok_or_else(|| {
375 XlogError::Kernel("weights_copy_slot_to_batch kernel not found".to_string())
376 })?;
377
378 let block_dim = 256u32;
379 let total = (batch_size as u64)
380 .checked_mul(var_stride as u64)
381 .ok_or_else(|| {
382 XlogError::Compilation("GpuCircuitCache batch copy overflow".to_string())
383 })?;
384 let grid_dim =
385 cache_grid_dim_for_u64_count("GpuCircuitCache batch weight copy", total, block_dim)?;
386 if grid_dim == 0 {
387 return Ok(());
388 }
389
390 unsafe {
392 func.clone().launch(
393 LaunchConfig {
394 grid_dim: (grid_dim, 1, 1),
395 block_dim: (block_dim, 1, 1),
396 shared_mem_bytes: 0,
397 },
398 (
399 handle.slot_device(),
400 self.var_cap,
401 &self.var_log_true,
402 &self.var_log_false,
403 out_true_batch,
404 out_false_batch,
405 var_stride,
406 batch_size,
407 ),
408 )
409 }
410 .map_err(|e| XlogError::Kernel(format!("weights_copy_slot_to_batch failed: {}", e)))?;
411
412 Ok(())
413 }
414
415 #[expect(
416 clippy::too_many_arguments,
417 reason = "fused batched evaluation keeps the circuit handle, paired weight tables, four output workspaces, and batch size explicit"
418 )]
419 pub(crate) fn eval_grads_inplace_fused_batched(
420 &mut self,
421 handle: &GpuCircuitCacheHandle,
422 var_log_true_batch: &TrackedCudaSlice<f64>,
423 var_log_false_batch: &TrackedCudaSlice<f64>,
424 values_batch: &mut TrackedCudaSlice<f64>,
425 adj_batch: &mut TrackedCudaSlice<f64>,
426 grad_true_batch: &mut TrackedCudaSlice<f64>,
427 grad_false_batch: &mut TrackedCudaSlice<f64>,
428 batch_size: u32,
429 ) -> Result<()> {
430 if batch_size == 0 {
431 return Ok(());
432 }
433 if self.has_free_var_mask_for_slot(handle.slot_index()) {
434 return Err(XlogError::Execution(
435 "Batched fused eval currently does not support free-var correction".to_string(),
436 ));
437 }
438
439 let var_stride = self.var_stride()?;
440 let node_stride = self.node_stride();
441 let expected_var = (batch_size as usize)
442 .checked_mul(var_stride as usize)
443 .ok_or_else(|| {
444 XlogError::Compilation("GpuCircuitCache batched var buffer overflow".to_string())
445 })?;
446 let expected_node = (batch_size as usize)
447 .checked_mul(node_stride as usize)
448 .ok_or_else(|| {
449 XlogError::Compilation("GpuCircuitCache batched node buffer overflow".to_string())
450 })?;
451
452 if var_log_true_batch.len() != expected_var
453 || var_log_false_batch.len() != expected_var
454 || grad_true_batch.len() != expected_var
455 || grad_false_batch.len() != expected_var
456 {
457 return Err(XlogError::Compilation(format!(
458 "GpuCircuitCache batched var buffers must have len {}",
459 expected_var
460 )));
461 }
462 if values_batch.len() != expected_node || adj_batch.len() != expected_node {
463 return Err(XlogError::Compilation(format!(
464 "GpuCircuitCache batched node buffers must have len {}",
465 expected_node
466 )));
467 }
468
469 let device = self.provider.device().inner();
470 device
471 .memset_zeros(adj_batch)
472 .map_err(|e| XlogError::Kernel(format!("Failed to zero batched adj: {}", e)))?;
473 device
474 .memset_zeros(grad_true_batch)
475 .map_err(|e| XlogError::Kernel(format!("Failed to zero batched grad_true: {}", e)))?;
476 device
477 .memset_zeros(grad_false_batch)
478 .map_err(|e| XlogError::Kernel(format!("Failed to zero batched grad_false: {}", e)))?;
479
480 let eval_all = device
481 .get_func(
482 xlog_cuda::CIRCUIT_MODULE,
483 xlog_cuda::circuit_kernels::XGCF_EVAL_ALL_LEVELS_CACHED_BATCHED,
484 )
485 .ok_or_else(|| {
486 XlogError::Kernel("xgcf_eval_all_levels_cached_batched not found".to_string())
487 })?;
488 let set_root_adj = device
489 .get_func(
490 xlog_cuda::CIRCUIT_MODULE,
491 xlog_cuda::circuit_kernels::XGCF_SET_ROOT_ADJ_CACHED_BATCHED,
492 )
493 .ok_or_else(|| {
494 XlogError::Kernel("xgcf_set_root_adj_cached_batched not found".to_string())
495 })?;
496 let backward_all = device
497 .get_func(
498 xlog_cuda::CIRCUIT_MODULE,
499 xlog_cuda::circuit_kernels::XGCF_BACKWARD_ALL_LEVELS_CACHED_BATCHED,
500 )
501 .ok_or_else(|| {
502 XlogError::Kernel("xgcf_backward_all_levels_cached_batched not found".to_string())
503 })?;
504
505 let block_size = 256u32;
506 let mut eval_params: Vec<*mut std::ffi::c_void> = vec![
507 handle.slot_device().as_kernel_param(),
508 self.node_cap.as_kernel_param(),
509 self.edge_cap.as_kernel_param(),
510 self.level_cap.as_kernel_param(),
511 self.var_cap.as_kernel_param(),
512 (&self.node_type).as_kernel_param(),
513 (&self.child_offsets).as_kernel_param(),
514 (&self.child_indices).as_kernel_param(),
515 (&self.lit).as_kernel_param(),
516 (&self.decision_var).as_kernel_param(),
517 (&self.decision_child_false).as_kernel_param(),
518 (&self.decision_child_true).as_kernel_param(),
519 (&self.level_nodes).as_kernel_param(),
520 (&self.level_offsets).as_kernel_param(),
521 (&self.meta_num_levels).as_kernel_param(),
522 var_log_true_batch.as_kernel_param(),
523 var_log_false_batch.as_kernel_param(),
524 var_stride.as_kernel_param(),
525 values_batch.as_kernel_param(),
526 node_stride.as_kernel_param(),
527 batch_size.as_kernel_param(),
528 ];
529 unsafe {
531 eval_all.clone().launch(
532 LaunchConfig {
533 grid_dim: (batch_size, 1, 1),
534 block_dim: (block_size, 1, 1),
535 shared_mem_bytes: 0,
536 },
537 &mut eval_params,
538 )
539 }
540 .map_err(|e| {
541 XlogError::Kernel(format!("xgcf_eval_all_levels_cached_batched failed: {}", e))
542 })?;
543
544 unsafe {
546 set_root_adj.clone().launch(
547 LaunchConfig {
548 grid_dim: (batch_size, 1, 1),
549 block_dim: (1, 1, 1),
550 shared_mem_bytes: 0,
551 },
552 (
553 handle.slot_device(),
554 self.node_cap,
555 &self.meta_root,
556 &mut *adj_batch,
557 node_stride,
558 batch_size,
559 ),
560 )
561 }
562 .map_err(|e| {
563 XlogError::Kernel(format!("xgcf_set_root_adj_cached_batched failed: {}", e))
564 })?;
565
566 let mut backward_params: Vec<*mut std::ffi::c_void> = vec![
567 handle.slot_device().as_kernel_param(),
568 self.node_cap.as_kernel_param(),
569 self.edge_cap.as_kernel_param(),
570 self.level_cap.as_kernel_param(),
571 self.var_cap.as_kernel_param(),
572 (&self.node_type).as_kernel_param(),
573 (&self.child_offsets).as_kernel_param(),
574 (&self.child_indices).as_kernel_param(),
575 (&self.decision_var).as_kernel_param(),
576 (&self.decision_child_false).as_kernel_param(),
577 (&self.decision_child_true).as_kernel_param(),
578 (&self.lit).as_kernel_param(),
579 (&self.level_nodes).as_kernel_param(),
580 (&self.level_offsets).as_kernel_param(),
581 (&self.meta_num_levels).as_kernel_param(),
582 var_log_true_batch.as_kernel_param(),
583 var_log_false_batch.as_kernel_param(),
584 var_stride.as_kernel_param(),
585 values_batch.as_kernel_param(),
586 node_stride.as_kernel_param(),
587 adj_batch.as_kernel_param(),
588 node_stride.as_kernel_param(),
589 grad_true_batch.as_kernel_param(),
590 grad_false_batch.as_kernel_param(),
591 var_stride.as_kernel_param(),
592 batch_size.as_kernel_param(),
593 ];
594 unsafe {
596 backward_all.clone().launch(
597 LaunchConfig {
598 grid_dim: (batch_size, 1, 1),
599 block_dim: (block_size, 1, 1),
600 shared_mem_bytes: 0,
601 },
602 &mut backward_params,
603 )
604 }
605 .map_err(|e| {
606 XlogError::Kernel(format!(
607 "xgcf_backward_all_levels_cached_batched failed: {}",
608 e
609 ))
610 })?;
611
612 Ok(())
613 }
614
615 pub(crate) fn copy_root_batched_from_values(
616 &self,
617 handle: &GpuCircuitCacheHandle,
618 values_batch: &TrackedCudaSlice<f64>,
619 out_roots: &mut TrackedCudaSlice<f64>,
620 batch_size: u32,
621 ) -> Result<()> {
622 if batch_size == 0 {
623 return Ok(());
624 }
625 let node_stride = self.node_stride();
626 let expected_values = (batch_size as usize)
627 .checked_mul(node_stride as usize)
628 .ok_or_else(|| {
629 XlogError::Compilation("GpuCircuitCache batched values overflow".to_string())
630 })?;
631 if values_batch.len() != expected_values || out_roots.len() != batch_size as usize {
632 return Err(XlogError::Compilation(format!(
633 "GpuCircuitCache root copy expects values len {} and roots len {}, got {} and {}",
634 expected_values,
635 batch_size,
636 values_batch.len(),
637 out_roots.len()
638 )));
639 }
640
641 let device = self.provider.device().inner();
642 let copy_root = device
643 .get_func(
644 xlog_cuda::CIRCUIT_MODULE,
645 xlog_cuda::circuit_kernels::XGCF_COPY_ROOT_CACHED_META_BATCHED,
646 )
647 .ok_or_else(|| {
648 XlogError::Kernel("xgcf_copy_root_cached_meta_batched not found".to_string())
649 })?;
650 unsafe {
652 copy_root.clone().launch(
653 LaunchConfig {
654 grid_dim: (batch_size, 1, 1),
655 block_dim: (1, 1, 1),
656 shared_mem_bytes: 0,
657 },
658 (
659 handle.slot_device(),
660 self.node_cap,
661 &self.meta_root,
662 values_batch,
663 node_stride,
664 out_roots,
665 batch_size,
666 ),
667 )
668 }
669 .map_err(|e| {
670 XlogError::Kernel(format!("xgcf_copy_root_cached_meta_batched failed: {}", e))
671 })?;
672 Ok(())
673 }
674
675 pub fn new(provider: &Arc<CudaKernelProvider>, config: GpuCircuitCacheConfig) -> Result<Self> {
677 if config.num_slots == 0 {
678 return Err(XlogError::Compilation(
679 "GpuCircuitCache requires num_slots > 0".to_string(),
680 ));
681 }
682 if config.table_size == 0 {
683 return Err(XlogError::Compilation(
684 "GpuCircuitCache requires table_size > 0".to_string(),
685 ));
686 }
687 if config.table_size < config.num_slots {
688 return Err(XlogError::Compilation(format!(
689 "GpuCircuitCache table_size {} < num_slots {}",
690 config.table_size, config.num_slots
691 )));
692 }
693 if config.node_cap == 0
694 || config.edge_cap == 0
695 || config.level_cap == 0
696 || config.var_cap == 0
697 {
698 return Err(XlogError::Compilation(
699 "GpuCircuitCache requires non-zero caps".to_string(),
700 ));
701 }
702
703 let memory = provider.memory();
704 let device = provider.device().inner();
705
706 let table_len = usize::try_from(config.table_size).map_err(|_| {
707 XlogError::Compilation("GpuCircuitCache table_size overflow".to_string())
708 })?;
709 let slot_len = usize::try_from(config.num_slots).map_err(|_| {
710 XlogError::Compilation("GpuCircuitCache num_slots overflow".to_string())
711 })?;
712
713 let node_cap = usize::try_from(config.node_cap)
714 .map_err(|_| XlogError::Compilation("GpuCircuitCache node_cap overflow".to_string()))?;
715 let edge_cap = usize::try_from(config.edge_cap)
716 .map_err(|_| XlogError::Compilation("GpuCircuitCache edge_cap overflow".to_string()))?;
717 let level_cap = usize::try_from(config.level_cap).map_err(|_| {
718 XlogError::Compilation("GpuCircuitCache level_cap overflow".to_string())
719 })?;
720 let var_cap = usize::try_from(config.var_cap)
721 .map_err(|_| XlogError::Compilation("GpuCircuitCache var_cap overflow".to_string()))?;
722
723 let node_slots = slot_len.checked_mul(node_cap).ok_or_else(|| {
724 XlogError::Compilation("GpuCircuitCache node slots overflow".to_string())
725 })?;
726 let edge_slots = slot_len.checked_mul(edge_cap).ok_or_else(|| {
727 XlogError::Compilation("GpuCircuitCache edge slots overflow".to_string())
728 })?;
729 let var_slots = slot_len.checked_mul(var_cap + 1).ok_or_else(|| {
730 XlogError::Compilation("GpuCircuitCache var slots overflow".to_string())
731 })?;
732 let node_offsets = slot_len.checked_mul(node_cap + 1).ok_or_else(|| {
733 XlogError::Compilation("GpuCircuitCache offset slots overflow".to_string())
734 })?;
735 let level_offsets = slot_len.checked_mul(level_cap + 1).ok_or_else(|| {
736 XlogError::Compilation("GpuCircuitCache level offsets overflow".to_string())
737 })?;
738
739 let mut keys = memory.alloc::<u64>(table_len)?;
740 let mut slots = memory.alloc::<u32>(table_len)?;
741 let mut state = memory.alloc::<u32>(table_len)?;
742 let mut last_used = memory.alloc::<u64>(table_len)?;
743 let mut slot_states = memory.alloc::<u32>(slot_len)?;
744 let mut clock = memory.alloc::<u64>(1)?;
745
746 let mut node_type = memory.alloc::<u8>(node_slots)?;
747 let mut child_offsets = memory.alloc::<u32>(node_offsets)?;
748 let mut child_indices = memory.alloc::<u32>(edge_slots)?;
749 let mut lit = memory.alloc::<i32>(node_slots)?;
750 let mut decision_var = memory.alloc::<u32>(node_slots)?;
751 let mut decision_child_false = memory.alloc::<u32>(node_slots)?;
752 let mut decision_child_true = memory.alloc::<u32>(node_slots)?;
753 let mut level_nodes = memory.alloc::<u32>(node_slots)?;
754 let mut level_offsets = memory.alloc::<u32>(level_offsets)?;
755
756 let mut var_log_true = memory.alloc::<f64>(var_slots)?;
757 let mut var_log_false = memory.alloc::<f64>(var_slots)?;
758 let mut values = memory.alloc::<f64>(node_slots)?;
759 let mut adj = memory.alloc::<f64>(node_slots)?;
760 let mut grad_true = memory.alloc::<f64>(var_slots)?;
761 let mut grad_false = memory.alloc::<f64>(var_slots)?;
762 let mut free_var_mask = memory.alloc::<u8>(var_slots)?;
763 let mut meta_num_nodes = memory.alloc::<u32>(slot_len)?;
764 let mut meta_num_levels = memory.alloc::<u32>(slot_len)?;
765 let mut meta_root = memory.alloc::<u32>(slot_len)?;
766 let mut meta_max_var = memory.alloc::<u32>(slot_len)?;
767 let mut always_on = memory.alloc::<u32>(1)?;
768 let zero_len = node_cap.max(var_cap + 1);
769 let mut zero_f64 = memory.alloc::<f64>(zero_len)?;
770 let mut one_f64 = memory.alloc::<f64>(1)?;
771
772 device
773 .memset_zeros(&mut keys)
774 .map_err(|e| XlogError::Kernel(format!("GpuCircuitCache zero keys failed: {}", e)))?;
775 device
776 .memset_zeros(&mut slots)
777 .map_err(|e| XlogError::Kernel(format!("GpuCircuitCache zero slots failed: {}", e)))?;
778 device
779 .memset_zeros(&mut state)
780 .map_err(|e| XlogError::Kernel(format!("GpuCircuitCache zero state failed: {}", e)))?;
781 device.memset_zeros(&mut last_used).map_err(|e| {
782 XlogError::Kernel(format!("GpuCircuitCache zero last_used failed: {}", e))
783 })?;
784 device.memset_zeros(&mut slot_states).map_err(|e| {
785 XlogError::Kernel(format!("GpuCircuitCache zero slot_states failed: {}", e))
786 })?;
787 device
788 .memset_zeros(&mut clock)
789 .map_err(|e| XlogError::Kernel(format!("GpuCircuitCache zero clock failed: {}", e)))?;
790
791 device.memset_zeros(&mut node_type).map_err(|e| {
792 XlogError::Kernel(format!("GpuCircuitCache zero node_type failed: {}", e))
793 })?;
794 device.memset_zeros(&mut child_offsets).map_err(|e| {
795 XlogError::Kernel(format!("GpuCircuitCache zero child_offsets failed: {}", e))
796 })?;
797 device.memset_zeros(&mut child_indices).map_err(|e| {
798 XlogError::Kernel(format!("GpuCircuitCache zero child_indices failed: {}", e))
799 })?;
800 device
801 .memset_zeros(&mut lit)
802 .map_err(|e| XlogError::Kernel(format!("GpuCircuitCache zero lit failed: {}", e)))?;
803 device.memset_zeros(&mut decision_var).map_err(|e| {
804 XlogError::Kernel(format!("GpuCircuitCache zero decision_var failed: {}", e))
805 })?;
806 device
807 .memset_zeros(&mut decision_child_false)
808 .map_err(|e| {
809 XlogError::Kernel(format!(
810 "GpuCircuitCache zero decision_child_false failed: {}",
811 e
812 ))
813 })?;
814 device.memset_zeros(&mut decision_child_true).map_err(|e| {
815 XlogError::Kernel(format!(
816 "GpuCircuitCache zero decision_child_true failed: {}",
817 e
818 ))
819 })?;
820 device.memset_zeros(&mut level_nodes).map_err(|e| {
821 XlogError::Kernel(format!("GpuCircuitCache zero level_nodes failed: {}", e))
822 })?;
823 device.memset_zeros(&mut level_offsets).map_err(|e| {
824 XlogError::Kernel(format!("GpuCircuitCache zero level_offsets failed: {}", e))
825 })?;
826 device.memset_zeros(&mut var_log_true).map_err(|e| {
827 XlogError::Kernel(format!("GpuCircuitCache zero var_log_true failed: {}", e))
828 })?;
829 device.memset_zeros(&mut var_log_false).map_err(|e| {
830 XlogError::Kernel(format!("GpuCircuitCache zero var_log_false failed: {}", e))
831 })?;
832 device
833 .memset_zeros(&mut values)
834 .map_err(|e| XlogError::Kernel(format!("GpuCircuitCache zero values failed: {}", e)))?;
835 device
836 .memset_zeros(&mut adj)
837 .map_err(|e| XlogError::Kernel(format!("GpuCircuitCache zero adj failed: {}", e)))?;
838 device.memset_zeros(&mut grad_true).map_err(|e| {
839 XlogError::Kernel(format!("GpuCircuitCache zero grad_true failed: {}", e))
840 })?;
841 device.memset_zeros(&mut grad_false).map_err(|e| {
842 XlogError::Kernel(format!("GpuCircuitCache zero grad_false failed: {}", e))
843 })?;
844 device.memset_zeros(&mut free_var_mask).map_err(|e| {
845 XlogError::Kernel(format!("GpuCircuitCache zero free_var_mask failed: {}", e))
846 })?;
847 device.memset_zeros(&mut meta_num_nodes).map_err(|e| {
848 XlogError::Kernel(format!("GpuCircuitCache zero meta_num_nodes failed: {}", e))
849 })?;
850 device.memset_zeros(&mut meta_num_levels).map_err(|e| {
851 XlogError::Kernel(format!(
852 "GpuCircuitCache zero meta_num_levels failed: {}",
853 e
854 ))
855 })?;
856 device.memset_zeros(&mut meta_root).map_err(|e| {
857 XlogError::Kernel(format!("GpuCircuitCache zero meta_root failed: {}", e))
858 })?;
859 device.memset_zeros(&mut meta_max_var).map_err(|e| {
860 XlogError::Kernel(format!("GpuCircuitCache zero meta_max_var failed: {}", e))
861 })?;
862 device.memset_zeros(&mut zero_f64).map_err(|e| {
863 XlogError::Kernel(format!("GpuCircuitCache zero zero_f64 failed: {}", e))
864 })?;
865 provider
866 .htod_launch_metadata_sync_copy_into(&[1u32], &mut always_on)
867 .map_err(|e| {
868 XlogError::Kernel(format!("GpuCircuitCache init always_on failed: {}", e))
869 })?;
870 provider
871 .htod_launch_metadata_sync_copy_into(&[1.0f64], &mut one_f64)
872 .map_err(|e| {
873 XlogError::Kernel(format!("GpuCircuitCache init one_f64 failed: {}", e))
874 })?;
875
876 Ok(Self {
877 provider: provider.clone(),
878 table_size: config.table_size,
879 num_slots: config.num_slots,
880 node_cap: config.node_cap,
881 edge_cap: config.edge_cap,
882 level_cap: config.level_cap,
883 var_cap: config.var_cap,
884 keys,
885 slots,
886 state,
887 last_used,
888 slot_states,
889 clock,
890 node_type,
891 child_offsets,
892 child_indices,
893 lit,
894 decision_var,
895 decision_child_false,
896 decision_child_true,
897 level_nodes,
898 level_offsets,
899 var_log_true,
900 var_log_false,
901 values,
902 adj,
903 grad_true,
904 grad_false,
905 meta_num_nodes,
906 meta_num_levels,
907 meta_root,
908 meta_max_var,
909 always_on,
910 zero_f64,
911 one_f64,
912 free_var_mask,
913 has_free_var_mask: vec![false; config.num_slots as usize],
914 })
915 }
916
917 pub fn lookup_or_insert(&mut self, key: u64) -> Result<GpuCacheLookup> {
919 let memory = self.provider.memory();
920 let mut key_device = memory.alloc::<u64>(1)?;
921 self.provider
922 .htod_launch_metadata_sync_copy_into(&[key], &mut key_device)
923 .map_err(|e| XlogError::Kernel(format!("cache upload key failed: {}", e)))?;
924 self.lookup_or_insert_device(&key_device)
925 }
926
927 pub(crate) fn lookup_or_insert_device(
928 &mut self,
929 key_device: &TrackedCudaSlice<u64>,
930 ) -> Result<GpuCacheLookup> {
931 let memory = self.provider.memory();
932 let mut out_slot = memory.alloc::<u32>(1)?;
933 let mut out_compile_needed = memory.alloc::<u32>(1)?;
934
935 let func = self
936 .provider
937 .device()
938 .inner()
939 .get_func(CACHE_MODULE, cache_kernels::CACHE_LOOKUP_OR_INSERT)
940 .ok_or_else(|| {
941 XlogError::Kernel("cache_lookup_or_insert kernel not found".to_string())
942 })?;
943
944 unsafe {
946 func.clone().launch(
947 LaunchConfig {
948 grid_dim: (1, 1, 1),
949 block_dim: (1, 1, 1),
950 shared_mem_bytes: 0,
951 },
952 (
953 key_device,
954 self.table_size,
955 self.num_slots,
956 &mut self.keys,
957 &mut self.slots,
958 &mut self.state,
959 &mut self.last_used,
960 &mut self.slot_states,
961 &mut self.clock,
962 &mut out_slot,
963 &mut out_compile_needed,
964 ),
965 )
966 }
967 .map_err(|e| XlogError::Kernel(format!("cache_lookup_or_insert failed: {}", e)))?;
968 Ok(GpuCacheLookup {
970 provider: self.provider.clone(),
971 slot: out_slot,
972 compile_needed: out_compile_needed,
973 })
974 }
975
976 pub fn claim_slot(&mut self, key: u64) -> Result<GpuCircuitCacheHandle> {
978 let lookup = self.lookup_or_insert(key)?;
979 lookup.into_handle()
980 }
981
982 pub fn store_from_xgcf(
984 &mut self,
985 handle: &mut GpuCircuitCacheHandle,
986 xgcf: &GpuXgcf,
987 ) -> Result<()> {
988 let device = self.provider.device().inner();
993 let num_nodes_host: Vec<u32> = device
994 .dtoh_sync_copy(xgcf.num_nodes_device())
995 .map_err(|e| XlogError::Kernel(format!("dtoh meta_num_nodes: {}", e)))?;
996 let num_nodes = num_nodes_host[0];
997 if num_nodes == 0 {
998 return Err(XlogError::Compilation(
999 "GpuCircuitCache store: num_nodes must be > 0".to_string(),
1000 ));
1001 }
1002 if num_nodes > self.node_cap {
1003 return Err(XlogError::Compilation(format!(
1004 "GpuCircuitCache store: num_nodes {} exceeds node_cap {}",
1005 num_nodes, self.node_cap
1006 )));
1007 }
1008
1009 let num_edges_host: Vec<u32> = device
1010 .dtoh_sync_copy(xgcf.num_edges_device())
1011 .map_err(|e| XlogError::Kernel(format!("dtoh meta_num_edges: {}", e)))?;
1012 let num_edges = num_edges_host[0];
1013 if num_edges > self.edge_cap {
1014 return Err(XlogError::Compilation(format!(
1015 "GpuCircuitCache store: num_edges {} exceeds edge_cap {}",
1016 num_edges, self.edge_cap
1017 )));
1018 }
1019
1020 let num_levels = xgcf.num_levels();
1021 if num_levels == 0 {
1022 return Err(XlogError::Compilation(
1023 "GpuCircuitCache store: num_levels must be > 0".to_string(),
1024 ));
1025 }
1026 if num_levels > self.level_cap {
1027 return Err(XlogError::Compilation(format!(
1028 "GpuCircuitCache store: num_levels {} exceeds level_cap {}",
1029 num_levels, self.level_cap
1030 )));
1031 }
1032
1033 let root = xgcf.root();
1034 if root >= num_nodes {
1035 return Err(XlogError::Compilation(format!(
1036 "GpuCircuitCache store: root {} out of bounds (num_nodes={})",
1037 root, num_nodes
1038 )));
1039 }
1040
1041 let max_var = xgcf.max_var();
1042 if max_var > self.var_cap {
1043 return Err(XlogError::Compilation(format!(
1044 "GpuCircuitCache store: max_var {} exceeds var_cap {}",
1045 max_var, self.var_cap
1046 )));
1047 }
1048
1049 let expected_child_offsets = (num_nodes as usize) + 1;
1050 if xgcf.child_offsets().len() < expected_child_offsets {
1051 return Err(XlogError::Compilation(format!(
1052 "GpuCircuitCache store: child_offsets len {} < num_nodes+1 {}",
1053 xgcf.child_offsets().len(),
1054 expected_child_offsets
1055 )));
1056 }
1057 if xgcf.level_nodes().len() < num_nodes as usize {
1058 return Err(XlogError::Compilation(format!(
1059 "GpuCircuitCache store: level_nodes len {} < num_nodes {}",
1060 xgcf.level_nodes().len(),
1061 num_nodes
1062 )));
1063 }
1064 let expected_level_offsets = (num_levels as usize) + 1;
1065 if xgcf.level_offsets().len() != expected_level_offsets {
1066 return Err(XlogError::Compilation(format!(
1067 "GpuCircuitCache store: level_offsets len {} != num_levels+1 {}",
1068 xgcf.level_offsets().len(),
1069 expected_level_offsets
1070 )));
1071 }
1072
1073 handle.num_nodes = num_nodes;
1074 handle.num_levels = num_levels;
1075 handle.root = root;
1076 handle.max_var = max_var;
1077
1078 let store_u8 = device
1079 .get_func(CACHE_MODULE, cache_kernels::CACHE_STORE_U8)
1080 .ok_or_else(|| XlogError::Kernel("cache_store_u8 kernel not found".to_string()))?;
1081 let store_u32 = device
1082 .get_func(CACHE_MODULE, cache_kernels::CACHE_STORE_U32)
1083 .ok_or_else(|| XlogError::Kernel("cache_store_u32 kernel not found".to_string()))?;
1084 let store_i32 = device
1085 .get_func(CACHE_MODULE, cache_kernels::CACHE_STORE_I32)
1086 .ok_or_else(|| XlogError::Kernel("cache_store_i32 kernel not found".to_string()))?;
1087 let store_f64 = device
1088 .get_func(CACHE_MODULE, cache_kernels::CACHE_STORE_F64)
1089 .ok_or_else(|| XlogError::Kernel("cache_store_f64 kernel not found".to_string()))?;
1090 let store_meta = device
1091 .get_func(CACHE_MODULE, cache_kernels::CACHE_STORE_META)
1092 .ok_or_else(|| XlogError::Kernel("cache_store_meta kernel not found".to_string()))?;
1093
1094 let block_dim = 256u32;
1095
1096 let node_stride = self.node_cap;
1097 let offset_stride = self.node_cap.checked_add(1).ok_or_else(|| {
1098 XlogError::Compilation("GpuCircuitCache store: node_cap overflow".to_string())
1099 })?;
1100 let level_offset_stride = self.level_cap.checked_add(1).ok_or_else(|| {
1101 XlogError::Compilation("GpuCircuitCache store: level_cap overflow".to_string())
1102 })?;
1103 let var_stride = self.var_cap.checked_add(1).ok_or_else(|| {
1104 XlogError::Compilation("GpuCircuitCache store: var_cap overflow".to_string())
1105 })?;
1106
1107 let num_nodes_plus1 = num_nodes.checked_add(1).ok_or_else(|| {
1108 XlogError::Compilation("GpuCircuitCache store: num_nodes overflow".to_string())
1109 })?;
1110 let num_levels_plus1 = num_levels.checked_add(1).ok_or_else(|| {
1111 XlogError::Compilation("GpuCircuitCache store: num_levels overflow".to_string())
1112 })?;
1113 let weights_len = max_var.checked_add(1).ok_or_else(|| {
1114 XlogError::Compilation("GpuCircuitCache store: max_var overflow".to_string())
1115 })?;
1116
1117 let grid_nodes =
1118 cache_grid_dim_for_u32_count("GpuCircuitCache store node_type", num_nodes, block_dim)?;
1119 if grid_nodes != 0 {
1120 unsafe {
1122 store_u8.clone().launch(
1123 LaunchConfig {
1124 grid_dim: (grid_nodes, 1, 1),
1125 block_dim: (block_dim, 1, 1),
1126 shared_mem_bytes: 0,
1127 },
1128 (
1129 handle.slot_device(),
1130 handle.compile_needed_device(),
1131 node_stride,
1132 xgcf.node_type(),
1133 &mut self.node_type,
1134 num_nodes,
1135 ),
1136 )
1137 }
1138 .map_err(|e| XlogError::Kernel(format!("cache_store_u8 failed: {}", e)))?;
1139 }
1140
1141 let grid_offsets = cache_grid_dim_for_u32_count(
1142 "GpuCircuitCache store child_offsets",
1143 num_nodes_plus1,
1144 block_dim,
1145 )?;
1146 if grid_offsets != 0 {
1147 unsafe {
1149 store_u32.clone().launch(
1150 LaunchConfig {
1151 grid_dim: (grid_offsets, 1, 1),
1152 block_dim: (block_dim, 1, 1),
1153 shared_mem_bytes: 0,
1154 },
1155 (
1156 handle.slot_device(),
1157 handle.compile_needed_device(),
1158 offset_stride,
1159 xgcf.child_offsets(),
1160 &mut self.child_offsets,
1161 num_nodes_plus1,
1162 ),
1163 )
1164 }
1165 .map_err(|e| XlogError::Kernel(format!("cache_store_child_offsets failed: {}", e)))?;
1166 }
1167
1168 let grid_edges = cache_grid_dim_for_u32_count(
1169 "GpuCircuitCache store child_indices",
1170 num_edges,
1171 block_dim,
1172 )?;
1173 if grid_edges != 0 {
1174 unsafe {
1176 store_u32.clone().launch(
1177 LaunchConfig {
1178 grid_dim: (grid_edges, 1, 1),
1179 block_dim: (block_dim, 1, 1),
1180 shared_mem_bytes: 0,
1181 },
1182 (
1183 handle.slot_device(),
1184 handle.compile_needed_device(),
1185 self.edge_cap,
1186 xgcf.child_indices(),
1187 &mut self.child_indices,
1188 num_edges,
1189 ),
1190 )
1191 }
1192 .map_err(|e| XlogError::Kernel(format!("cache_store_child_indices failed: {}", e)))?;
1193 }
1194
1195 if grid_nodes != 0 {
1196 unsafe {
1198 store_i32.clone().launch(
1199 LaunchConfig {
1200 grid_dim: (grid_nodes, 1, 1),
1201 block_dim: (block_dim, 1, 1),
1202 shared_mem_bytes: 0,
1203 },
1204 (
1205 handle.slot_device(),
1206 handle.compile_needed_device(),
1207 node_stride,
1208 xgcf.lit(),
1209 &mut self.lit,
1210 num_nodes,
1211 ),
1212 )
1213 }
1214 .map_err(|e| XlogError::Kernel(format!("cache_store_lit failed: {}", e)))?;
1215
1216 unsafe {
1218 store_u32.clone().launch(
1219 LaunchConfig {
1220 grid_dim: (grid_nodes, 1, 1),
1221 block_dim: (block_dim, 1, 1),
1222 shared_mem_bytes: 0,
1223 },
1224 (
1225 handle.slot_device(),
1226 handle.compile_needed_device(),
1227 node_stride,
1228 xgcf.decision_var(),
1229 &mut self.decision_var,
1230 num_nodes,
1231 ),
1232 )
1233 }
1234 .map_err(|e| XlogError::Kernel(format!("cache_store_decision_var failed: {}", e)))?;
1235
1236 unsafe {
1238 store_u32.clone().launch(
1239 LaunchConfig {
1240 grid_dim: (grid_nodes, 1, 1),
1241 block_dim: (block_dim, 1, 1),
1242 shared_mem_bytes: 0,
1243 },
1244 (
1245 handle.slot_device(),
1246 handle.compile_needed_device(),
1247 node_stride,
1248 xgcf.decision_child_false(),
1249 &mut self.decision_child_false,
1250 num_nodes,
1251 ),
1252 )
1253 }
1254 .map_err(|e| {
1255 XlogError::Kernel(format!("cache_store_decision_child_false failed: {}", e))
1256 })?;
1257
1258 unsafe {
1260 store_u32.clone().launch(
1261 LaunchConfig {
1262 grid_dim: (grid_nodes, 1, 1),
1263 block_dim: (block_dim, 1, 1),
1264 shared_mem_bytes: 0,
1265 },
1266 (
1267 handle.slot_device(),
1268 handle.compile_needed_device(),
1269 node_stride,
1270 xgcf.decision_child_true(),
1271 &mut self.decision_child_true,
1272 num_nodes,
1273 ),
1274 )
1275 }
1276 .map_err(|e| {
1277 XlogError::Kernel(format!("cache_store_decision_child_true failed: {}", e))
1278 })?;
1279
1280 unsafe {
1282 store_u32.clone().launch(
1283 LaunchConfig {
1284 grid_dim: (grid_nodes, 1, 1),
1285 block_dim: (block_dim, 1, 1),
1286 shared_mem_bytes: 0,
1287 },
1288 (
1289 handle.slot_device(),
1290 handle.compile_needed_device(),
1291 node_stride,
1292 xgcf.level_nodes(),
1293 &mut self.level_nodes,
1294 num_nodes,
1295 ),
1296 )
1297 }
1298 .map_err(|e| XlogError::Kernel(format!("cache_store_level_nodes failed: {}", e)))?;
1299 }
1300
1301 let grid_levels = cache_grid_dim_for_u32_count(
1302 "GpuCircuitCache store level_offsets",
1303 num_levels_plus1,
1304 block_dim,
1305 )?;
1306 if grid_levels != 0 {
1307 unsafe {
1309 store_u32.clone().launch(
1310 LaunchConfig {
1311 grid_dim: (grid_levels, 1, 1),
1312 block_dim: (block_dim, 1, 1),
1313 shared_mem_bytes: 0,
1314 },
1315 (
1316 handle.slot_device(),
1317 handle.compile_needed_device(),
1318 level_offset_stride,
1319 xgcf.level_offsets(),
1320 &mut self.level_offsets,
1321 num_levels_plus1,
1322 ),
1323 )
1324 }
1325 .map_err(|e| XlogError::Kernel(format!("cache_store_level_offsets failed: {}", e)))?;
1326 }
1327
1328 let grid_weights = cache_grid_dim_for_u32_count(
1329 "GpuCircuitCache store free_var_mask",
1330 weights_len,
1331 block_dim,
1332 )?;
1333 if grid_weights != 0 {
1334 unsafe {
1336 store_f64.clone().launch(
1337 LaunchConfig {
1338 grid_dim: (grid_weights, 1, 1),
1339 block_dim: (block_dim, 1, 1),
1340 shared_mem_bytes: 0,
1341 },
1342 (
1343 handle.slot_device(),
1344 handle.compile_needed_device(),
1345 var_stride,
1346 xgcf.var_log_true(),
1347 &mut self.var_log_true,
1348 weights_len,
1349 ),
1350 )
1351 }
1352 .map_err(|e| XlogError::Kernel(format!("cache_store_var_log_true failed: {}", e)))?;
1353
1354 unsafe {
1356 store_f64.clone().launch(
1357 LaunchConfig {
1358 grid_dim: (grid_weights, 1, 1),
1359 block_dim: (block_dim, 1, 1),
1360 shared_mem_bytes: 0,
1361 },
1362 (
1363 handle.slot_device(),
1364 handle.compile_needed_device(),
1365 var_stride,
1366 xgcf.var_log_false(),
1367 &mut self.var_log_false,
1368 weights_len,
1369 ),
1370 )
1371 }
1372 .map_err(|e| XlogError::Kernel(format!("cache_store_var_log_false failed: {}", e)))?;
1373 }
1374
1375 unsafe {
1377 store_meta.clone().launch(
1378 LaunchConfig {
1379 grid_dim: (1, 1, 1),
1380 block_dim: (1, 1, 1),
1381 shared_mem_bytes: 0,
1382 },
1383 (
1384 handle.slot_device(),
1385 handle.compile_needed_device(),
1386 self.num_slots,
1387 num_nodes,
1388 num_levels,
1389 root,
1390 max_var,
1391 &mut self.meta_num_nodes,
1392 &mut self.meta_num_levels,
1393 &mut self.meta_root,
1394 &mut self.meta_max_var,
1395 ),
1396 )
1397 }
1398 .map_err(|e| XlogError::Kernel(format!("cache_store_meta failed: {}", e)))?;
1399
1400 Ok(())
1403 }
1404
1405 pub fn store_weights(
1407 &mut self,
1408 handle: &GpuCircuitCacheHandle,
1409 weights_true: &TrackedCudaSlice<f64>,
1410 weights_false: &TrackedCudaSlice<f64>,
1411 ) -> Result<()> {
1412 let weights_len = handle.max_var.checked_add(1).ok_or_else(|| {
1413 XlogError::Compilation("GpuCircuitCache store_weights max_var overflow".to_string())
1414 })?;
1415 let weights_len_usize = usize::try_from(weights_len).map_err(|_| {
1416 XlogError::Compilation("GpuCircuitCache store_weights len overflow".to_string())
1417 })?;
1418 if weights_true.len() < weights_len_usize || weights_false.len() < weights_len_usize {
1419 return Err(XlogError::Compilation(format!(
1420 "GpuCircuitCache store_weights requires weights len >= {}, got true={} false={}",
1421 weights_len,
1422 weights_true.len(),
1423 weights_false.len()
1424 )));
1425 }
1426
1427 let device = self.provider.device().inner();
1428 let store_f64 = device
1429 .get_func(CACHE_MODULE, cache_kernels::CACHE_STORE_F64)
1430 .ok_or_else(|| XlogError::Kernel("cache_store_f64 kernel not found".to_string()))?;
1431
1432 let block_dim = 256u32;
1433 let grid_dim = if weights_len == 0 {
1434 0
1435 } else {
1436 weights_len.div_ceil(block_dim)
1437 };
1438 if grid_dim != 0 {
1439 let var_stride = self.var_cap.checked_add(1).ok_or_else(|| {
1440 XlogError::Compilation("GpuCircuitCache store_weights var_cap overflow".to_string())
1441 })?;
1442 unsafe {
1444 store_f64.clone().launch(
1445 LaunchConfig {
1446 grid_dim: (grid_dim, 1, 1),
1447 block_dim: (block_dim, 1, 1),
1448 shared_mem_bytes: 0,
1449 },
1450 (
1451 handle.slot_device(),
1452 handle.compile_needed_device(),
1453 var_stride,
1454 weights_true,
1455 &mut self.var_log_true,
1456 weights_len,
1457 ),
1458 )
1459 }
1460 .map_err(|e| XlogError::Kernel(format!("cache_store_weights_true failed: {}", e)))?;
1461
1462 unsafe {
1464 store_f64.clone().launch(
1465 LaunchConfig {
1466 grid_dim: (grid_dim, 1, 1),
1467 block_dim: (block_dim, 1, 1),
1468 shared_mem_bytes: 0,
1469 },
1470 (
1471 handle.slot_device(),
1472 handle.compile_needed_device(),
1473 var_stride,
1474 weights_false,
1475 &mut self.var_log_false,
1476 weights_len,
1477 ),
1478 )
1479 }
1480 .map_err(|e| XlogError::Kernel(format!("cache_store_weights_false failed: {}", e)))?;
1481 }
1482
1483 Ok(())
1485 }
1486
1487 pub fn overwrite_weights(
1489 &mut self,
1490 handle: &GpuCircuitCacheHandle,
1491 weights_true: &TrackedCudaSlice<f64>,
1492 weights_false: &TrackedCudaSlice<f64>,
1493 ) -> Result<()> {
1494 let weights_len = handle.max_var.checked_add(1).ok_or_else(|| {
1495 XlogError::Compilation("GpuCircuitCache overwrite_weights max_var overflow".to_string())
1496 })?;
1497 let weights_len_usize = usize::try_from(weights_len).map_err(|_| {
1498 XlogError::Compilation("GpuCircuitCache overwrite_weights len overflow".to_string())
1499 })?;
1500 if weights_true.len() < weights_len_usize || weights_false.len() < weights_len_usize {
1501 return Err(XlogError::Compilation(format!(
1502 "GpuCircuitCache overwrite_weights requires weights len >= {}, got true={} false={}",
1503 weights_len,
1504 weights_true.len(),
1505 weights_false.len()
1506 )));
1507 }
1508
1509 let device = self.provider.device().inner();
1510 let store_f64 = device
1511 .get_func(CACHE_MODULE, cache_kernels::CACHE_STORE_F64)
1512 .ok_or_else(|| XlogError::Kernel("cache_store_f64 kernel not found".to_string()))?;
1513
1514 let block_dim = 256u32;
1515 let grid_dim = if weights_len == 0 {
1516 0
1517 } else {
1518 weights_len.div_ceil(block_dim)
1519 };
1520 if grid_dim != 0 {
1521 let var_stride = self.var_cap.checked_add(1).ok_or_else(|| {
1522 XlogError::Compilation(
1523 "GpuCircuitCache overwrite_weights var_cap overflow".to_string(),
1524 )
1525 })?;
1526 unsafe {
1528 store_f64.clone().launch(
1529 LaunchConfig {
1530 grid_dim: (grid_dim, 1, 1),
1531 block_dim: (block_dim, 1, 1),
1532 shared_mem_bytes: 0,
1533 },
1534 (
1535 handle.slot_device(),
1536 &self.always_on,
1537 var_stride,
1538 weights_true,
1539 &mut self.var_log_true,
1540 weights_len,
1541 ),
1542 )
1543 }
1544 .map_err(|e| {
1545 XlogError::Kernel(format!("cache_overwrite_weights_true failed: {}", e))
1546 })?;
1547
1548 unsafe {
1550 store_f64.clone().launch(
1551 LaunchConfig {
1552 grid_dim: (grid_dim, 1, 1),
1553 block_dim: (block_dim, 1, 1),
1554 shared_mem_bytes: 0,
1555 },
1556 (
1557 handle.slot_device(),
1558 &self.always_on,
1559 var_stride,
1560 weights_false,
1561 &mut self.var_log_false,
1562 weights_len,
1563 ),
1564 )
1565 }
1566 .map_err(|e| {
1567 XlogError::Kernel(format!("cache_overwrite_weights_false failed: {}", e))
1568 })?;
1569 }
1570
1571 Ok(())
1573 }
1574
1575 pub fn store_free_var_mask(
1577 &mut self,
1578 handle: &GpuCircuitCacheHandle,
1579 mask: &TrackedCudaSlice<u8>,
1580 ) -> Result<()> {
1581 let mask_len = u32::try_from(mask.len()).map_err(|_| {
1582 XlogError::Compilation("GpuCircuitCache free_var_mask len overflow".to_string())
1583 })?;
1584 let expected_len = handle.max_var.checked_add(1).ok_or_else(|| {
1585 XlogError::Compilation("GpuCircuitCache free_var_mask max_var overflow".to_string())
1586 })?;
1587 if mask_len != expected_len {
1588 return Err(XlogError::Compilation(format!(
1589 "GpuCircuitCache free_var_mask len {} != expected {}",
1590 mask_len, expected_len
1591 )));
1592 }
1593 let var_stride = self.var_cap.checked_add(1).ok_or_else(|| {
1594 XlogError::Compilation("GpuCircuitCache free_var_mask var_cap overflow".to_string())
1595 })?;
1596 if expected_len > var_stride {
1597 return Err(XlogError::Compilation(format!(
1598 "GpuCircuitCache free_var_mask len {} exceeds var_cap+1 {}",
1599 expected_len, var_stride
1600 )));
1601 }
1602
1603 let device = self.provider.device().inner();
1604 let store_u8 = device
1605 .get_func(CACHE_MODULE, cache_kernels::CACHE_STORE_U8)
1606 .ok_or_else(|| XlogError::Kernel("cache_store_u8 kernel not found".to_string()))?;
1607
1608 let block_dim = 256u32;
1609 let grid_dim = mask_len.div_ceil(block_dim);
1610 if grid_dim == 0 {
1611 return Ok(());
1612 }
1613
1614 unsafe {
1616 store_u8.clone().launch(
1617 LaunchConfig {
1618 grid_dim: (grid_dim, 1, 1),
1619 block_dim: (block_dim, 1, 1),
1620 shared_mem_bytes: 0,
1621 },
1622 (
1623 handle.slot_device(),
1624 handle.compile_needed_device(),
1625 var_stride,
1626 mask,
1627 &mut self.free_var_mask,
1628 mask_len,
1629 ),
1630 )
1631 }
1632 .map_err(|e| XlogError::Kernel(format!("cache_store_free_var_mask failed: {}", e)))?;
1633
1634 let slot_idx = handle.slot_index() as usize;
1636 debug_assert!(
1637 slot_idx < self.has_free_var_mask.len(),
1638 "slot_index {} exceeds num_slots {}",
1639 slot_idx,
1640 self.has_free_var_mask.len()
1641 );
1642 if slot_idx < self.has_free_var_mask.len() {
1643 self.has_free_var_mask[slot_idx] = true;
1644 }
1645 Ok(())
1646 }
1647
1648 pub(crate) fn restore_from_host_arrays(
1655 &mut self,
1656 handle: &mut GpuCircuitCacheHandle,
1657 artifact: &disk_cache::CircuitArtifact,
1658 ) -> Result<()> {
1659 let num_nodes = artifact.num_nodes;
1661 if num_nodes == 0 {
1662 return Err(XlogError::Compilation(
1663 "GpuCircuitCache restore: num_nodes must be > 0".to_string(),
1664 ));
1665 }
1666 if num_nodes > self.node_cap {
1667 return Err(XlogError::Compilation(format!(
1668 "GpuCircuitCache restore: num_nodes {} exceeds node_cap {}",
1669 num_nodes, self.node_cap
1670 )));
1671 }
1672
1673 let num_edges = artifact.num_edges;
1674 if num_edges > self.edge_cap {
1675 return Err(XlogError::Compilation(format!(
1676 "GpuCircuitCache restore: num_edges {} exceeds edge_cap {}",
1677 num_edges, self.edge_cap
1678 )));
1679 }
1680
1681 let num_levels = artifact.num_levels;
1682 if num_levels == 0 {
1683 return Err(XlogError::Compilation(
1684 "GpuCircuitCache restore: num_levels must be > 0".to_string(),
1685 ));
1686 }
1687 if num_levels > self.level_cap {
1688 return Err(XlogError::Compilation(format!(
1689 "GpuCircuitCache restore: num_levels {} exceeds level_cap {}",
1690 num_levels, self.level_cap
1691 )));
1692 }
1693
1694 let root = artifact.root;
1695 if root >= num_nodes {
1696 return Err(XlogError::Compilation(format!(
1697 "GpuCircuitCache restore: root {} out of bounds (num_nodes={})",
1698 root, num_nodes
1699 )));
1700 }
1701
1702 let max_var = artifact.max_var;
1703 if max_var > self.var_cap {
1704 return Err(XlogError::Compilation(format!(
1705 "GpuCircuitCache restore: max_var {} exceeds var_cap {}",
1706 max_var, self.var_cap
1707 )));
1708 }
1709
1710 let expected_child_offsets = (num_nodes as usize) + 1;
1711 if artifact.child_offsets.len() < expected_child_offsets {
1712 return Err(XlogError::Compilation(format!(
1713 "GpuCircuitCache restore: child_offsets len {} < num_nodes+1 {}",
1714 artifact.child_offsets.len(),
1715 expected_child_offsets
1716 )));
1717 }
1718 if artifact.level_nodes.len() < num_nodes as usize {
1719 return Err(XlogError::Compilation(format!(
1720 "GpuCircuitCache restore: level_nodes len {} < num_nodes {}",
1721 artifact.level_nodes.len(),
1722 num_nodes
1723 )));
1724 }
1725 let expected_level_offsets = (num_levels as usize) + 1;
1726 if artifact.level_offsets.len() != expected_level_offsets {
1727 return Err(XlogError::Compilation(format!(
1728 "GpuCircuitCache restore: level_offsets len {} != num_levels+1 {}",
1729 artifact.level_offsets.len(),
1730 expected_level_offsets
1731 )));
1732 }
1733
1734 handle.num_nodes = num_nodes;
1736 handle.num_levels = num_levels;
1737 handle.root = root;
1738 handle.max_var = max_var;
1739
1740 let device = self.provider.device().inner();
1742 let memory = self.provider.memory();
1743
1744 let store_u8 = device
1745 .get_func(CACHE_MODULE, cache_kernels::CACHE_STORE_U8)
1746 .ok_or_else(|| XlogError::Kernel("cache_store_u8 kernel not found".to_string()))?;
1747 let store_u32 = device
1748 .get_func(CACHE_MODULE, cache_kernels::CACHE_STORE_U32)
1749 .ok_or_else(|| XlogError::Kernel("cache_store_u32 kernel not found".to_string()))?;
1750 let store_i32 = device
1751 .get_func(CACHE_MODULE, cache_kernels::CACHE_STORE_I32)
1752 .ok_or_else(|| XlogError::Kernel("cache_store_i32 kernel not found".to_string()))?;
1753 let store_meta = device
1754 .get_func(CACHE_MODULE, cache_kernels::CACHE_STORE_META)
1755 .ok_or_else(|| XlogError::Kernel("cache_store_meta kernel not found".to_string()))?;
1756
1757 let block_dim = 256u32;
1758
1759 let node_stride = self.node_cap;
1760 let offset_stride = self.node_cap.checked_add(1).ok_or_else(|| {
1761 XlogError::Compilation("GpuCircuitCache restore: node_cap overflow".to_string())
1762 })?;
1763 let level_offset_stride = self.level_cap.checked_add(1).ok_or_else(|| {
1764 XlogError::Compilation("GpuCircuitCache restore: level_cap overflow".to_string())
1765 })?;
1766 let var_stride = self.var_cap.checked_add(1).ok_or_else(|| {
1767 XlogError::Compilation("GpuCircuitCache restore: var_cap overflow".to_string())
1768 })?;
1769
1770 let num_nodes_plus1 = num_nodes.checked_add(1).ok_or_else(|| {
1771 XlogError::Compilation("GpuCircuitCache restore: num_nodes overflow".to_string())
1772 })?;
1773 let num_levels_plus1 = num_levels.checked_add(1).ok_or_else(|| {
1774 XlogError::Compilation("GpuCircuitCache restore: num_levels overflow".to_string())
1775 })?;
1776
1777 let grid_nodes = cache_grid_dim_for_u32_count(
1779 "GpuCircuitCache restore node_type",
1780 num_nodes,
1781 block_dim,
1782 )?;
1783 if grid_nodes != 0 {
1784 let mut d_node_type = memory.alloc::<u8>(num_nodes as usize)?;
1785 self.provider
1786 .htod_sync_copy_into_tracked(
1787 &artifact.node_type[..num_nodes as usize],
1788 &mut d_node_type,
1789 )
1790 .map_err(|e| XlogError::Kernel(format!("restore htod node_type failed: {}", e)))?;
1791 unsafe {
1793 store_u8.clone().launch(
1794 LaunchConfig {
1795 grid_dim: (grid_nodes, 1, 1),
1796 block_dim: (block_dim, 1, 1),
1797 shared_mem_bytes: 0,
1798 },
1799 (
1800 handle.slot_device(),
1801 handle.compile_needed_device(),
1802 node_stride,
1803 &d_node_type,
1804 &mut self.node_type,
1805 num_nodes,
1806 ),
1807 )
1808 }
1809 .map_err(|e| {
1810 XlogError::Kernel(format!("restore cache_store node_type failed: {}", e))
1811 })?;
1812 }
1813
1814 let grid_offsets = cache_grid_dim_for_u32_count(
1816 "GpuCircuitCache restore child_offsets",
1817 num_nodes_plus1,
1818 block_dim,
1819 )?;
1820 if grid_offsets != 0 {
1821 let mut d_child_offsets = memory.alloc::<u32>(num_nodes_plus1 as usize)?;
1822 self.provider
1823 .htod_sync_copy_into_tracked(
1824 &artifact.child_offsets[..num_nodes_plus1 as usize],
1825 &mut d_child_offsets,
1826 )
1827 .map_err(|e| {
1828 XlogError::Kernel(format!("restore htod child_offsets failed: {}", e))
1829 })?;
1830 unsafe {
1832 store_u32.clone().launch(
1833 LaunchConfig {
1834 grid_dim: (grid_offsets, 1, 1),
1835 block_dim: (block_dim, 1, 1),
1836 shared_mem_bytes: 0,
1837 },
1838 (
1839 handle.slot_device(),
1840 handle.compile_needed_device(),
1841 offset_stride,
1842 &d_child_offsets,
1843 &mut self.child_offsets,
1844 num_nodes_plus1,
1845 ),
1846 )
1847 }
1848 .map_err(|e| {
1849 XlogError::Kernel(format!("restore cache_store child_offsets failed: {}", e))
1850 })?;
1851 }
1852
1853 let grid_edges = cache_grid_dim_for_u32_count(
1855 "GpuCircuitCache restore child_indices",
1856 num_edges,
1857 block_dim,
1858 )?;
1859 if grid_edges != 0 {
1860 let mut d_child_indices = memory.alloc::<u32>(num_edges as usize)?;
1861 self.provider
1862 .htod_sync_copy_into_tracked(
1863 &artifact.child_indices[..num_edges as usize],
1864 &mut d_child_indices,
1865 )
1866 .map_err(|e| {
1867 XlogError::Kernel(format!("restore htod child_indices failed: {}", e))
1868 })?;
1869 unsafe {
1871 store_u32.clone().launch(
1872 LaunchConfig {
1873 grid_dim: (grid_edges, 1, 1),
1874 block_dim: (block_dim, 1, 1),
1875 shared_mem_bytes: 0,
1876 },
1877 (
1878 handle.slot_device(),
1879 handle.compile_needed_device(),
1880 self.edge_cap,
1881 &d_child_indices,
1882 &mut self.child_indices,
1883 num_edges,
1884 ),
1885 )
1886 }
1887 .map_err(|e| {
1888 XlogError::Kernel(format!("restore cache_store child_indices failed: {}", e))
1889 })?;
1890 }
1891
1892 if grid_nodes != 0 {
1894 let mut d_lit = memory.alloc::<i32>(num_nodes as usize)?;
1895 self.provider
1896 .htod_sync_copy_into_tracked(&artifact.lit[..num_nodes as usize], &mut d_lit)
1897 .map_err(|e| XlogError::Kernel(format!("restore htod lit failed: {}", e)))?;
1898 unsafe {
1900 store_i32.clone().launch(
1901 LaunchConfig {
1902 grid_dim: (grid_nodes, 1, 1),
1903 block_dim: (block_dim, 1, 1),
1904 shared_mem_bytes: 0,
1905 },
1906 (
1907 handle.slot_device(),
1908 handle.compile_needed_device(),
1909 node_stride,
1910 &d_lit,
1911 &mut self.lit,
1912 num_nodes,
1913 ),
1914 )
1915 }
1916 .map_err(|e| XlogError::Kernel(format!("restore cache_store lit failed: {}", e)))?;
1917
1918 let mut d_decision_var = memory.alloc::<u32>(num_nodes as usize)?;
1920 self.provider
1921 .htod_sync_copy_into_tracked(
1922 &artifact.decision_var[..num_nodes as usize],
1923 &mut d_decision_var,
1924 )
1925 .map_err(|e| {
1926 XlogError::Kernel(format!("restore htod decision_var failed: {}", e))
1927 })?;
1928 unsafe {
1930 store_u32.clone().launch(
1931 LaunchConfig {
1932 grid_dim: (grid_nodes, 1, 1),
1933 block_dim: (block_dim, 1, 1),
1934 shared_mem_bytes: 0,
1935 },
1936 (
1937 handle.slot_device(),
1938 handle.compile_needed_device(),
1939 node_stride,
1940 &d_decision_var,
1941 &mut self.decision_var,
1942 num_nodes,
1943 ),
1944 )
1945 }
1946 .map_err(|e| {
1947 XlogError::Kernel(format!("restore cache_store decision_var failed: {}", e))
1948 })?;
1949
1950 let mut d_decision_child_false = memory.alloc::<u32>(num_nodes as usize)?;
1952 self.provider
1953 .htod_sync_copy_into_tracked(
1954 &artifact.decision_child_false[..num_nodes as usize],
1955 &mut d_decision_child_false,
1956 )
1957 .map_err(|e| {
1958 XlogError::Kernel(format!("restore htod decision_child_false failed: {}", e))
1959 })?;
1960 unsafe {
1962 store_u32.clone().launch(
1963 LaunchConfig {
1964 grid_dim: (grid_nodes, 1, 1),
1965 block_dim: (block_dim, 1, 1),
1966 shared_mem_bytes: 0,
1967 },
1968 (
1969 handle.slot_device(),
1970 handle.compile_needed_device(),
1971 node_stride,
1972 &d_decision_child_false,
1973 &mut self.decision_child_false,
1974 num_nodes,
1975 ),
1976 )
1977 }
1978 .map_err(|e| {
1979 XlogError::Kernel(format!(
1980 "restore cache_store decision_child_false failed: {}",
1981 e
1982 ))
1983 })?;
1984
1985 let mut d_decision_child_true = memory.alloc::<u32>(num_nodes as usize)?;
1987 self.provider
1988 .htod_sync_copy_into_tracked(
1989 &artifact.decision_child_true[..num_nodes as usize],
1990 &mut d_decision_child_true,
1991 )
1992 .map_err(|e| {
1993 XlogError::Kernel(format!("restore htod decision_child_true failed: {}", e))
1994 })?;
1995 unsafe {
1997 store_u32.clone().launch(
1998 LaunchConfig {
1999 grid_dim: (grid_nodes, 1, 1),
2000 block_dim: (block_dim, 1, 1),
2001 shared_mem_bytes: 0,
2002 },
2003 (
2004 handle.slot_device(),
2005 handle.compile_needed_device(),
2006 node_stride,
2007 &d_decision_child_true,
2008 &mut self.decision_child_true,
2009 num_nodes,
2010 ),
2011 )
2012 }
2013 .map_err(|e| {
2014 XlogError::Kernel(format!(
2015 "restore cache_store decision_child_true failed: {}",
2016 e
2017 ))
2018 })?;
2019
2020 let mut d_level_nodes = memory.alloc::<u32>(num_nodes as usize)?;
2022 self.provider
2023 .htod_sync_copy_into_tracked(
2024 &artifact.level_nodes[..num_nodes as usize],
2025 &mut d_level_nodes,
2026 )
2027 .map_err(|e| {
2028 XlogError::Kernel(format!("restore htod level_nodes failed: {}", e))
2029 })?;
2030 unsafe {
2032 store_u32.clone().launch(
2033 LaunchConfig {
2034 grid_dim: (grid_nodes, 1, 1),
2035 block_dim: (block_dim, 1, 1),
2036 shared_mem_bytes: 0,
2037 },
2038 (
2039 handle.slot_device(),
2040 handle.compile_needed_device(),
2041 node_stride,
2042 &d_level_nodes,
2043 &mut self.level_nodes,
2044 num_nodes,
2045 ),
2046 )
2047 }
2048 .map_err(|e| {
2049 XlogError::Kernel(format!("restore cache_store level_nodes failed: {}", e))
2050 })?;
2051 }
2052
2053 let grid_levels = cache_grid_dim_for_u32_count(
2055 "GpuCircuitCache restore level_offsets",
2056 num_levels_plus1,
2057 block_dim,
2058 )?;
2059 if grid_levels != 0 {
2060 let mut d_level_offsets = memory.alloc::<u32>(num_levels_plus1 as usize)?;
2061 self.provider
2062 .htod_sync_copy_into_tracked(
2063 &artifact.level_offsets[..num_levels_plus1 as usize],
2064 &mut d_level_offsets,
2065 )
2066 .map_err(|e| {
2067 XlogError::Kernel(format!("restore htod level_offsets failed: {}", e))
2068 })?;
2069 unsafe {
2071 store_u32.clone().launch(
2072 LaunchConfig {
2073 grid_dim: (grid_levels, 1, 1),
2074 block_dim: (block_dim, 1, 1),
2075 shared_mem_bytes: 0,
2076 },
2077 (
2078 handle.slot_device(),
2079 handle.compile_needed_device(),
2080 level_offset_stride,
2081 &d_level_offsets,
2082 &mut self.level_offsets,
2083 num_levels_plus1,
2084 ),
2085 )
2086 }
2087 .map_err(|e| {
2088 XlogError::Kernel(format!("restore cache_store level_offsets failed: {}", e))
2089 })?;
2090 }
2091
2092 unsafe {
2095 store_meta.clone().launch(
2096 LaunchConfig {
2097 grid_dim: (1, 1, 1),
2098 block_dim: (1, 1, 1),
2099 shared_mem_bytes: 0,
2100 },
2101 (
2102 handle.slot_device(),
2103 handle.compile_needed_device(),
2104 self.num_slots,
2105 num_nodes,
2106 num_levels,
2107 root,
2108 max_var,
2109 &mut self.meta_num_nodes,
2110 &mut self.meta_num_levels,
2111 &mut self.meta_root,
2112 &mut self.meta_max_var,
2113 ),
2114 )
2115 }
2116 .map_err(|e| XlogError::Kernel(format!("restore cache_store_meta failed: {}", e)))?;
2117
2118 let slot_idx = handle.slot_index() as usize;
2120
2121 let mask_cap = var_stride; let grid_mask_zero = cache_grid_dim_for_u32_count(
2125 "GpuCircuitCache restore zero free_var_mask",
2126 mask_cap,
2127 block_dim,
2128 )?;
2129 if grid_mask_zero != 0 {
2130 let mut d_zeros = memory.alloc::<u8>(mask_cap as usize)?;
2131 device.memset_zeros(&mut d_zeros).map_err(|e| {
2132 XlogError::Kernel(format!("restore memset_zeros free_var_mask failed: {}", e))
2133 })?;
2134 unsafe {
2136 store_u8.clone().launch(
2137 LaunchConfig {
2138 grid_dim: (grid_mask_zero, 1, 1),
2139 block_dim: (block_dim, 1, 1),
2140 shared_mem_bytes: 0,
2141 },
2142 (
2143 handle.slot_device(),
2144 handle.compile_needed_device(),
2145 var_stride,
2146 &d_zeros,
2147 &mut self.free_var_mask,
2148 mask_cap,
2149 ),
2150 )
2151 }
2152 .map_err(|e| {
2153 XlogError::Kernel(format!(
2154 "restore cache_store zero free_var_mask failed: {}",
2155 e
2156 ))
2157 })?;
2158 }
2159
2160 let has_mask = artifact.has_free_var_mask && !artifact.free_var_mask.is_empty();
2162 if has_mask {
2163 let mask_len = max_var.checked_add(1).ok_or_else(|| {
2164 XlogError::Compilation(
2165 "GpuCircuitCache restore: free_var_mask max_var overflow".to_string(),
2166 )
2167 })?;
2168 let actual_len = std::cmp::min(mask_len as usize, artifact.free_var_mask.len());
2169 if actual_len > 0 {
2170 let actual_len_u32 = u32::try_from(actual_len).map_err(|_| {
2171 XlogError::Compilation(
2172 "GpuCircuitCache restore free_var_mask len exceeds u32".to_string(),
2173 )
2174 })?;
2175 let grid_mask = cache_grid_dim_for_u32_count(
2176 "GpuCircuitCache restore free_var_mask",
2177 actual_len_u32,
2178 block_dim,
2179 )?;
2180 if grid_mask != 0 {
2181 let mut d_mask = memory.alloc::<u8>(actual_len)?;
2182 self.provider
2183 .htod_sync_copy_into_tracked(
2184 &artifact.free_var_mask[..actual_len],
2185 &mut d_mask,
2186 )
2187 .map_err(|e| {
2188 XlogError::Kernel(format!("restore htod free_var_mask failed: {}", e))
2189 })?;
2190 unsafe {
2192 store_u8.clone().launch(
2193 LaunchConfig {
2194 grid_dim: (grid_mask, 1, 1),
2195 block_dim: (block_dim, 1, 1),
2196 shared_mem_bytes: 0,
2197 },
2198 (
2199 handle.slot_device(),
2200 handle.compile_needed_device(),
2201 var_stride,
2202 &d_mask,
2203 &mut self.free_var_mask,
2204 actual_len_u32,
2205 ),
2206 )
2207 }
2208 .map_err(|e| {
2209 XlogError::Kernel(format!(
2210 "restore cache_store free_var_mask failed: {}",
2211 e
2212 ))
2213 })?;
2214 }
2215 }
2216 }
2217
2218 debug_assert!(
2220 slot_idx < self.has_free_var_mask.len(),
2221 "slot_index {} exceeds num_slots {}",
2222 slot_idx,
2223 self.has_free_var_mask.len()
2224 );
2225 if slot_idx < self.has_free_var_mask.len() {
2226 self.has_free_var_mask[slot_idx] = has_mask;
2227 }
2228
2229 Ok(())
2232 }
2233
2234 pub(crate) fn build_artifact_from_device(
2241 &self,
2242 handle: &GpuCircuitCacheHandle,
2243 provider: &Arc<CudaKernelProvider>,
2244 ) -> Result<disk_cache::CircuitArtifact> {
2245 let device = provider.device().inner();
2246 let slot = handle.slot_index() as usize;
2247 let num_nodes = handle.num_nodes();
2248 let num_levels = handle.num_levels();
2249 let root = handle.root();
2250 let max_var = handle.max_var();
2251
2252 if num_nodes == 0 {
2253 return Err(XlogError::Compilation(
2254 "build_artifact_from_device: num_nodes is 0".to_string(),
2255 ));
2256 }
2257
2258 let node_stride = self.node_cap as usize;
2259 let offset_stride = (self.node_cap as usize) + 1;
2260 let edge_stride = self.edge_cap as usize;
2261 let level_offset_stride = (self.level_cap as usize) + 1;
2262 let var_stride = (self.var_cap as usize) + 1;
2263
2264 let slot_node_start = slot * node_stride;
2265 let slot_offset_start = slot * offset_stride;
2266 let slot_level_offset_start = slot * level_offset_stride;
2267 let slot_var_start = slot * var_stride;
2268
2269 let nn = num_nodes as usize;
2270 let nn1 = nn + 1;
2271 let nl1 = (num_levels as usize) + 1;
2272
2273 let child_offsets_view = self
2276 .child_offsets
2277 .slice(slot_offset_start..(slot_offset_start + nn1));
2278 let child_offsets: Vec<u32> = device
2279 .dtoh_sync_copy(&child_offsets_view)
2280 .map_err(|e| XlogError::Kernel(format!("build_artifact dtoh child_offsets: {}", e)))?;
2281 let num_edges = if nn1 > 0 {
2282 child_offsets[nn]
2283 .checked_sub(child_offsets[0])
2284 .ok_or_else(|| {
2285 XlogError::Compilation(
2286 "build_artifact_from_device: child_offsets[num_nodes] < child_offsets[0]"
2287 .to_string(),
2288 )
2289 })?
2290 } else {
2291 0
2292 };
2293
2294 let slot_edge_start = slot * edge_stride;
2296 let ne = num_edges as usize;
2297 let child_indices: Vec<u32> = if ne > 0 {
2298 let view = self
2299 .child_indices
2300 .slice(slot_edge_start..(slot_edge_start + ne));
2301 device.dtoh_sync_copy(&view).map_err(|e| {
2302 XlogError::Kernel(format!("build_artifact dtoh child_indices: {}", e))
2303 })?
2304 } else {
2305 Vec::new()
2306 };
2307
2308 let node_type_view = self
2310 .node_type
2311 .slice(slot_node_start..(slot_node_start + nn));
2312 let node_type: Vec<u8> = device
2313 .dtoh_sync_copy(&node_type_view)
2314 .map_err(|e| XlogError::Kernel(format!("build_artifact dtoh node_type: {}", e)))?;
2315
2316 let lit_view = self.lit.slice(slot_node_start..(slot_node_start + nn));
2318 let lit: Vec<i32> = device
2319 .dtoh_sync_copy(&lit_view)
2320 .map_err(|e| XlogError::Kernel(format!("build_artifact dtoh lit: {}", e)))?;
2321
2322 let dv_view = self
2324 .decision_var
2325 .slice(slot_node_start..(slot_node_start + nn));
2326 let decision_var: Vec<u32> = device
2327 .dtoh_sync_copy(&dv_view)
2328 .map_err(|e| XlogError::Kernel(format!("build_artifact dtoh decision_var: {}", e)))?;
2329
2330 let dcf_view = self
2332 .decision_child_false
2333 .slice(slot_node_start..(slot_node_start + nn));
2334 let decision_child_false: Vec<u32> = device.dtoh_sync_copy(&dcf_view).map_err(|e| {
2335 XlogError::Kernel(format!("build_artifact dtoh decision_child_false: {}", e))
2336 })?;
2337
2338 let dct_view = self
2340 .decision_child_true
2341 .slice(slot_node_start..(slot_node_start + nn));
2342 let decision_child_true: Vec<u32> = device.dtoh_sync_copy(&dct_view).map_err(|e| {
2343 XlogError::Kernel(format!("build_artifact dtoh decision_child_true: {}", e))
2344 })?;
2345
2346 let ln_view = self
2348 .level_nodes
2349 .slice(slot_node_start..(slot_node_start + nn));
2350 let level_nodes: Vec<u32> = device
2351 .dtoh_sync_copy(&ln_view)
2352 .map_err(|e| XlogError::Kernel(format!("build_artifact dtoh level_nodes: {}", e)))?;
2353
2354 let lo_view = self
2356 .level_offsets
2357 .slice(slot_level_offset_start..(slot_level_offset_start + nl1));
2358 let level_offsets: Vec<u32> = device
2359 .dtoh_sync_copy(&lo_view)
2360 .map_err(|e| XlogError::Kernel(format!("build_artifact dtoh level_offsets: {}", e)))?;
2361
2362 let has_free_var_mask = self.has_free_var_mask_for_slot(slot as u32);
2364 let mask_len = (max_var as usize) + 1;
2365 let free_var_mask: Vec<u8> = if mask_len > 0 {
2366 let fvm_view = self
2367 .free_var_mask
2368 .slice(slot_var_start..(slot_var_start + mask_len));
2369 device.dtoh_sync_copy(&fvm_view).map_err(|e| {
2370 XlogError::Kernel(format!("build_artifact dtoh free_var_mask: {}", e))
2371 })?
2372 } else {
2373 Vec::new()
2374 };
2375
2376 Ok(disk_cache::CircuitArtifact {
2377 num_nodes,
2378 num_edges,
2379 num_levels,
2380 root,
2381 max_var,
2382 has_free_var_mask,
2383 node_type,
2384 child_offsets,
2385 child_indices,
2386 lit,
2387 decision_var,
2388 decision_child_false,
2389 decision_child_true,
2390 level_nodes,
2391 level_offsets,
2392 free_var_mask,
2393 })
2394 }
2395
2396 pub fn eval_log_wmc_device_inplace(
2403 &mut self,
2404 handle: &GpuCircuitCacheHandle,
2405 out_log_z: &mut TrackedCudaSlice<f64>,
2406 ) -> Result<()> {
2407 self.eval_log_wmc_device_only(handle, out_log_z)
2408 }
2409
2410 pub fn eval_log_wmc_device_only(
2413 &mut self,
2414 handle: &GpuCircuitCacheHandle,
2415 out_log_z: &mut TrackedCudaSlice<f64>,
2416 ) -> Result<()> {
2417 if out_log_z.len() != 1 {
2418 return Err(XlogError::Compilation(format!(
2419 "GPU cache logZ output len {} != 1",
2420 out_log_z.len()
2421 )));
2422 }
2423
2424 {
2425 let device = self.provider.device().inner();
2426 let eval_all = device
2427 .get_func(
2428 xlog_cuda::CIRCUIT_MODULE,
2429 xlog_cuda::circuit_kernels::XGCF_EVAL_ALL_LEVELS_CACHED,
2430 )
2431 .ok_or_else(|| {
2432 XlogError::Kernel("xgcf_eval_all_levels_cached kernel not found".to_string())
2433 })?;
2434
2435 let block_size: u32 = 256;
2436 let mut params: Vec<*mut std::ffi::c_void> = vec![
2437 handle.slot_device().as_kernel_param(),
2438 self.node_cap.as_kernel_param(),
2439 self.edge_cap.as_kernel_param(),
2440 self.level_cap.as_kernel_param(),
2441 self.var_cap.as_kernel_param(),
2442 (&self.node_type).as_kernel_param(),
2443 (&self.child_offsets).as_kernel_param(),
2444 (&self.child_indices).as_kernel_param(),
2445 (&self.lit).as_kernel_param(),
2446 (&self.decision_var).as_kernel_param(),
2447 (&self.decision_child_false).as_kernel_param(),
2448 (&self.decision_child_true).as_kernel_param(),
2449 (&self.level_nodes).as_kernel_param(),
2450 (&self.level_offsets).as_kernel_param(),
2451 (&self.var_log_true).as_kernel_param(),
2452 (&self.var_log_false).as_kernel_param(),
2453 (&self.values).as_kernel_param(),
2454 (&self.meta_num_levels).as_kernel_param(),
2455 ];
2456 unsafe {
2458 eval_all.clone().launch(
2459 LaunchConfig {
2460 grid_dim: (1, 1, 1),
2461 block_dim: (block_size, 1, 1),
2462 shared_mem_bytes: 0,
2463 },
2464 &mut params,
2465 )
2466 }
2467 .map_err(|e| XlogError::Kernel(format!("xgcf_eval_all_levels_cached failed: {}", e)))?;
2468 }
2469
2470 self.apply_free_var_correction_cached(handle, true, false)?;
2471
2472 let device = self.provider.device().inner();
2473 let copy_root = device
2474 .get_func(
2475 xlog_cuda::CIRCUIT_MODULE,
2476 xlog_cuda::circuit_kernels::XGCF_COPY_ROOT_CACHED_META,
2477 )
2478 .ok_or_else(|| {
2479 XlogError::Kernel("xgcf_copy_root_cached_meta kernel not found".to_string())
2480 })?;
2481 unsafe {
2483 copy_root.clone().launch(
2484 LaunchConfig {
2485 grid_dim: (1, 1, 1),
2486 block_dim: (1, 1, 1),
2487 shared_mem_bytes: 0,
2488 },
2489 (
2490 handle.slot_device(),
2491 self.node_cap,
2492 &self.values,
2493 &self.meta_root,
2494 out_log_z,
2495 ),
2496 )
2497 }
2498 .map_err(|e| XlogError::Kernel(format!("xgcf_copy_root_cached_meta failed: {}", e)))?;
2499
2500 Ok(())
2503 }
2504
2505 pub fn eval_grads_inplace(&mut self, handle: &GpuCircuitCacheHandle) -> Result<()> {
2512 let device = self.provider.device().inner();
2513 let eval_all = device
2514 .get_func(
2515 xlog_cuda::CIRCUIT_MODULE,
2516 xlog_cuda::circuit_kernels::XGCF_EVAL_ALL_LEVELS_CACHED,
2517 )
2518 .ok_or_else(|| {
2519 XlogError::Kernel("xgcf_eval_all_levels_cached kernel not found".to_string())
2520 })?;
2521 let block_size: u32 = 256;
2522 let mut params: Vec<*mut std::ffi::c_void> = vec![
2523 handle.slot_device().as_kernel_param(),
2524 self.node_cap.as_kernel_param(),
2525 self.edge_cap.as_kernel_param(),
2526 self.level_cap.as_kernel_param(),
2527 self.var_cap.as_kernel_param(),
2528 (&self.node_type).as_kernel_param(),
2529 (&self.child_offsets).as_kernel_param(),
2530 (&self.child_indices).as_kernel_param(),
2531 (&self.lit).as_kernel_param(),
2532 (&self.decision_var).as_kernel_param(),
2533 (&self.decision_child_false).as_kernel_param(),
2534 (&self.decision_child_true).as_kernel_param(),
2535 (&self.level_nodes).as_kernel_param(),
2536 (&self.level_offsets).as_kernel_param(),
2537 (&self.var_log_true).as_kernel_param(),
2538 (&self.var_log_false).as_kernel_param(),
2539 (&self.values).as_kernel_param(),
2540 (&self.meta_num_levels).as_kernel_param(),
2541 ];
2542 unsafe {
2544 eval_all.clone().launch(
2545 LaunchConfig {
2546 grid_dim: (1, 1, 1),
2547 block_dim: (block_size, 1, 1),
2548 shared_mem_bytes: 0,
2549 },
2550 &mut params,
2551 )
2552 }
2553 .map_err(|e| XlogError::Kernel(format!("xgcf_eval_all_levels_cached failed: {}", e)))?;
2554
2555 let device = self.provider.device().inner();
2556 let store_f64 = device
2557 .get_func(CACHE_MODULE, cache_kernels::CACHE_STORE_F64)
2558 .ok_or_else(|| XlogError::Kernel("cache_store_f64 kernel not found".to_string()))?;
2559
2560 let node_stride = self.node_cap;
2561 let var_stride = self.var_cap.checked_add(1).ok_or_else(|| {
2562 XlogError::Compilation("GPU cache eval_grads var_cap overflow".to_string())
2563 })?;
2564 let weights_len = self.var_cap.checked_add(1).ok_or_else(|| {
2565 XlogError::Compilation("GPU cache eval_grads var_cap overflow".to_string())
2566 })?;
2567
2568 let grid_nodes = cache_grid_dim_for_u32_count(
2569 "GpuCircuitCache eval_grads zero adj",
2570 self.node_cap,
2571 block_size,
2572 )?;
2573 if grid_nodes != 0 {
2574 unsafe {
2576 store_f64.clone().launch(
2577 LaunchConfig {
2578 grid_dim: (grid_nodes, 1, 1),
2579 block_dim: (block_size, 1, 1),
2580 shared_mem_bytes: 0,
2581 },
2582 (
2583 handle.slot_device(),
2584 &self.always_on,
2585 node_stride,
2586 &self.zero_f64,
2587 &mut self.adj,
2588 self.node_cap,
2589 ),
2590 )
2591 }
2592 .map_err(|e| XlogError::Kernel(format!("cache zero adj failed: {}", e)))?;
2593 }
2594
2595 let grid_weights = cache_grid_dim_for_u32_count(
2596 "GpuCircuitCache eval_grads zero weights",
2597 weights_len,
2598 block_size,
2599 )?;
2600 if grid_weights != 0 {
2601 unsafe {
2603 store_f64.clone().launch(
2604 LaunchConfig {
2605 grid_dim: (grid_weights, 1, 1),
2606 block_dim: (block_size, 1, 1),
2607 shared_mem_bytes: 0,
2608 },
2609 (
2610 handle.slot_device(),
2611 &self.always_on,
2612 var_stride,
2613 &self.zero_f64,
2614 &mut self.grad_true,
2615 weights_len,
2616 ),
2617 )
2618 }
2619 .map_err(|e| XlogError::Kernel(format!("cache zero grad_true failed: {}", e)))?;
2620
2621 unsafe {
2623 store_f64.clone().launch(
2624 LaunchConfig {
2625 grid_dim: (grid_weights, 1, 1),
2626 block_dim: (block_size, 1, 1),
2627 shared_mem_bytes: 0,
2628 },
2629 (
2630 handle.slot_device(),
2631 &self.always_on,
2632 var_stride,
2633 &self.zero_f64,
2634 &mut self.grad_false,
2635 weights_len,
2636 ),
2637 )
2638 }
2639 .map_err(|e| XlogError::Kernel(format!("cache zero grad_false failed: {}", e)))?;
2640 }
2641
2642 let add_scalar = device
2643 .get_func(
2644 xlog_cuda::CIRCUIT_MODULE,
2645 xlog_cuda::circuit_kernels::XGCF_ADD_SCALAR_CACHED,
2646 )
2647 .ok_or_else(|| {
2648 XlogError::Kernel("xgcf_add_scalar_cached kernel not found".to_string())
2649 })?;
2650 unsafe {
2652 add_scalar.clone().launch(
2653 LaunchConfig {
2654 grid_dim: (1, 1, 1),
2655 block_dim: (1, 1, 1),
2656 shared_mem_bytes: 0,
2657 },
2658 (
2659 handle.slot_device(),
2660 self.node_cap,
2661 &mut self.adj,
2662 &self.meta_root,
2663 &self.one_f64,
2664 ),
2665 )
2666 }
2667 .map_err(|e| XlogError::Kernel(format!("xgcf_add_scalar_cached (adj) failed: {}", e)))?;
2668
2669 let propagate = device
2670 .get_func(
2671 xlog_cuda::CIRCUIT_MODULE,
2672 xlog_cuda::circuit_kernels::XGCF_BACKWARD_LEVEL_PROPAGATE_CACHED,
2673 )
2674 .ok_or_else(|| {
2675 XlogError::Kernel(
2676 "xgcf_backward_level_propagate_cached kernel not found".to_string(),
2677 )
2678 })?;
2679 let decision_grad = device
2680 .get_func(
2681 xlog_cuda::CIRCUIT_MODULE,
2682 xlog_cuda::circuit_kernels::XGCF_BACKWARD_LEVEL_DECISION_GRAD_CACHED,
2683 )
2684 .ok_or_else(|| {
2685 XlogError::Kernel(
2686 "xgcf_backward_level_decision_grad_cached kernel not found".to_string(),
2687 )
2688 })?;
2689 let lit_grad = device
2690 .get_func(
2691 xlog_cuda::CIRCUIT_MODULE,
2692 xlog_cuda::circuit_kernels::XGCF_BACKWARD_LEVEL_LIT_GRAD_CACHED,
2693 )
2694 .ok_or_else(|| {
2695 XlogError::Kernel(
2696 "xgcf_backward_level_lit_grad_cached kernel not found".to_string(),
2697 )
2698 })?;
2699
2700 let num_blocks = self.node_cap.div_ceil(block_size);
2701 let num_levels = self.level_cap;
2702 for level in (0..num_levels).rev() {
2703 if num_blocks == 0 {
2704 continue;
2705 }
2706 let level_u32: u32 = level;
2707 let mut params: Vec<*mut std::ffi::c_void> = vec![
2708 handle.slot_device().as_kernel_param(),
2709 self.node_cap.as_kernel_param(),
2710 self.edge_cap.as_kernel_param(),
2711 self.level_cap.as_kernel_param(),
2712 self.var_cap.as_kernel_param(),
2713 (&self.node_type).as_kernel_param(),
2714 (&self.child_offsets).as_kernel_param(),
2715 (&self.child_indices).as_kernel_param(),
2716 (&self.decision_var).as_kernel_param(),
2717 (&self.decision_child_false).as_kernel_param(),
2718 (&self.decision_child_true).as_kernel_param(),
2719 (&self.level_nodes).as_kernel_param(),
2720 (&self.level_offsets).as_kernel_param(),
2721 level_u32.as_kernel_param(),
2722 (&self.var_log_true).as_kernel_param(),
2723 (&self.var_log_false).as_kernel_param(),
2724 (&self.values).as_kernel_param(),
2725 (&self.adj).as_kernel_param(),
2726 (&self.meta_num_levels).as_kernel_param(),
2727 ];
2728
2729 unsafe {
2731 propagate.clone().launch(
2732 LaunchConfig {
2733 grid_dim: (num_blocks, 1, 1),
2734 block_dim: (block_size, 1, 1),
2735 shared_mem_bytes: 0,
2736 },
2737 &mut params,
2738 )
2739 }
2740 .map_err(|e| {
2741 XlogError::Kernel(format!(
2742 "xgcf_backward_level_propagate_cached failed: {}",
2743 e
2744 ))
2745 })?;
2746
2747 let mut params: Vec<*mut std::ffi::c_void> = vec![
2748 handle.slot_device().as_kernel_param(),
2749 self.node_cap.as_kernel_param(),
2750 self.edge_cap.as_kernel_param(),
2751 self.level_cap.as_kernel_param(),
2752 self.var_cap.as_kernel_param(),
2753 (&self.node_type).as_kernel_param(),
2754 (&self.decision_var).as_kernel_param(),
2755 (&self.decision_child_false).as_kernel_param(),
2756 (&self.decision_child_true).as_kernel_param(),
2757 (&self.level_nodes).as_kernel_param(),
2758 (&self.level_offsets).as_kernel_param(),
2759 level_u32.as_kernel_param(),
2760 (&self.var_log_true).as_kernel_param(),
2761 (&self.var_log_false).as_kernel_param(),
2762 (&self.values).as_kernel_param(),
2763 (&self.adj).as_kernel_param(),
2764 (&self.grad_true).as_kernel_param(),
2765 (&self.grad_false).as_kernel_param(),
2766 (&self.meta_num_levels).as_kernel_param(),
2767 ];
2768
2769 unsafe {
2771 decision_grad.clone().launch(
2772 LaunchConfig {
2773 grid_dim: (num_blocks, 1, 1),
2774 block_dim: (block_size, 1, 1),
2775 shared_mem_bytes: 0,
2776 },
2777 &mut params,
2778 )
2779 }
2780 .map_err(|e| {
2781 XlogError::Kernel(format!(
2782 "xgcf_backward_level_decision_grad_cached failed: {}",
2783 e
2784 ))
2785 })?;
2786
2787 let mut params: Vec<*mut std::ffi::c_void> = vec![
2788 handle.slot_device().as_kernel_param(),
2789 self.node_cap.as_kernel_param(),
2790 self.edge_cap.as_kernel_param(),
2791 self.level_cap.as_kernel_param(),
2792 self.var_cap.as_kernel_param(),
2793 (&self.node_type).as_kernel_param(),
2794 (&self.lit).as_kernel_param(),
2795 (&self.level_nodes).as_kernel_param(),
2796 (&self.level_offsets).as_kernel_param(),
2797 level_u32.as_kernel_param(),
2798 (&self.adj).as_kernel_param(),
2799 (&self.grad_true).as_kernel_param(),
2800 (&self.grad_false).as_kernel_param(),
2801 (&self.meta_num_levels).as_kernel_param(),
2802 ];
2803
2804 unsafe {
2806 lit_grad.clone().launch(
2807 LaunchConfig {
2808 grid_dim: (num_blocks, 1, 1),
2809 block_dim: (block_size, 1, 1),
2810 shared_mem_bytes: 0,
2811 },
2812 &mut params,
2813 )
2814 }
2815 .map_err(|e| {
2816 XlogError::Kernel(format!("xgcf_backward_level_lit_grad_cached failed: {}", e))
2817 })?;
2818 }
2819
2820 self.apply_free_var_correction_cached(handle, true, true)?;
2821 Ok(())
2824 }
2825
2826 pub fn eval_grads_inplace_fused(&mut self, handle: &GpuCircuitCacheHandle) -> Result<()> {
2832 let device = self.provider.device().inner();
2833 let eval_all = device
2834 .get_func(
2835 xlog_cuda::CIRCUIT_MODULE,
2836 xlog_cuda::circuit_kernels::XGCF_EVAL_ALL_LEVELS_CACHED,
2837 )
2838 .ok_or_else(|| {
2839 XlogError::Kernel("xgcf_eval_all_levels_cached kernel not found".to_string())
2840 })?;
2841 let block_size: u32 = 256;
2842 let mut params: Vec<*mut std::ffi::c_void> = vec![
2843 handle.slot_device().as_kernel_param(),
2844 self.node_cap.as_kernel_param(),
2845 self.edge_cap.as_kernel_param(),
2846 self.level_cap.as_kernel_param(),
2847 self.var_cap.as_kernel_param(),
2848 (&self.node_type).as_kernel_param(),
2849 (&self.child_offsets).as_kernel_param(),
2850 (&self.child_indices).as_kernel_param(),
2851 (&self.lit).as_kernel_param(),
2852 (&self.decision_var).as_kernel_param(),
2853 (&self.decision_child_false).as_kernel_param(),
2854 (&self.decision_child_true).as_kernel_param(),
2855 (&self.level_nodes).as_kernel_param(),
2856 (&self.level_offsets).as_kernel_param(),
2857 (&self.var_log_true).as_kernel_param(),
2858 (&self.var_log_false).as_kernel_param(),
2859 (&self.values).as_kernel_param(),
2860 (&self.meta_num_levels).as_kernel_param(),
2861 ];
2862 unsafe {
2864 eval_all.clone().launch(
2865 LaunchConfig {
2866 grid_dim: (1, 1, 1),
2867 block_dim: (block_size, 1, 1),
2868 shared_mem_bytes: 0,
2869 },
2870 &mut params,
2871 )
2872 }
2873 .map_err(|e| XlogError::Kernel(format!("xgcf_eval_all_levels_cached failed: {}", e)))?;
2874
2875 let device = self.provider.device().inner();
2876 let store_f64 = device
2877 .get_func(CACHE_MODULE, cache_kernels::CACHE_STORE_F64)
2878 .ok_or_else(|| XlogError::Kernel("cache_store_f64 kernel not found".to_string()))?;
2879
2880 let node_stride = self.node_cap;
2881 let var_stride = self.var_cap.checked_add(1).ok_or_else(|| {
2882 XlogError::Compilation("GPU cache eval_grads var_cap overflow".to_string())
2883 })?;
2884 let weights_len = self.var_cap.checked_add(1).ok_or_else(|| {
2885 XlogError::Compilation("GPU cache eval_grads var_cap overflow".to_string())
2886 })?;
2887
2888 let grid_nodes = cache_grid_dim_for_u32_count(
2889 "GpuCircuitCache batched eval_grads zero adj",
2890 self.node_cap,
2891 block_size,
2892 )?;
2893 if grid_nodes != 0 {
2894 unsafe {
2896 store_f64.clone().launch(
2897 LaunchConfig {
2898 grid_dim: (grid_nodes, 1, 1),
2899 block_dim: (block_size, 1, 1),
2900 shared_mem_bytes: 0,
2901 },
2902 (
2903 handle.slot_device(),
2904 &self.always_on,
2905 node_stride,
2906 &self.zero_f64,
2907 &mut self.adj,
2908 self.node_cap,
2909 ),
2910 )
2911 }
2912 .map_err(|e| XlogError::Kernel(format!("cache zero adj failed: {}", e)))?;
2913 }
2914
2915 let grid_weights = cache_grid_dim_for_u32_count(
2916 "GpuCircuitCache batched eval_grads zero weights",
2917 weights_len,
2918 block_size,
2919 )?;
2920 if grid_weights != 0 {
2921 unsafe {
2923 store_f64.clone().launch(
2924 LaunchConfig {
2925 grid_dim: (grid_weights, 1, 1),
2926 block_dim: (block_size, 1, 1),
2927 shared_mem_bytes: 0,
2928 },
2929 (
2930 handle.slot_device(),
2931 &self.always_on,
2932 var_stride,
2933 &self.zero_f64,
2934 &mut self.grad_true,
2935 weights_len,
2936 ),
2937 )
2938 }
2939 .map_err(|e| XlogError::Kernel(format!("cache zero grad_true failed: {}", e)))?;
2940
2941 unsafe {
2943 store_f64.clone().launch(
2944 LaunchConfig {
2945 grid_dim: (grid_weights, 1, 1),
2946 block_dim: (block_size, 1, 1),
2947 shared_mem_bytes: 0,
2948 },
2949 (
2950 handle.slot_device(),
2951 &self.always_on,
2952 var_stride,
2953 &self.zero_f64,
2954 &mut self.grad_false,
2955 weights_len,
2956 ),
2957 )
2958 }
2959 .map_err(|e| XlogError::Kernel(format!("cache zero grad_false failed: {}", e)))?;
2960 }
2961
2962 let add_scalar = device
2963 .get_func(
2964 xlog_cuda::CIRCUIT_MODULE,
2965 xlog_cuda::circuit_kernels::XGCF_ADD_SCALAR_CACHED,
2966 )
2967 .ok_or_else(|| {
2968 XlogError::Kernel("xgcf_add_scalar_cached kernel not found".to_string())
2969 })?;
2970 unsafe {
2972 add_scalar.clone().launch(
2973 LaunchConfig {
2974 grid_dim: (1, 1, 1),
2975 block_dim: (1, 1, 1),
2976 shared_mem_bytes: 0,
2977 },
2978 (
2979 handle.slot_device(),
2980 self.node_cap,
2981 &mut self.adj,
2982 &self.meta_root,
2983 &self.one_f64,
2984 ),
2985 )
2986 }
2987 .map_err(|e| XlogError::Kernel(format!("xgcf_add_scalar_cached (adj) failed: {}", e)))?;
2988
2989 let backward_all = device
2991 .get_func(
2992 xlog_cuda::CIRCUIT_MODULE,
2993 xlog_cuda::circuit_kernels::XGCF_BACKWARD_ALL_LEVELS_CACHED,
2994 )
2995 .ok_or_else(|| XlogError::Kernel("xgcf_backward_all_levels_cached not found".into()))?;
2996
2997 let mut params: Vec<*mut std::ffi::c_void> = vec![
2998 handle.slot_device().as_kernel_param(),
2999 self.node_cap.as_kernel_param(),
3000 self.edge_cap.as_kernel_param(),
3001 self.level_cap.as_kernel_param(),
3002 self.var_cap.as_kernel_param(),
3003 (&self.node_type).as_kernel_param(),
3004 (&self.child_offsets).as_kernel_param(),
3005 (&self.child_indices).as_kernel_param(),
3006 (&self.decision_var).as_kernel_param(),
3007 (&self.decision_child_false).as_kernel_param(),
3008 (&self.decision_child_true).as_kernel_param(),
3009 (&self.lit).as_kernel_param(),
3010 (&self.level_nodes).as_kernel_param(),
3011 (&self.level_offsets).as_kernel_param(),
3012 (&self.var_log_true).as_kernel_param(),
3013 (&self.var_log_false).as_kernel_param(),
3014 (&self.values).as_kernel_param(),
3015 (&self.adj).as_kernel_param(),
3016 (&self.grad_true).as_kernel_param(),
3017 (&self.grad_false).as_kernel_param(),
3018 (&self.meta_num_levels).as_kernel_param(),
3019 ];
3020
3021 unsafe {
3023 backward_all.clone().launch(
3024 LaunchConfig {
3025 grid_dim: (1, 1, 1),
3026 block_dim: (block_size, 1, 1),
3027 shared_mem_bytes: 0,
3028 },
3029 &mut params,
3030 )
3031 }
3032 .map_err(|e| XlogError::Kernel(format!("xgcf_backward_all_levels_cached failed: {}", e)))?;
3033
3034 self.apply_free_var_correction_cached(handle, true, true)?;
3035 Ok(())
3036 }
3037
3038 fn apply_free_var_correction_cached(
3039 &mut self,
3040 handle: &GpuCircuitCacheHandle,
3041 apply_log_z: bool,
3042 apply_grads: bool,
3043 ) -> Result<()> {
3044 if !self.has_free_var_mask_for_slot(handle.slot_index()) {
3045 return Ok(());
3046 }
3047 let n = self
3048 .var_cap
3049 .checked_add(1)
3050 .ok_or_else(|| XlogError::Compilation("GPU cache free-var overflow".to_string()))?;
3051 if n == 0 {
3052 return Ok(());
3053 }
3054
3055 let device = self.provider.device().inner();
3056 let block_dim = 256u32;
3057 let grid_dim = n.div_ceil(block_dim);
3058
3059 if apply_grads {
3060 let apply_grad = device
3061 .get_func(
3062 xlog_cuda::CIRCUIT_MODULE,
3063 xlog_cuda::circuit_kernels::XGCF_FREE_VAR_APPLY_GRAD_CACHED,
3064 )
3065 .ok_or_else(|| {
3066 XlogError::Kernel(
3067 "xgcf_free_var_apply_grad_cached kernel not found".to_string(),
3068 )
3069 })?;
3070 unsafe {
3072 apply_grad.clone().launch(
3073 LaunchConfig {
3074 grid_dim: (grid_dim, 1, 1),
3075 block_dim: (block_dim, 1, 1),
3076 shared_mem_bytes: 0,
3077 },
3078 (
3079 handle.slot_device(),
3080 self.var_cap,
3081 &self.free_var_mask,
3082 &self.var_log_true,
3083 &self.var_log_false,
3084 n,
3085 &mut self.grad_true,
3086 &mut self.grad_false,
3087 ),
3088 )
3089 }
3090 .map_err(|e| {
3091 XlogError::Kernel(format!("xgcf_free_var_apply_grad_cached failed: {}", e))
3092 })?;
3093 }
3094
3095 if apply_log_z {
3096 let reduce_stage = device
3097 .get_func(
3098 xlog_cuda::CIRCUIT_MODULE,
3099 xlog_cuda::circuit_kernels::XGCF_FREE_VAR_REDUCE_STAGE_CACHED,
3100 )
3101 .ok_or_else(|| {
3102 XlogError::Kernel(
3103 "xgcf_free_var_reduce_stage_cached kernel not found".to_string(),
3104 )
3105 })?;
3106 let add_scalar = device
3107 .get_func(
3108 xlog_cuda::CIRCUIT_MODULE,
3109 xlog_cuda::circuit_kernels::XGCF_ADD_SCALAR_CACHED,
3110 )
3111 .ok_or_else(|| {
3112 XlogError::Kernel("xgcf_add_scalar_cached kernel not found".to_string())
3113 })?;
3114
3115 let memory = self.provider.memory();
3116 let mut buf_a = memory.alloc::<f64>(n as usize)?;
3117 let mut buf_b = memory.alloc::<f64>(n as usize)?;
3118
3119 let mut stage_n = n;
3120 let mut stage0 = true;
3121 let mut output_is_a = true;
3122 loop {
3123 let out_len = stage_n.div_ceil(2);
3124 let stage_grid = out_len.div_ceil(block_dim);
3125
3126 let (in_buf, out_buf): (&TrackedCudaSlice<f64>, &mut TrackedCudaSlice<f64>) =
3127 if output_is_a {
3128 (&buf_b, &mut buf_a)
3129 } else {
3130 (&buf_a, &mut buf_b)
3131 };
3132 let mode = if stage0 { 0u32 } else { 1u32 };
3133
3134 unsafe {
3136 reduce_stage.clone().launch(
3137 LaunchConfig {
3138 grid_dim: (stage_grid, 1, 1),
3139 block_dim: (block_dim, 1, 1),
3140 shared_mem_bytes: 0,
3141 },
3142 (
3143 handle.slot_device(),
3144 self.var_cap,
3145 &self.free_var_mask,
3146 &self.var_log_true,
3147 &self.var_log_false,
3148 in_buf,
3149 stage_n,
3150 mode,
3151 out_buf,
3152 ),
3153 )
3154 }
3155 .map_err(|e| {
3156 XlogError::Kernel(format!("xgcf_free_var_reduce_stage_cached failed: {}", e))
3157 })?;
3158
3159 if out_len == 1 {
3160 let result_buf = if output_is_a { &buf_a } else { &buf_b };
3161 unsafe {
3163 add_scalar.clone().launch(
3164 LaunchConfig {
3165 grid_dim: (1, 1, 1),
3166 block_dim: (1, 1, 1),
3167 shared_mem_bytes: 0,
3168 },
3169 (
3170 handle.slot_device(),
3171 self.node_cap,
3172 &mut self.values,
3173 &self.meta_root,
3174 result_buf,
3175 ),
3176 )
3177 }
3178 .map_err(|e| {
3179 XlogError::Kernel(format!("xgcf_add_scalar_cached failed: {}", e))
3180 })?;
3181 break;
3182 }
3183
3184 stage_n = out_len;
3185 stage0 = false;
3186 output_is_a = !output_is_a;
3187 }
3188 }
3189
3190 Ok(())
3191 }
3192}