mod common;
use common::acquire_live_backend as live_backend;
use std::sync::{Mutex, MutexGuard};
use std::time::{Duration, Instant};
use vyre::ir::{BufferDecl, DataType, Expr, Node, Program};
use vyre::{DispatchConfig, VyreBackend};
use vyre_driver::CompiledPipeline;
static HOT_PATH_TEST_LOCK: Mutex<()> = Mutex::new(());
fn hot_path_test_guard() -> MutexGuard<'static, ()> {
HOT_PATH_TEST_LOCK.lock().unwrap_or_else(|error| {
panic!(
"dispatch hot-path test mutex was poisoned: {error}. Fix: resolve the earlier hot-path panic before trusting latency measurements."
)
})
}
fn add_one_program(words: u32) -> Program {
let idx = Expr::gid_x();
let in_bounds = Expr::lt(idx.clone(), Expr::u32(words));
Program::wrapped(
vec![
BufferDecl::read("input", 0, DataType::U32).with_count(words),
BufferDecl::output("out", 1, DataType::U32)
.with_count(words)
.with_output_byte_range(0..(words as usize * 4)),
],
[64, 1, 1],
vec![
Node::if_then(
in_bounds,
vec![Node::store(
"out",
idx.clone(),
Expr::add(Expr::load("input", idx), Expr::u32(1)),
)],
),
Node::return_(),
],
)
}
#[test]
fn pipeline_cache_hit_avoids_recompilation_latency() {
let _guard = hot_path_test_guard();
let backend = live_backend();
let program = add_one_program(1024);
let input: Vec<u8> = vyre_primitives::wire::pack_u32_iter(0..1024u32);
let cold_start = Instant::now();
let _ = backend
.dispatch(&program, &[input.clone()], &DispatchConfig::default())
.expect("Fix: cold dispatch must succeed");
let cold_elapsed = cold_start.elapsed();
let hot_start = Instant::now();
let _ = backend
.dispatch(&program, &[input.clone()], &DispatchConfig::default())
.expect("Fix: hot dispatch must succeed");
let hot_elapsed = hot_start.elapsed();
assert!(
hot_elapsed < cold_elapsed,
"Fix: pipeline cache hit must be faster than cold compile+dispatch. \
cold={cold_elapsed:?}, hot={hot_elapsed:?}"
);
let stats = backend.stats();
assert!(
stats.pipeline_cache_entries >= 1,
"Fix: after two dispatches of the same program the pipeline cache must contain at least one entry"
);
assert!(
stats.pipeline_cache_entries <= stats.pipeline_cache_capacity,
"Fix: pipeline cache must never exceed its declared capacity"
);
}
#[test]
fn bind_group_cache_reused_on_repeated_dispatches() {
let _guard = hot_path_test_guard();
let backend = live_backend();
let program = add_one_program(256);
let input: Vec<u8> = vyre_primitives::wire::pack_u32_iter(0..256u32);
let pipeline = backend
.compile_persistent(&program, &DispatchConfig::default())
.expect("Fix: compile_persistent must succeed");
let _ = pipeline
.dispatch(&[input.clone()], &DispatchConfig::default())
.expect("Fix: first compiled dispatch must succeed");
let stats_after_first = pipeline.bind_group_cache_stats();
assert_eq!(
stats_after_first.misses, 1,
"Fix: first dispatch of a compiled pipeline with new buffers must create exactly one bind group"
);
assert_eq!(
stats_after_first.hits, 0,
"Fix: no bind-group cache hit expected on first dispatch"
);
let _ = pipeline
.dispatch(&[input.clone()], &DispatchConfig::default())
.expect("Fix: second compiled dispatch must succeed");
let stats_after_second = pipeline.bind_group_cache_stats();
assert_eq!(
stats_after_second.hits, 1,
"Fix: second dispatch with identical inputs must hit the bind-group cache"
);
assert_eq!(
stats_after_second.misses, 1,
"Fix: bind-group cache misses must not increase on repeated identical dispatches"
);
}
#[test]
fn persistent_pool_reuses_allocations_on_repeated_dispatches() {
let _guard = hot_path_test_guard();
let backend = live_backend();
let program = add_one_program(256);
let input: Vec<u8> = vyre_primitives::wire::pack_u32_iter(0..256u32);
let pipeline = backend
.compile_persistent(&program, &DispatchConfig::default())
.expect("Fix: compile_persistent must succeed");
let stats_before = backend.stats().persistent_pool;
for i in 0..5 {
let _ = pipeline
.dispatch(&[input.clone()], &DispatchConfig::default())
.unwrap_or_else(|_| panic!("Fix: repeated compiled dispatch #{i} must succeed"));
}
let stats_after = backend.stats().persistent_pool;
assert!(
stats_after.hits > stats_before.hits,
"Fix: repeated dispatches through a compiled pipeline must show buffer-pool reuse. \
before_hits={}, after_hits={}",
stats_before.hits,
stats_after.hits
);
}
#[test]
fn compiled_dispatch_never_cpu_fallback() {
let _guard = hot_path_test_guard();
let backend = live_backend();
let program = add_one_program(1024);
let input: Vec<u8> = vyre_primitives::wire::pack_u32_iter(0..1024u32);
let pipeline = backend
.compile_persistent(&program, &DispatchConfig::default())
.expect("Fix: compile_persistent must succeed");
let start = Instant::now();
let outputs = pipeline
.dispatch(&[input], &DispatchConfig::default())
.expect("Fix: compiled dispatch must succeed");
let elapsed = start.elapsed();
assert!(
elapsed > Duration::from_micros(10),
"Fix: compiled dispatch returned in {elapsed:?}, which is too fast for a real GPU round-trip. \
This suggests a silent CPU fallback."
);
let expected: Vec<u8> = vyre_primitives::wire::pack_u32_iter(1..=1024u32);
assert_eq!(
outputs,
vec![expected],
"Fix: compiled pipeline must return correct GPU-computed results"
);
}
#[test]
fn dispatch_borrowed_avoids_async_pool_overhead() {
let _guard = hot_path_test_guard();
let backend = live_backend();
let program = add_one_program(1024);
let input: Vec<u8> = vyre_primitives::wire::pack_u32_iter(0..1024u32);
let borrowed = [input.as_slice()];
let _ = backend
.dispatch_borrowed(&program, &borrowed, &DispatchConfig::default())
.expect("Fix: warm-up borrowed dispatch must succeed");
let sync_start = Instant::now();
let _ = backend
.dispatch_borrowed(&program, &borrowed, &DispatchConfig::default())
.expect("Fix: sync borrowed dispatch must succeed");
let sync_elapsed = sync_start.elapsed();
let async_start = Instant::now();
let pending = backend
.dispatch_async(&program, &[input.clone()], &DispatchConfig::default())
.expect("Fix: dispatch_async must return a handle");
let _ = pending
.await_result()
.expect("Fix: async dispatch must resolve");
let async_elapsed = async_start.elapsed();
assert!(
sync_elapsed <= async_elapsed + Duration::from_millis(5),
"Fix: dispatch_borrowed (sync) took {sync_elapsed:?}, which is slower than \
dispatch_async+await at {async_elapsed:?}. The borrowed path must not pay \
async worker-pool overhead."
);
}
#[test]
fn hot_cached_dispatch_latency_bounded() {
let _guard = hot_path_test_guard();
let backend = live_backend();
let program = add_one_program(1024);
let input: Vec<u8> = vyre_primitives::wire::pack_u32_iter(0..1024u32);
let _ = backend
.dispatch(&program, &[input.clone()], &DispatchConfig::default())
.expect("Fix: warm-up dispatch must succeed");
const BUDGET: Duration = Duration::from_millis(200);
let start = Instant::now();
let _ = backend
.dispatch(&program, &[input.clone()], &DispatchConfig::default())
.expect("Fix: hot cached dispatch must succeed");
let elapsed = start.elapsed();
assert!(
elapsed < BUDGET,
"Fix: hot cached small dispatch exceeded latency budget {BUDGET:?}. \
Elapsed={elapsed:?}. Fix: inspect pipeline cache hit path and buffer-pool reuse."
);
}
#[test]
fn long_buffer_throughput_latency_bounded() {
let _guard = hot_path_test_guard();
let backend = live_backend();
let words = 1 << 20;
let program = add_one_program(words);
let input: Vec<u8> = vyre_primitives::wire::pack_u32_iter(0..words);
let _ = backend
.dispatch(&program, &[input.clone()], &DispatchConfig::default())
.expect("Fix: long-buffer warm-up dispatch must succeed");
const BUDGET: Duration = Duration::from_millis(1500);
let start = Instant::now();
let outputs = backend
.dispatch(&program, &[input], &DispatchConfig::default())
.expect("Fix: hot long-buffer dispatch must succeed");
let elapsed = start.elapsed();
assert!(
elapsed < BUDGET,
"Fix: hot long-buffer dispatch exceeded throughput budget {BUDGET:?}. \
Elapsed={elapsed:?}. Fix: inspect upload, dispatch, copy, and readback throughput."
);
assert_eq!(
outputs.first().map(Vec::len),
Some(words as usize * 4),
"Fix: long-buffer throughput test must read back the full output range"
);
}
#[test]
fn dispatch_batch_submit_overhead_bounded() {
let _guard = hot_path_test_guard();
let backend = live_backend();
let program = add_one_program(512);
let input: Vec<u8> = vyre_primitives::wire::pack_u32_iter(0..512u32);
let _ = backend
.dispatch(&program, &[input.clone()], &DispatchConfig::default())
.expect("Fix: warm-up dispatch must succeed");
let jobs = vec![
(
program.clone(),
vec![input.clone()],
DispatchConfig::default(),
),
(
program.clone(),
vec![input.clone()],
DispatchConfig::default(),
),
(
program.clone(),
vec![input.clone()],
DispatchConfig::default(),
),
];
const BUDGET: Duration = Duration::from_millis(300);
let start = Instant::now();
let results = backend
.dispatch_batch(&jobs)
.expect("Fix: dispatch_batch must launch all jobs");
let elapsed = start.elapsed();
assert!(
elapsed < BUDGET,
"Fix: three-job dispatch_batch exceeded latency budget {BUDGET:?}. \
Elapsed={elapsed:?}. Fix: inspect batch launch path for host-side serialization."
);
for (i, result) in results.iter().enumerate() {
let outputs = result
.as_ref()
.unwrap_or_else(|e| panic!("Fix: batch job #{i} must succeed: {e:?}"));
let expected: Vec<u8> = vyre_primitives::wire::pack_u32_iter(1..=512u32);
assert_eq!(
*outputs,
vec![expected],
"Fix: batch job #{i} produced wrong output"
);
}
}