use std::sync::Arc;
use fuel_backend_contract::backend::BackendRuntime;
use fuel_ir::DeviceLocation;
use fuel_ir::backend::FitStatus;
use fuel_ir::probe::BackendId;
use super::{AlternativeSet, Candidate, RuntimeSelector};
pub type BackendRuntimeHandle = Box<dyn BackendRuntime + Send + Sync>;
pub type BackendRuntimeLookup =
Arc<dyn Fn(BackendId, DeviceLocation) -> Option<BackendRuntimeHandle> + Send + Sync>;
pub type OutputBytesEstimator = Arc<dyn Fn(&Candidate) -> u64 + Send + Sync>;
pub fn default_estimate_output_bytes(c: &Candidate) -> u64 {
c.static_cost.bytes_moved
}
#[derive(Clone)]
pub struct VramPressureSelector {
backend_runtime_lookup: BackendRuntimeLookup,
estimate_output_bytes: OutputBytesEstimator,
}
impl VramPressureSelector {
pub fn new(
backend_runtime_lookup: BackendRuntimeLookup,
estimate_output_bytes: OutputBytesEstimator,
) -> Self {
Self {
backend_runtime_lookup,
estimate_output_bytes,
}
}
pub fn with_default_estimator(backend_runtime_lookup: BackendRuntimeLookup) -> Self {
Self::new(
backend_runtime_lookup,
Arc::new(default_estimate_output_bytes),
)
}
fn fit_status_for(&self, c: &Candidate) -> FitStatus {
let size = (self.estimate_output_bytes)(c);
match (self.backend_runtime_lookup)(c.backend, c.device) {
Some(runtime) => runtime.would_fit(size),
None => FitStatus::Unknown,
}
}
}
impl std::fmt::Debug for VramPressureSelector {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VramPressureSelector")
.field("backend_runtime_lookup", &"<closure>")
.field("estimate_output_bytes", &"<closure>")
.finish()
}
}
impl RuntimeSelector for VramPressureSelector {
fn select<'a>(&self, set: &'a AlternativeSet) -> Option<&'a Candidate> {
let alts = set.alternatives();
if alts.is_empty() {
return None;
}
let mut best_idx: Option<usize> = None;
let mut best_status: Option<FitStatus> = None;
fn score_of(s: FitStatus) -> u8 {
match s {
FitStatus::Comfortable | FitStatus::Unknown => 0,
FitStatus::Tight => 1,
FitStatus::WontFit => unreachable!("filtered above"),
}
}
for (i, c) in alts.iter().enumerate() {
let status = self.fit_status_for(c);
if matches!(status, FitStatus::WontFit) {
continue;
}
let score = score_of(status);
let prev_score = best_status.map(score_of);
match prev_score {
None => {
best_idx = Some(i);
best_status = Some(status);
}
Some(prev) if score < prev => {
best_idx = Some(i);
best_status = Some(status);
}
_ => {}
}
}
match best_idx {
Some(i) => alts.get(i),
None => set.winner(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::fused::{CostEstimate, PrecisionGuarantee};
use crate::kernel::{KernelCaps, OpParams};
use fuel_backend_contract::backend::BackendRuntime;
use fuel_ir::probe::BackendId;
use fuel_ir::{DeviceLocation, Layout, Result};
use fuel_memory::Storage;
use std::sync::{Arc, RwLock};
fn noop_kernel(
_i: &[Arc<RwLock<Storage>>],
_o: &mut [Arc<RwLock<Storage>>],
_l: &[Layout],
_p: &OpParams,
) -> Result<()> {
Ok(())
}
fn make_candidate(backend: BackendId, device: DeviceLocation, bytes_moved: u64) -> Candidate {
Candidate {
kernel: noop_kernel,
caps: KernelCaps::empty(),
backend,
device,
precision: PrecisionGuarantee::PRIMITIVE_DETERMINISTIC_CPU,
static_cost: CostEstimate {
flops: bytes_moved,
bytes_moved,
kernel_overhead_ns: 0,
},
inbound_transfer_ns: 0,
op_params: OpParams::None,
coupling: Vec::new(),
kernel_source: "",
kernel_revision_hash: 0,
}
}
struct MockRuntime {
available: Option<u64>,
total: Option<u64>,
}
impl BackendRuntime for MockRuntime {
fn available_bytes(&self) -> Option<u64> {
self.available
}
fn total_bytes(&self) -> Option<u64> {
self.total
}
}
fn single_backend_lookup(
target: BackendId,
available: Option<u64>,
total: Option<u64>,
) -> BackendRuntimeLookup {
Arc::new(move |b, _d| {
if b == target {
Some(Box::new(MockRuntime { available, total })
as Box<dyn BackendRuntime + Send + Sync>)
} else {
None
}
})
}
fn per_backend_lookup(
cuda: (Option<u64>, Option<u64>),
cpu: (Option<u64>, Option<u64>),
) -> BackendRuntimeLookup {
Arc::new(move |b, _d| match b {
BackendId::Cuda => Some(Box::new(MockRuntime {
available: cuda.0,
total: cuda.1,
}) as Box<dyn BackendRuntime + Send + Sync>),
BackendId::Cpu => Some(Box::new(MockRuntime {
available: cpu.0,
total: cpu.1,
}) as Box<dyn BackendRuntime + Send + Sync>),
_ => None,
})
}
fn make_selector(lookup: BackendRuntimeLookup) -> VramPressureSelector {
VramPressureSelector::with_default_estimator(lookup)
}
#[test]
fn all_comfortable_picks_winner() {
let mut set = AlternativeSet::empty();
set.push(make_candidate(
BackendId::Cuda,
DeviceLocation::Cuda { gpu_id: 0 },
100,
));
set.push(make_candidate(BackendId::Cpu, DeviceLocation::Cpu, 200));
let lookup = per_backend_lookup(
(Some(1_000_000), Some(2_000_000)),
(Some(1_000_000), Some(2_000_000)),
);
let sel = make_selector(lookup);
let pick = sel.select(&set).expect("non-empty");
assert_eq!(
pick.backend,
BackendId::Cuda,
"winner is the cost-ranked first"
);
}
#[test]
fn wont_fit_skips_candidate() {
let mut set = AlternativeSet::empty();
set.push(make_candidate(
BackendId::Cuda,
DeviceLocation::Cuda { gpu_id: 0 },
1_000,
));
set.push(make_candidate(BackendId::Cpu, DeviceLocation::Cpu, 1_000));
let lookup = per_backend_lookup((Some(500), Some(10_000)), (Some(8_000), Some(10_000)));
let sel = make_selector(lookup);
let pick = sel.select(&set).expect("non-empty");
assert_eq!(
pick.backend,
BackendId::Cpu,
"CUDA skipped due to WontFit; CPU is next admissible",
);
}
#[test]
fn tight_demoted_below_comfortable() {
let mut set = AlternativeSet::empty();
set.push(make_candidate(
BackendId::Cuda,
DeviceLocation::Cuda { gpu_id: 0 },
100,
));
set.push(make_candidate(BackendId::Cpu, DeviceLocation::Cpu, 100));
let lookup = per_backend_lookup((Some(100), Some(100)), (Some(10_000), Some(10_000)));
let sel = make_selector(lookup);
let pick = sel.select(&set).expect("non-empty");
assert_eq!(
pick.backend,
BackendId::Cpu,
"CUDA demoted (Tight) to favor Comfortable CPU even though CUDA was cost-winner",
);
}
#[test]
fn all_wont_fit_falls_back_to_winner() {
let mut set = AlternativeSet::empty();
set.push(make_candidate(
BackendId::Cuda,
DeviceLocation::Cuda { gpu_id: 0 },
1_000,
));
set.push(make_candidate(BackendId::Cpu, DeviceLocation::Cpu, 1_000));
let lookup = per_backend_lookup((Some(1), Some(10_000)), (Some(1), Some(10_000)));
let sel = make_selector(lookup);
let pick = sel.select(&set).expect("non-empty (falls back to winner)");
assert_eq!(
pick.backend,
BackendId::Cuda,
"all WontFit → fall back to static winner",
);
}
#[test]
fn unknown_ties_with_comfortable_preserves_winner() {
let mut set = AlternativeSet::empty();
set.push(make_candidate(
BackendId::Cuda,
DeviceLocation::Cuda { gpu_id: 0 },
100,
));
set.push(make_candidate(BackendId::Cpu, DeviceLocation::Cpu, 100));
let lookup = single_backend_lookup(BackendId::Cpu, Some(10_000), Some(10_000));
let sel = make_selector(lookup);
let pick = sel.select(&set).expect("non-empty");
assert_eq!(
pick.backend,
BackendId::Cuda,
"Unknown ties with Comfortable; cost-rank winner preserved",
);
}
#[test]
fn empty_set_returns_none() {
let set = AlternativeSet::empty();
let lookup: BackendRuntimeLookup = Arc::new(|_, _| None);
let sel = make_selector(lookup);
assert!(sel.select(&set).is_none());
}
#[test]
fn custom_estimator_changes_fit_outcome() {
let mut set = AlternativeSet::empty();
set.push(make_candidate(
BackendId::Cuda,
DeviceLocation::Cuda { gpu_id: 0 },
100,
));
set.push(make_candidate(BackendId::Cpu, DeviceLocation::Cpu, 100));
let lookup = per_backend_lookup((Some(50), Some(1_000)), (Some(50), Some(1_000)));
let custom: OutputBytesEstimator = Arc::new(|_c: &Candidate| 10u64);
let sel = VramPressureSelector::new(lookup.clone(), custom);
let pick = sel.select(&set).expect("non-empty");
assert_eq!(
pick.backend,
BackendId::Cuda,
"custom estimator returned size that fits → cost-winner stays",
);
let sel_default = VramPressureSelector::with_default_estimator(lookup);
let pick = sel_default.select(&set).expect("non-empty");
assert_eq!(
pick.backend,
BackendId::Cuda,
"all WontFit → static winner fallback",
);
}
#[test]
fn debug_does_not_panic() {
let lookup: BackendRuntimeLookup = Arc::new(|_, _| None);
let sel = VramPressureSelector::with_default_estimator(lookup);
let s = format!("{sel:?}");
assert!(s.contains("VramPressureSelector"));
}
}