use memra_engine::Engine;
fn host_tables(
sel: &[i32],
selw: &[f32],
(pg, pu, pd): (u64, u64, u64),
(sg, su, sd): (usize, usize, usize),
macros: Option<(&[f32], &[f32], &[f32])>,
) -> (Vec<u64>, Vec<f32>) {
let n_pairs = sel.len();
let mut ptrs = vec![0u64; 3 * n_pairs];
let mut scl = vec![0f32; 3 * n_pairs];
for (p, (&ex, &w)) in sel.iter().zip(selw).enumerate() {
let ex = ex as usize;
ptrs[p] = pg + (ex * sg) as u64;
ptrs[n_pairs + p] = pu + (ex * su) as u64;
ptrs[2 * n_pairs + p] = pd + (ex * sd) as u64;
let (mg, mu, md) = match macros {
Some((g, u, d)) => (g[ex], u[ex], d[ex]),
None => (1.0, 1.0, 1.0),
};
scl[p] = mg;
scl[n_pairs + p] = mu;
scl[2 * n_pairs + p] = w * md;
}
(ptrs, scl)
}
#[allow(clippy::too_many_arguments)] fn check_case(
e: &Engine,
label: &str,
n_expert: usize,
sel: &[i32],
selw: &[f32],
bases: (u64, u64, u64),
strides: (usize, usize, usize),
with_macros: bool,
) -> Result<(), Box<dyn std::error::Error>> {
let n_pairs = sel.len();
let mg: Vec<f32> = (0..n_expert).map(|i| 1.0 + i as f32 * 1e-3).collect();
let mu: Vec<f32> = (0..n_expert).map(|i| 2.0 + i as f32 * 1e-3).collect();
let md: Vec<f32> = (0..n_expert).map(|i| 3.0 + i as f32 * 1e-3).collect();
let macros = with_macros.then_some((mg.as_slice(), mu.as_slice(), md.as_slice()));
let (want_ptrs, want_scl) = host_tables(sel, selw, bases, strides, macros);
let sel_d = e.htod_i32(sel)?;
let selw_d = e.htod(selw)?;
let mut ptrs_d = e.htod_u64(&vec![0u64; 3 * n_pairs])?;
let mut scl_d = e.htod(&vec![0f32; 3 * n_pairs])?;
let il = if with_macros { 7u16 } else { 9u16 };
e.moe_vrows_tables_from_sel(
&sel_d,
&selw_d,
il,
macros,
bases,
strides,
n_pairs,
&mut ptrs_d,
&mut scl_d,
)?;
let got_ptrs = e.dtoh_u64(&ptrs_d)?;
let got_scl = e.dtoh(&scl_d)?;
let mut bad = 0usize;
for i in 0..3 * n_pairs {
if got_ptrs[i] != want_ptrs[i] {
if bad < 6 {
let plane = ["gate", "up", "down"][i / n_pairs];
let p = i % n_pairs;
println!(
" [{label}] PTR MISMATCH plane={plane} pair={p} expert={} host=0x{:x} dev=0x{:x} (delta={})",
sel[p],
want_ptrs[i],
got_ptrs[i],
got_ptrs[i].wrapping_sub(want_ptrs[i]) as i64
);
}
bad += 1;
}
if got_scl[i].to_bits() != want_scl[i].to_bits() {
if bad < 6 {
let plane = ["gate", "up", "down"][i / n_pairs];
println!(
" [{label}] SCL MISMATCH plane={plane} pair={} host={:e} dev={:e}",
i % n_pairs,
want_scl[i],
got_scl[i]
);
}
bad += 1;
}
}
if bad > 0 {
return Err(format!("{label}: {bad}/{} table entries differ", 6 * n_pairs).into());
}
println!(
" [{label}] OK ({} pointers + {} scales identical)",
3 * n_pairs,
3 * n_pairs
);
Ok(())
}
#[test]
#[ignore = "needs a CUDA device — run under flock /tmp/memra-5090.lock"]
fn dev_built_vrows_tables_match_the_host_arithmetic() -> Result<(), Box<dyn std::error::Error>> {
let e = Engine::new(0)?;
println!("[vrows-dev-tables] GPU0: {}", e.ctx().name()?);
let n_expert = 256usize;
let sel: Vec<i32> = vec![0, 1, 17, 63, 128, 200, 254, 255];
let selw: Vec<f32> = vec![0.5, 0.25, 0.125, 1.0, 0.0625, 0.75, 0.375, 0.875];
let bases = (
0x0000_7f00_0000_0000u64,
0x0000_7f40_0000_0000u64,
0x0000_7f80_0000_0000u64,
);
check_case(
&e,
"small-strides",
n_expert,
&sel,
&selw,
bases,
(860_160, 860_160, 1_114_112),
true,
)?;
let big = (24 << 20, 24 << 20, 48 << 20);
check_case(
&e,
"serving-strides-past-4GiB",
n_expert,
&sel,
&selw,
bases,
big,
true,
)?;
check_case(&e, "no-macros", n_expert, &sel, &selw, bases, big, false)?;
let sel_all: Vec<i32> = (0..8i32).map(|j| j * 32 + 31).collect();
let selw_all: Vec<f32> = (0..8).map(|j| 1.0 / (j as f32 + 3.0)).collect();
check_case(
&e,
"spread-selection",
n_expert,
&sel_all,
&selw_all,
bases,
big,
true,
)?;
Ok(())
}