#![allow(clippy::too_many_arguments)]
use super::super::{
sizing, DirectIfdsEntry, DirectIfdsKey, DirectResidentIfdsBatch, DirectResidentIfdsGraph,
DirectResidentIfdsGraphKey,
};
use crate::ifds_gpu::IfdsResidentDispatch;
use crate::ifds_resident_direct_prepare::prepare_ifds_csr_resident_direct_with_scratch_via;
impl<R> DirectResidentIfdsBatch<R>
where
R: Clone,
{
#[allow(clippy::too_many_arguments)]
pub fn solve_many_with_graph_key_into<D>(
&mut self,
dispatch: &D,
graph_key: DirectResidentIfdsGraphKey,
num_procs: u32,
blocks_per_proc: u32,
facts_per_proc: u32,
intra_edges: &[(u32, u32, u32)],
inter_edges: &[(u32, u32, u32, u32)],
flow_gen: &[(u32, u32, u32)],
flow_kill: &[(u32, u32, u32)],
seed_sets: &[&[(u32, u32, u32)]],
max_iterations: u32,
results: &mut Vec<Vec<u32>>,
) -> Result<(), String>
where
D: IfdsResidentDispatch<Resource = R>,
{
if seed_sets.is_empty() {
results.clear();
return Ok(());
}
if seed_sets.iter().all(|seed_facts| seed_facts.is_empty()) {
crate::dispatch_decode::require_positive_iterations(
"weir direct resident IFDS solve with precomputed graph key into",
max_iterations,
)?;
super::resize_and_clear_result_slots(results, seed_sets.len())?;
return Ok(());
}
graph_key.validate_edges(
num_procs,
blocks_per_proc,
facts_per_proc,
intra_edges,
inter_edges,
flow_gen,
flow_kill,
)?;
self.solve_many_validated_graph_key_into(
dispatch,
graph_key,
intra_edges,
inter_edges,
flow_gen,
flow_kill,
seed_sets,
max_iterations,
results,
)
}
pub fn solve_many_with_graph_view_into<D>(
&mut self,
dispatch: &D,
graph: DirectResidentIfdsGraph<'_>,
seed_sets: &[&[(u32, u32, u32)]],
max_iterations: u32,
results: &mut Vec<Vec<u32>>,
) -> Result<(), String>
where
D: IfdsResidentDispatch<Resource = R>,
{
if seed_sets.is_empty() {
results.clear();
return Ok(());
}
if seed_sets.iter().all(|seed_facts| seed_facts.is_empty()) {
crate::dispatch_decode::require_positive_iterations(
"weir direct resident IFDS solve with graph view into",
max_iterations,
)?;
super::resize_and_clear_result_slots(results, seed_sets.len())?;
return Ok(());
}
self.solve_many_validated_graph_key_into(
dispatch,
graph.key,
graph.intra_edges,
graph.inter_edges,
graph.flow_gen,
graph.flow_kill,
seed_sets,
max_iterations,
results,
)
}
pub(super) fn solve_many_validated_graph_key_into<D>(
&mut self,
dispatch: &D,
graph_key: DirectResidentIfdsGraphKey,
intra_edges: &[(u32, u32, u32)],
inter_edges: &[(u32, u32, u32, u32)],
flow_gen: &[(u32, u32, u32)],
flow_kill: &[(u32, u32, u32)],
seed_sets: &[&[(u32, u32, u32)]],
max_iterations: u32,
results: &mut Vec<Vec<u32>>,
) -> Result<(), String>
where
D: IfdsResidentDispatch<Resource = R>,
{
let key = DirectIfdsKey {
backend_id: dispatch.resident_backend_id(),
backend_version: dispatch.resident_backend_version(),
num_procs: graph_key.num_procs,
blocks_per_proc: graph_key.blocks_per_proc,
facts_per_proc: graph_key.facts_per_proc,
graph_hash: graph_key.graph_hash,
};
if self.entries.contains_key(&key) {
self.hits = self
.hits
.checked_add(1)
.ok_or_else(|| {
"weir direct resident IFDS hit counter overflowed u64. Fix: rebuild the direct resident batch facade before reuse."
.to_string()
})?;
let retained_bytes = self
.entries
.get(&key)
.ok_or_else(|| {
"weir direct resident IFDS batch cache lookup failed before hit telemetry"
.to_string()
})?
.retained_bytes;
self.record_warm_graph_reuse(retained_bytes)?;
self.touch_key(&key)?;
let resident = &self
.entries
.get(&key)
.ok_or_else(|| {
"weir direct resident IFDS batch cache lookup failed after hit".to_string()
})?
.graph;
if seed_sets.iter().all(|seed_facts| seed_facts.is_empty()) {
crate::dispatch_decode::require_positive_iterations(
"weir direct resident IFDS solve_cached with graph key into",
max_iterations,
)?;
super::resize_and_clear_result_slots(results, seed_sets.len())?;
return Ok(());
}
self.solver.solve_resident_prepared_many_into(
dispatch,
resident,
seed_sets,
max_iterations,
results,
)?;
self.record_resident_solve_batch(seed_sets)?;
return Ok(());
}
self.misses = self
.misses
.checked_add(1)
.ok_or_else(|| {
"weir direct resident IFDS miss counter overflowed u64. Fix: rebuild the direct resident batch facade before reuse."
.to_string()
})?;
let retained_bytes = sizing::direct_retained_graph_bytes(
graph_key.num_procs,
graph_key.blocks_per_proc,
graph_key.facts_per_proc,
intra_edges.len(),
inter_edges.len(),
flow_gen.len(),
)?;
if let Some(max_retained_bytes) = self
.max_retained_bytes
.filter(|max_retained_bytes| retained_bytes > *max_retained_bytes)
{
return Err(format!(
"weir direct resident IFDS CSR requires {retained_bytes} retained bytes but the configured device-memory budget allows {max_retained_bytes}. Fix: increase max_retained_bytes, shard the IFDS graph, or use a smaller fact/block domain before direct resident prepare."
));
}
self.evict_until_room(dispatch, retained_bytes)?;
self.reserve_cache_admission_slot()?;
let resident = prepare_ifds_csr_resident_direct_with_scratch_via(
dispatch,
graph_key.num_procs,
graph_key.blocks_per_proc,
graph_key.facts_per_proc,
intra_edges,
inter_edges,
flow_gen,
flow_kill,
&mut self.prepare_scratch,
&mut self.prepare_resource_scratch,
)?;
self.direct_prepares = self
.direct_prepares
.checked_add(1)
.ok_or_else(|| {
"weir direct resident IFDS prepare counter overflowed u64. Fix: rebuild the direct resident batch facade before reuse."
.to_string()
})?;
self.direct_prepare_bytes = self
.direct_prepare_bytes
.checked_add(
u64::try_from(retained_bytes)
.map_err(|_| {
"weir direct resident IFDS retained byte count exceeded u64. Fix: shard the IFDS graph before direct resident prepare."
.to_string()
})?,
)
.ok_or_else(|| {
"weir direct resident IFDS prepare byte counter overflowed u64. Fix: rebuild the direct resident batch facade before reuse."
.to_string()
})?;
self.record_cold_graph_prepare(retained_bytes)?;
self.entries.insert(
key.clone(),
DirectIfdsEntry {
retained_bytes,
last_seen: 0,
graph: resident,
},
);
self.retained_bytes = self
.retained_bytes
.checked_add(retained_bytes)
.ok_or_else(|| {
"weir direct resident IFDS retained byte accounting overflowed usize. Fix: reduce retained graph budget or rebuild the direct resident batch facade."
.to_string()
})?;
self.touch_key(&key)?;
let resident = &self
.entries
.get(&key)
.ok_or_else(|| {
"weir direct resident IFDS batch cache lookup failed after prepare".to_string()
})?
.graph;
if seed_sets.iter().all(|seed_facts| seed_facts.is_empty()) {
crate::dispatch_decode::require_positive_iterations(
"weir direct resident IFDS solve_with_graph_key_into after prepare",
max_iterations,
)?;
super::resize_and_clear_result_slots(results, seed_sets.len())?;
return Ok(());
}
self.solver.solve_resident_prepared_many_into(
dispatch,
resident,
seed_sets,
max_iterations,
results,
)?;
self.record_resident_solve_batch(seed_sets)
}
}