makepad/libs/relax/compiler/std/check/tests/coll_hasher.rs
Admin b22a714840 makepad run: the Rust JIT (libs/relax) and makepad-remote
The JIT that runs makepad apps from source: lexer, front end, type checker,
lazy borrow checker (Gandalf; `--check-first` opts in), codegen, session and
warm/cold caches keyed by compiler stamp, provided crates bound late,
startup images, live patching. User-facing name is `makepad run` (binary
`makepad`, MAKEPAD_RUN_* env, ~/.makepad-run); libs/relax stays the internal
library name. `makepad check` reports rustc-style diagnostics with rustc
error codes and checks a whole workspace (`--workspace`). Runs on macOS,
Linux, Windows and Android. makepad-remote: one tool schema for apps and the
WM (app/1): grabs, captures, traces, frame profiles and tweaks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 12:26:21 +02:00

75 lines
2.9 KiB
Rust

//! Default hasher sanity (distribution, determinism) and a rough speed comparison with std.
mod coll_common;
use relax_std_check::collections::{BTreeMap, HashMap};
use relax_std_check::hash::{BuildHasher, DefaultHasher, Hasher, RandomState};
use std::time::Instant;
#[test]
fn distribution() {
let s = RandomState::new();
// sequential, strided and high-bit-only keys must spread over low bits (bucket index)
// and the top 7 bits (control byte)
for (name, step) in [("seq", 1u64), ("stride4096", 4096), ("high", 1u64 << 40)] {
let mut low = [0u32; 1024];
let mut top = [0u32; 128];
for i in 0..1u64 << 16 {
let h = s.hash_one(i.wrapping_mul(step));
low[(h & 1023) as usize] += 1;
top[(h >> 57) as usize] += 1;
}
let (lmin, lmax) = (*low.iter().min().unwrap(), *low.iter().max().unwrap());
let (tmin, tmax) = (*top.iter().min().unwrap(), *top.iter().max().unwrap());
// expected 64 per low bucket, 512 per tag
assert!(lmin > 25 && lmax < 120, "{name}: low bits {lmin}..{lmax}");
assert!(tmin > 380 && tmax < 650, "{name}: tag bits {tmin}..{tmax}");
}
let mut a = DefaultHasher::new();
a.write(b"hello world, this is a longer string");
let mut b = DefaultHasher::new();
b.write(b"hello world, this is a longer strinG");
assert_ne!(a.finish(), b.finish());
let mut c = DefaultHasher::new();
c.write(b"hello world, this is a longer string");
assert_eq!(a.finish(), c.finish());
assert_ne!(s.hash_one("ab"), s.hash_one("ba"));
assert_ne!(s.hash_one(("a", "bc")), s.hash_one(("ab", "c")));
}
#[test]
fn speed() {
let n = 1_000_000u64;
let t = Instant::now();
let mut a: HashMap<u64, u64> = HashMap::new();
for i in 0..n {
a.insert(i.wrapping_mul(0x9e37_79b9), i);
}
let mut s = 0u64;
for i in 0..n {
s = s.wrapping_add(*a.get(&i.wrapping_mul(0x9e37_79b9)).unwrap());
}
let ours = t.elapsed();
let t = Instant::now();
let mut b: std::collections::HashMap<u64, u64> = std::collections::HashMap::new();
for i in 0..n {
b.insert(i.wrapping_mul(0x9e37_79b9), i);
}
let mut s2 = 0u64;
for i in 0..n {
s2 = s2.wrapping_add(*b.get(&i.wrapping_mul(0x9e37_79b9)).unwrap());
}
let std_t = t.elapsed();
assert_eq!(s, s2);
let t = Instant::now();
let mut c: BTreeMap<u64, u64> = BTreeMap::new();
for i in 0..n {
c.insert(i.wrapping_mul(0x9e37_79b9) % 10_000_019, i);
}
let bt = t.elapsed();
let t = Instant::now();
let mut d: std::collections::BTreeMap<u64, u64> = std::collections::BTreeMap::new();
for i in 0..n {
d.insert(i.wrapping_mul(0x9e37_79b9) % 10_000_019, i);
}
let sbt = t.elapsed();
println!("HashMap 1M insert+get: ours {:?} std {:?}; BTreeMap 1M insert: ours {:?} std {:?}", ours, std_t, bt, sbt);
}