int_mlicpp::decode and decode_traced = ops::decode(_traced); int_mlicpp::legacy (the direct decode) behind cfg(any(test, feature = "legacy_int")), and run_net_checked and to_rgb8 imported only there. int_payload::decode_int's WIM1 branch: the bundle parsed, ops::check, the three networks from the store of loaded sets (key: capability, wmb1/1, acc64, the SHA-256 of model.wmb's listing) or ops::load, ops::fit, ops::decode_loaded. int_ops::WMB1; Resident for int_mlicpp::ops::Nets. Tests: int_mlicpp_ops the_new_load_rules_affect_no_committed_set_or_payload (every committed bundle meets the rules and loads; every committed WIM1 payload, in any file, plans, decodes or is refused alike); int_payload_cost (now also legacy_int): with its networks loaded the decode holds no more than the direct decode; through the sink the decode that finds the bundle held holds less than the one that loads it, and keeps nothing more; int_set_cache: the vector is kept and hit, one byte apart never shares an entry, a refused bundle is never kept. Text: integer-payloads §14 (the decode through wmb1/1, step 1's checks, and the codes the decoder reports: payload_malformed for the payload, its streams and its values, model_mismatch for a bundle that breaks its rules or does not fit); integer-operators §4, the status note and §15.3 item 3; SPEC §8 planned breaks; CHANGELOG. Co-Authored-By: Claude Opus 5.5 <[email protected]>
455 lines
23 KiB
Rust
455 lines
23 KiB
Rust
//! The store of loaded parameter sets (`int_set_cache`) under the decodes that use it,
|
||
//! `int_payload::decode_int` and `declared_cost`, for every capability that decodes
|
||
//! against a set through the integer operator set.
|
||
//!
|
||
//! - A second decode through the same set is a hit, by the store's count, and gives the
|
||
//! same bytes as a decode with the store off, for every committed vector that decodes
|
||
//! against a set: `tiny`, `video_nk`, `audio` and `splat` of the int-payload corpus,
|
||
//! the int_synth corpus, and the `wai.neural.int_mlicpp` vector.
|
||
//! - Two sets whose members have the same names and whose bytes differ in one byte never
|
||
//! share an entry.
|
||
//! - A set the load refuses is refused again, with the same refusal, while a set one
|
||
//! byte away from it is held, and it is never kept: for int_synth, a set the bounds
|
||
//! pass refuses (load step 8); for the conditional codec and the MLIC++-topology codec,
|
||
//! a set whose lowering leaves a layer no admissible input range (integer-operators
|
||
//! §13.2).
|
||
//! - Every refusal fixture of the int-payload corpus keeps its code and its detail with
|
||
//! its set held, and a payload refused while it is parsed never reaches the store.
|
||
//! - Under a small budget the least recently used set goes first, a set past the budget
|
||
//! is not kept, and a budget of 0 neither keeps nor looks up.
|
||
//! - Threads decoding one set at once get the same bytes, and one entry is kept.
|
||
//!
|
||
//! The store is one per process, so each test takes a lock and starts from an empty
|
||
//! store with its counts at 0.
|
||
#![cfg(all(feature = "video_int", feature = "audio_int", feature = "splat_int"))]
|
||
|
||
use std::collections::BTreeMap;
|
||
use std::path::PathBuf;
|
||
use std::sync::{Mutex, MutexGuard};
|
||
|
||
use serde_json::Value;
|
||
|
||
use wai::codecs as c;
|
||
use wai::int_payload::{declared_cost, decode_int, resolve, IntDecoded};
|
||
use wai::int_set_cache::{LoadedSets, SetKey, Stats};
|
||
|
||
const DEFAULT_BUDGET: u64 = LoadedSets::DEFAULT_BUDGET;
|
||
use wai::pin::HeldBytes;
|
||
|
||
static SERIAL: Mutex<()> = Mutex::new(());
|
||
|
||
/// The store, to this test alone: empty, its counts at 0, its budget `budget`.
|
||
fn store(budget: u64) -> MutexGuard<'static, ()> {
|
||
let guard = SERIAL.lock().unwrap_or_else(|e| e.into_inner());
|
||
let s = LoadedSets::global();
|
||
s.set_budget(0);
|
||
s.set_budget(budget);
|
||
s.reset_stats();
|
||
guard
|
||
}
|
||
|
||
fn stats() -> Stats {
|
||
LoadedSets::global().stats()
|
||
}
|
||
|
||
fn wai_dir() -> PathBuf {
|
||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..")
|
||
}
|
||
fn read(rel: &str) -> Vec<u8> {
|
||
let p = wai_dir().join(rel);
|
||
std::fs::read(&p).unwrap_or_else(|e| panic!("read {}: {e}", p.display()))
|
||
}
|
||
fn index() -> Value {
|
||
serde_json::from_slice(&read("int-payload-conformance/vectors.json")).expect("the index parses")
|
||
}
|
||
|
||
/// A set: member name → bytes.
|
||
type Set = BTreeMap<String, Vec<u8>>;
|
||
|
||
fn set_of(entry: &Value) -> Set {
|
||
entry["set"].as_object().into_iter().flatten().map(|(k, v)| (k.clone(), read(v.as_str().unwrap()))).collect()
|
||
}
|
||
fn view(set: &Set) -> BTreeMap<String, &[u8]> {
|
||
set.iter().map(|(k, v)| (k.clone(), v.as_slice())).collect()
|
||
}
|
||
|
||
/// A decode's output as bytes, or its refusal's code and detail.
|
||
type Out = Result<Vec<u8>, (String, String)>;
|
||
|
||
fn bytes(d: IntDecoded) -> Vec<u8> {
|
||
match d {
|
||
IntDecoded::Image { rgb, .. } => rgb,
|
||
IntDecoded::Video { frames, .. } => frames.concat(),
|
||
IntDecoded::Pcm { samples, .. } => samples.iter().flat_map(|s| s.to_le_bytes()).collect(),
|
||
IntDecoded::Attr { bytes, .. } => bytes,
|
||
other => panic!("{other:?}"),
|
||
}
|
||
}
|
||
|
||
fn decode(cap: &str, payload: &[u8], set: &Set) -> Out {
|
||
let v = view(set);
|
||
let params = resolve(cap, None, &[HeldBytes::Set { listing: b"", files: &v }], None).expect("an unpinned set resolves");
|
||
decode_int(cap, payload, ¶ms).map(bytes).map_err(|e| (e.code().to_string(), e.detail().to_string()))
|
||
}
|
||
|
||
/// The same decode with the store off, then on again with the budget it had. A budget of
|
||
/// 0 empties the store, so a test takes what it needs with the store off before it fills it.
|
||
fn decode_off(cap: &str, payload: &[u8], set: &Set) -> Out {
|
||
let s = LoadedSets::global();
|
||
let budget = s.budget();
|
||
s.set_budget(0);
|
||
let out = decode(cap, payload, set);
|
||
s.set_budget(budget);
|
||
out
|
||
}
|
||
|
||
/// The capability constant `cap` names, and the lowering its driver lowers by.
|
||
fn registered(cap: &str) -> (&'static str, &'static str) {
|
||
match cap {
|
||
c::CAP_NEURAL_INT_HYPER => (c::CAP_NEURAL_INT_HYPER, wai::int_ops::WHM1),
|
||
c::CAP_VIDEO_INT_HYPER => (c::CAP_VIDEO_INT_HYPER, wai::int_ops::WHM1),
|
||
c::CAP_AUDIO_INT_CODEC => (c::CAP_AUDIO_INT_CODEC, wai::int_ops::WAM1),
|
||
c::CAP_SPLAT_INT_CODEC => (c::CAP_SPLAT_INT_CODEC, wai::int_ops::WHM1),
|
||
c::CAP_NEURAL_INT_SYNTH => (c::CAP_NEURAL_INT_SYNTH, wai::int_ops::INT_SYNTH),
|
||
c::CAP_NEURAL_INT_MLICPP => (c::CAP_NEURAL_INT_MLICPP, wai::int_ops::WMB1),
|
||
other => panic!("{other} decodes against no set"),
|
||
}
|
||
}
|
||
|
||
/// The key the decode of `cap` keeps `set` under.
|
||
fn key(cap: &str, set: &Set) -> SetKey {
|
||
let (cap, lowering) = registered(cap);
|
||
// Every lowering of integer-operators §13 declares acc64, which each graph loads under.
|
||
SetKey::of(cap, lowering, wai_ops::Profile::Acc64, &view(set)).expect("a set's names make a listing")
|
||
}
|
||
|
||
/// An `int_synth` payload (integer-payloads §13): `h`, `w`, `C` channels of side
|
||
/// `2^s_log2`, a reserved 0, then the latents.
|
||
fn synth_payload(h: u32, w: u32, ch: u16, s_log2: u8, latents: &[i8]) -> Vec<u8> {
|
||
let mut p = Vec::new();
|
||
p.extend_from_slice(&h.to_le_bytes());
|
||
p.extend_from_slice(&w.to_le_bytes());
|
||
p.extend_from_slice(&ch.to_le_bytes());
|
||
p.extend_from_slice(&[s_log2, 0]);
|
||
p.extend(latents.iter().map(|&v| v as u8));
|
||
p
|
||
}
|
||
fn synth(payload: &[u8], spec: &[u8]) -> Out {
|
||
decode(c::CAP_NEURAL_INT_SYNTH, payload, &synth_set(spec))
|
||
}
|
||
fn synth_off(payload: &[u8], spec: &[u8]) -> Out {
|
||
decode_off(c::CAP_NEURAL_INT_SYNTH, payload, &synth_set(spec))
|
||
}
|
||
fn synth_set(spec: &[u8]) -> Set {
|
||
BTreeMap::from([("int_synth.json".to_string(), spec.to_vec())])
|
||
}
|
||
|
||
/// The byte offsets of each convolution's multipliers and biases in a `WHM1` network
|
||
/// (integer-payloads §9), with its output channels: `(out, mult, bias)`.
|
||
fn whm1_rq(b: &[u8]) -> Vec<(usize, usize, usize)> {
|
||
let u = |p: usize| u32::from_le_bytes(b[p..p + 4].try_into().unwrap()) as usize;
|
||
let (mut p, mut out) = (20, Vec::new());
|
||
for _ in 0..u(16) {
|
||
let tag = b[p];
|
||
p += 1;
|
||
match tag {
|
||
0 | 1 => {
|
||
let (inc, o, k) = (u(p), u(p + 4), u(p + 8));
|
||
p += if tag == 0 { 24 } else { 20 } + 2 * inc * o * k * k;
|
||
out.push((o, p, p + 8 * o));
|
||
p += 16 * o;
|
||
}
|
||
2 => p += 4 + 4 * u(p) * u(p) + 8 * u(p),
|
||
3 => {}
|
||
4 => p += 12,
|
||
t => panic!("layer tag {t}"),
|
||
}
|
||
}
|
||
assert_eq!(p, b.len(), "the walk ends at the network's last byte");
|
||
out
|
||
}
|
||
|
||
/// The committed vectors that decode against a set, with their capability, payload and set.
|
||
fn vectors() -> Vec<(String, String, Vec<u8>, Set)> {
|
||
let mut v: Vec<_> = index()["vectors"].as_array().unwrap().iter().filter(|v| !set_of(v).is_empty()).map(|v| {
|
||
(v["name"].as_str().unwrap().to_string(), v["capability"].as_str().unwrap().to_string(), read(v["payload"].as_str().unwrap()), set_of(v))
|
||
}).collect();
|
||
let names: Vec<String> = v.iter().map(|x| x.0.clone()).collect();
|
||
assert_eq!(names, ["tiny", "video_nk", "audio", "splat"], "the corpus's vectors with a set");
|
||
let hashes: Value = serde_json::from_slice(&read("neural-conformance/expected_hashes.json")).unwrap();
|
||
for name in hashes.as_object().unwrap().keys() {
|
||
let dir = format!("neural-conformance/vectors/{name}");
|
||
v.push((format!("int_synth {name}"), c::CAP_NEURAL_INT_SYNTH.to_string(), read(&format!("{dir}/latent.bin")), synth_set(&read(&format!("{dir}/int_synth.json")))));
|
||
}
|
||
v.push(("int_mlicpp".to_string(), c::CAP_NEURAL_INT_MLICPP.to_string(), mlicpp_payload(), mlicpp_set(read(MLICPP_BUNDLE))));
|
||
v
|
||
}
|
||
|
||
/// The `wai.neural.int_mlicpp` vector: its bundle, and the payload of its envelope.
|
||
const MLICPP_BUNDLE: &str = "wai-rs/tests/vectors/int_mlicpp/model.wmb";
|
||
fn mlicpp_payload() -> Vec<u8> {
|
||
wai::container::Wai::from_bytes(&read("wai-rs/tests/vectors/int_mlicpp/stream.wai")).unwrap().payload
|
||
}
|
||
fn mlicpp_set(bundle: Vec<u8>) -> Set {
|
||
BTreeMap::from([("model.wmb".to_string(), bundle)])
|
||
}
|
||
|
||
/// The byte offset of a `WMB1` bundle's section `k` (0 `h_s`, 1 `entropy`, 2 `g_s`, 3 the
|
||
/// tables), past its length.
|
||
fn wmb1_section(b: &[u8], k: usize) -> usize {
|
||
let mut o = 4;
|
||
for _ in 0..k {
|
||
o += 4 + u32::from_le_bytes(b[o..o + 4].try_into().unwrap()) as usize;
|
||
}
|
||
o + 4
|
||
}
|
||
|
||
|
||
#[test]
|
||
fn a_second_decode_through_a_set_is_a_hit_and_gives_the_same_bytes() {
|
||
let _s = store(DEFAULT_BUDGET);
|
||
let vectors = vectors();
|
||
let offs: Vec<Vec<u8>> = vectors.iter().map(|(name, cap, payload, set)| decode_off(cap, payload, set).unwrap_or_else(|e| panic!("{name}: {e:?}"))).collect();
|
||
let mut n = 0;
|
||
for ((name, cap, payload, set), off) in vectors.into_iter().zip(offs) {
|
||
let s0 = stats();
|
||
let first = decode(&cap, &payload, &set);
|
||
let s1 = stats();
|
||
let second = decode(&cap, &payload, &set);
|
||
let s2 = stats();
|
||
assert_eq!(first.as_ref(), Ok(&off), "{name}: the decode that loads its set gives the bytes of the decode with the store off");
|
||
assert_eq!(second.as_ref(), Ok(&off), "{name}: the decode from the store gives the same bytes");
|
||
assert_eq!((s1.misses - s0.misses, s1.hits - s0.hits, s1.kept - s0.kept), (1, 0, 1), "{name}: the first decode loads its set and keeps it");
|
||
assert_eq!((s2.misses - s1.misses, s2.hits - s1.hits, s2.kept - s1.kept), (0, 1, 0), "{name}: the second is a hit");
|
||
assert!(LoadedSets::global().contains(&key(&cap, &set)), "{name}: kept under the SHA-256 of its members");
|
||
// The declared cost reads the same entry (int_synth and int_mlicpp have none).
|
||
if cap != c::CAP_NEURAL_INT_SYNTH && cap != c::CAP_NEURAL_INT_MLICPP {
|
||
let v = view(&set);
|
||
let params = resolve(&cap, None, &[HeldBytes::Set { listing: b"", files: &v }], None).unwrap();
|
||
declared_cost(&cap, &payload, ¶ms).unwrap();
|
||
assert_eq!((stats().hits - s2.hits, stats().misses - s2.misses), (1, 0), "{name}: the declared cost finds the set loaded");
|
||
}
|
||
println!("{name}: {} bytes alike with the store off, on (miss) and on (hit)", off.len());
|
||
n += 1;
|
||
}
|
||
assert_eq!(n, 9);
|
||
// tiny and video_nk read the same three members under two capabilities: two entries.
|
||
assert_eq!(stats().entries, 9);
|
||
}
|
||
|
||
#[test]
|
||
fn sets_one_byte_apart_never_share_an_entry() {
|
||
let _s = store(DEFAULT_BUDGET);
|
||
// The conditional codec: g_s's last bias, channel 0, its fourth byte flipped. The
|
||
// members keep their names.
|
||
let tiny = index()["vectors"].as_array().unwrap().iter().find(|v| v["name"] == "tiny").cloned().unwrap();
|
||
let (a, payload) = (set_of(&tiny), read(tiny["payload"].as_str().unwrap()));
|
||
let mut b = a.clone();
|
||
let g = b.get_mut("g_s.whm").unwrap();
|
||
let &(_, _, bias) = whm1_rq(g).last().unwrap();
|
||
g[bias + 3] ^= 0x01;
|
||
let cap = c::CAP_NEURAL_INT_HYPER;
|
||
// int_synth: the first digit of the last layer's first bias.
|
||
let base = read("neural-conformance/vectors/facade/int_synth.json");
|
||
let latent = read("neural-conformance/vectors/facade/latent.bin");
|
||
let at = base.windows(8).rposition(|w| w == b"\"bias\":[").unwrap() + 8;
|
||
let mut other = base.clone();
|
||
let digit = (at..other.len()).find(|&i| other[i].is_ascii_digit()).unwrap();
|
||
other[digit] = if other[digit] == b'9' { b'1' } else { b'9' };
|
||
// Each with the store off first.
|
||
let (a_off, b_off) = (decode_off(cap, &payload, &a).unwrap(), decode_off(cap, &payload, &b).unwrap());
|
||
let (x_off, y_off) = (synth_off(&latent, &base).unwrap(), synth_off(&latent, &other).unwrap());
|
||
assert_ne!(a_off, b_off, "the byte changes the image");
|
||
assert_ne!(x_off, y_off, "the digit changes the image");
|
||
|
||
assert_eq!(decode(cap, &payload, &a).unwrap(), a_off);
|
||
assert_eq!(decode(cap, &payload, &b).unwrap(), b_off, "the set one byte away is decoded with its own networks");
|
||
assert_eq!((stats().misses, stats().hits, stats().entries), (2, 0, 2), "each set is loaded and kept under its own key");
|
||
assert_eq!(decode(cap, &payload, &b).unwrap(), b_off);
|
||
assert_eq!(decode(cap, &payload, &a).unwrap(), a_off);
|
||
assert_eq!((stats().misses, stats().hits), (2, 2));
|
||
assert!(key(cap, &a) != key(cap, &b) && LoadedSets::global().contains(&key(cap, &a)) && LoadedSets::global().contains(&key(cap, &b)));
|
||
|
||
assert_eq!(synth(&latent, &base).unwrap(), x_off);
|
||
assert_eq!(synth(&latent, &other).unwrap(), y_off, "the set one byte away is decoded with its own graph");
|
||
assert_eq!(synth(&latent, &other).unwrap(), y_off);
|
||
assert_eq!((stats().misses, stats().hits, stats().entries), (4, 3, 4));
|
||
let (kx, ky) = (key(c::CAP_NEURAL_INT_SYNTH, &synth_set(&base)), key(c::CAP_NEURAL_INT_SYNTH, &synth_set(&other)));
|
||
assert!(kx != ky && LoadedSets::global().contains(&kx) && LoadedSets::global().contains(&ky));
|
||
|
||
// int_mlicpp: g_s's last bias, channel 0, its fourth byte flipped, inside the bundle.
|
||
let (ma, mp) = (mlicpp_set(read(MLICPP_BUNDLE)), mlicpp_payload());
|
||
let mut mb = ma.clone();
|
||
let w = mb.get_mut("model.wmb").unwrap();
|
||
let at = wmb1_section(w, 2);
|
||
let end = wmb1_section(w, 3) - 4;
|
||
let &(_, _, bias) = whm1_rq(&w[at..end]).last().unwrap();
|
||
w[at + bias + 3] ^= 0x01;
|
||
let mc = c::CAP_NEURAL_INT_MLICPP;
|
||
let (ma_off, mb_off) = (decode_off(mc, &mp, &ma).unwrap(), decode_off(mc, &mp, &mb).unwrap());
|
||
assert_ne!(ma_off, mb_off, "the byte changes the image");
|
||
let s0 = stats();
|
||
assert_eq!(decode(mc, &mp, &ma).unwrap(), ma_off);
|
||
assert_eq!(decode(mc, &mp, &mb).unwrap(), mb_off, "the bundle one byte away is decoded with its own networks");
|
||
assert_eq!(decode(mc, &mp, &mb).unwrap(), mb_off);
|
||
assert_eq!((stats().misses - s0.misses, stats().hits - s0.hits), (2, 1));
|
||
let (ka, kb) = (key(mc, &ma), key(mc, &mb));
|
||
assert!(ka != kb && LoadedSets::global().contains(&ka) && LoadedSets::global().contains(&kb));
|
||
}
|
||
|
||
#[test]
|
||
fn a_set_the_load_refuses_stays_refused_beside_a_held_neighbour() {
|
||
let _s = store(DEFAULT_BUDGET);
|
||
// int_synth: the falsification set that passes i64 by one unit (integer-operators
|
||
// §14.3), refused by the bounds pass, and its neighbour with a bias of 0 in place of
|
||
// 1, which loads.
|
||
let overflow = read("ops-conformance/sets/overflow_acc64/int_synth.json");
|
||
const BIAS: &[u8] = b"\"bias\":[1,0,0]";
|
||
let at = overflow.windows(BIAS.len()).position(|w| w == BIAS).expect("the overflow set's bias") + 8;
|
||
let mut fits = overflow.clone();
|
||
fits[at] = b'0';
|
||
assert_eq!(overflow.iter().zip(&fits).filter(|(x, y)| x != y).count(), 1, "one byte apart");
|
||
let p = synth_payload(1, 1, 1, 0, &[1]);
|
||
// The conditional codec: tiny with layer 0's first bias of g_s at 2^63 − 1, so no
|
||
// input range admits that layer (P_0(0) fails, integer-operators §13.2) and g_s is
|
||
// refused as it is lowered, after the plan; tiny itself is held.
|
||
let tiny = index()["vectors"].as_array().unwrap().iter().find(|v| v["name"] == "tiny").cloned().unwrap();
|
||
let (good, payload) = (set_of(&tiny), read(tiny["payload"].as_str().unwrap()));
|
||
let mut bad = good.clone();
|
||
let g = bad.get_mut("g_s.whm").unwrap();
|
||
let (_, _, bias) = whm1_rq(g)[0];
|
||
g[bias..bias + 8].copy_from_slice(&i64::MAX.to_le_bytes());
|
||
let cap = c::CAP_NEURAL_INT_HYPER;
|
||
// Each refusal with the store off first.
|
||
let synth_refused = synth_off(&p, &overflow).expect_err("the overflow set is refused");
|
||
assert!(synth_refused.1.contains("set does not load: model_mismatch at Load(8)"), "{synth_refused:?}");
|
||
let hyper_refused = decode_off(cap, &payload, &bad).expect_err("tiny with that bias is refused");
|
||
assert_eq!(hyper_refused, ("model_mismatch".to_string(), "g_s does not lower to an integer-operator graph that loads under acc64".to_string()));
|
||
|
||
assert!(synth(&p, &fits).is_ok(), "the neighbour loads and decodes");
|
||
let held = stats();
|
||
for _ in 0..2 {
|
||
assert_eq!(synth(&p, &overflow), Err(synth_refused.clone()), "refused again, with the same refusal");
|
||
}
|
||
let after = stats();
|
||
assert_eq!((after.misses - held.misses, after.hits - held.hits, after.kept - held.kept), (2, 0, 0), "loaded each time, never kept");
|
||
assert!(!LoadedSets::global().contains(&key(c::CAP_NEURAL_INT_SYNTH, &synth_set(&overflow))));
|
||
|
||
decode(cap, &payload, &good).expect("tiny decodes, and is kept");
|
||
let held = stats();
|
||
for _ in 0..2 {
|
||
assert_eq!(decode(cap, &payload, &bad), Err(hyper_refused.clone()), "refused again, with the same refusal");
|
||
}
|
||
let after = stats();
|
||
assert_eq!((after.misses - held.misses, after.hits - held.hits, after.kept - held.kept), (2, 0, 0), "loaded each time, never kept");
|
||
assert!(!LoadedSets::global().contains(&key(cap, &bad)) && LoadedSets::global().contains(&key(cap, &good)));
|
||
|
||
// int_mlicpp: the vector's bundle with g_s's layer-0 first bias at 2^63 − 1, refused as
|
||
// it is lowered, after the header and the networks' geometry; the vector's own bundle
|
||
// is held.
|
||
let (good, payload) = (mlicpp_set(read(MLICPP_BUNDLE)), mlicpp_payload());
|
||
let mut bad = good.clone();
|
||
let w = bad.get_mut("model.wmb").unwrap();
|
||
let at = wmb1_section(w, 2);
|
||
let end = wmb1_section(w, 3) - 4;
|
||
let (_, _, bias) = whm1_rq(&w[at..end])[0];
|
||
w[at + bias..at + bias + 8].copy_from_slice(&i64::MAX.to_le_bytes());
|
||
let cap = c::CAP_NEURAL_INT_MLICPP;
|
||
let mlic_refused = decode_off(cap, &payload, &bad).expect_err("the bundle with that bias is refused");
|
||
assert_eq!(mlic_refused.0, "model_mismatch");
|
||
assert!(mlic_refused.1.starts_with("g_s does not lower to an integer-operator graph that loads under acc64"), "{mlic_refused:?}");
|
||
decode(cap, &payload, &good).expect("the vector decodes, and is kept");
|
||
let held = stats();
|
||
for _ in 0..2 {
|
||
assert_eq!(decode(cap, &payload, &bad), Err(mlic_refused.clone()), "refused again, with the same refusal");
|
||
}
|
||
let after = stats();
|
||
assert_eq!((after.misses - held.misses, after.hits - held.hits, after.kept - held.kept), (2, 0, 0), "loaded each time, never kept");
|
||
assert!(!LoadedSets::global().contains(&key(cap, &bad)) && LoadedSets::global().contains(&key(cap, &good)));
|
||
}
|
||
|
||
#[test]
|
||
fn every_refusal_fixture_keeps_its_code_and_detail_with_its_set_held() {
|
||
let _s = store(DEFAULT_BUDGET);
|
||
let idx = index();
|
||
let vectors = idx["vectors"].as_array().unwrap();
|
||
let mut n = 0;
|
||
for f in idx["refusals"].as_array().unwrap() {
|
||
let set = set_of(f);
|
||
if set.is_empty() {
|
||
continue;
|
||
}
|
||
let (name, cap) = (f["name"].as_str().unwrap(), f["capability"].as_str().unwrap());
|
||
let payload = read(f["payload"].as_str().unwrap());
|
||
let off = decode_off(cap, &payload, &set);
|
||
assert_eq!(off.as_ref().map_err(|e| e.0.as_str()).map(|_| ()), Err(f["code"].as_str().unwrap()), "{name}: the store off");
|
||
// Twice from an empty store: the first may load and keep the set (a refusal after
|
||
// the load step), the second then finds it held.
|
||
LoadedSets::global().clear();
|
||
let s0 = stats();
|
||
assert_eq!(decode(cap, &payload, &set), off, "{name}: from an empty store");
|
||
assert_eq!(decode(cap, &payload, &set), off, "{name}: again");
|
||
if f["layer"] == "parse" {
|
||
assert_eq!(stats(), s0, "{name}: a payload refused while it is parsed never reaches the store");
|
||
}
|
||
// With the set held by a committed vector's decode, where one decodes against it.
|
||
if let Some(v) = vectors.iter().find(|v| v["capability"] == cap && set_of(v) == set) {
|
||
decode(cap, &read(v["payload"].as_str().unwrap()), &set).expect("the committed vector decodes");
|
||
assert!(LoadedSets::global().contains(&key(cap, &set)));
|
||
assert_eq!(decode(cap, &payload, &set), off, "{name}: with its set held");
|
||
}
|
||
n += 1;
|
||
}
|
||
assert!(n >= 40, "{n} fixtures with a set");
|
||
}
|
||
|
||
#[test]
|
||
fn the_least_recently_used_set_goes_first_under_a_small_budget() {
|
||
let _s = store(DEFAULT_BUDGET);
|
||
// Three sets of one shape, so of one size.
|
||
let spec = |w: u8| format!(r#"{{"layers":[{{"in_ch":1,"out_ch":3,"k":1,"stride":1,"pad":0,"weight":[1,2,{w}],"bias":[0,0,0]}}]}}"#).into_bytes();
|
||
let (s1, s2, s3) = (spec(3), spec(4), spec(5));
|
||
let k = |s: &[u8]| key(c::CAP_NEURAL_INT_SYNTH, &synth_set(s));
|
||
let p = synth_payload(1, 1, 1, 0, &[5]);
|
||
let has = |s: &[u8]| LoadedSets::global().contains(&k(s));
|
||
assert_eq!(synth(&p, &s1).unwrap(), [5, 10, 15]);
|
||
assert_eq!(synth(&p, &s2).unwrap(), [5, 10, 20]);
|
||
let two = stats().resident_bytes;
|
||
assert_eq!((stats().entries, two % 2), (2, 0));
|
||
let one = two / 2;
|
||
// Room for two: s1 is used again, so s2 is the least recently used when s3 comes.
|
||
LoadedSets::global().set_budget(two);
|
||
assert_eq!(synth(&p, &s1).unwrap(), [5, 10, 15]);
|
||
assert_eq!(synth(&p, &s3).unwrap(), [5, 10, 25]);
|
||
assert!(has(&s1) && has(&s3) && !has(&s2), "s2 went first");
|
||
assert_eq!((stats().evicted, stats().entries, stats().resident_bytes), (1, 2, two));
|
||
assert_eq!(synth(&p, &s2).unwrap(), [5, 10, 20], "an evicted set is loaded again");
|
||
assert!(has(&s2) && has(&s3) && !has(&s1));
|
||
// A budget under one entry: everything goes, and a set past it is decoded, not kept.
|
||
LoadedSets::global().set_budget(one - 1);
|
||
assert_eq!(stats().entries, 0);
|
||
let before = stats();
|
||
assert_eq!(synth(&p, &s1).unwrap(), [5, 10, 15]);
|
||
assert_eq!((stats().too_large - before.too_large, stats().entries), (1, 0));
|
||
// A budget of 0: no lookup and nothing kept.
|
||
LoadedSets::global().set_budget(0);
|
||
let before = stats();
|
||
assert_eq!(synth(&p, &s1).unwrap(), [5, 10, 15]);
|
||
assert_eq!((stats().hits, stats().misses, stats().entries), (before.hits, before.misses, 0));
|
||
}
|
||
|
||
#[test]
|
||
fn threads_decoding_one_set_at_once_keep_one_entry() {
|
||
let _s = store(DEFAULT_BUDGET);
|
||
let tiny = index()["vectors"].as_array().unwrap().iter().find(|v| v["name"] == "tiny").cloned().unwrap();
|
||
let (set, payload) = (set_of(&tiny), read(tiny["payload"].as_str().unwrap()));
|
||
let cap = c::CAP_NEURAL_INT_HYPER;
|
||
let off = decode_off(cap, &payload, &set).unwrap();
|
||
let outs: Vec<Out> = std::thread::scope(|sc| {
|
||
let hs: Vec<_> = (0..8).map(|_| sc.spawn(|| decode(cap, &payload, &set))).collect();
|
||
hs.into_iter().map(|h| h.join().unwrap()).collect()
|
||
});
|
||
assert!(outs.iter().all(|o| o.as_ref() == Ok(&off)), "every thread decodes the same bytes");
|
||
let s = stats();
|
||
assert_eq!((s.hits + s.misses, s.entries), (8, 1));
|
||
}
|