// Large-scale benchmark: 100k vectors, L2. Build takes minutes on the wasm
// backends, so this is meant for occasional manual runs rather than CI:
//
// moon run --target wasm-gc examples/benchmark-large
///|
priv struct Rng {
mut state : UInt64
}
///|
fn Rng::next_f64(self : Rng) -> Double {
let state = self.state + 0x9E3779B97F4A7C15UL
self.state = state
let mut z = state
z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9UL
z = (z ^ (z >> 27)) * 0x94D049BB133111EBUL
((z ^ (z >> 31)) >> 11).to_double() * (1.0 / 9007199254740992.0)
}
///|
fn gen_dataset(seed : UInt64, n : Int32, dim : Int32) -> FixedArray[Float] {
let rng = { state: seed, }
let data : FixedArray[Float] = FixedArray::make(n * dim, 0.0)
let mut i = 0
while i < n * dim {
data[i] = Float::from_double(rng.next_f64())
i += 1
}
data
}
///|
fn row_of(
data : FixedArray[Float],
row : Int32,
dim : Int32,
) -> FixedArray[Float] {
let out : FixedArray[Float] = FixedArray::make(dim, 0.0)
FixedArray::unsafe_blit(out, 0, data, row * dim, dim)
out
}
///|
/// Squared L2 distance between query `q` and flat-dataset row `row`.
fn l2_row(
data : FixedArray[Float],
row : Int32,
dim : Int32,
q : FixedArray[Float],
) -> Double {
let off = row * dim
let mut s : Float = 0.0
let mut j = 0
while j < dim {
let d = data.unsafe_get(off + j) - q.unsafe_get(j)
s += d * d
j += 1
}
s.to_double()
}
///|
/// Exact top-k ids via a single pass over all rows — the fair brute-force
/// baseline (n distances per query).
fn brute_topk(
data : FixedArray[Float],
n : Int32,
dim : Int32,
q : FixedArray[Float],
k : Int32,
) -> Array[Int32] {
let ids : FixedArray[Int32] = FixedArray::make(k, -1)
let ds : FixedArray[Double] = FixedArray::make(k, 1.0e308)
let mut filled = 0
for i in 0.. 0 && ds.unsafe_get(j - 1) > d {
ids[j] = ids.unsafe_get(j - 1)
ds[j] = ds.unsafe_get(j - 1)
j -= 1
}
ids[j] = i
ds[j] = d
filled += 1
} else if d < ds.unsafe_get(k - 1) {
let mut j = k - 1
while j > 0 && ds.unsafe_get(j - 1) > d {
ids[j] = ids.unsafe_get(j - 1)
ds[j] = ds.unsafe_get(j - 1)
j -= 1
}
ids[j] = i
ds[j] = d
}
}
let out : Array[Int32] = []
for j in 0.. String {
((x * 100.0 + 0.5).to_int().to_double() / 100.0).to_string()
}
///|
fn fmt_mb(bytes : Int64) -> String {
fmt2(bytes.to_double() / (1024.0 * 1024.0))
}
///|
fn run_all() -> Unit raise @moonhnsw.HnswError {
let n = 100000
let dim = 32
let n_queries = 20
let k = 10
let m = 16
let ef_construction = 200
println(
"--- L2 scale n=\{n} dim=\{dim} queries=\{n_queries} k=\{k} m=\{m} ef_construction=\{ef_construction} ---",
)
let data = gen_dataset(20261028UL, n, dim)
let queries = gen_dataset(20261029UL, n_queries, dim)
// Build.
let t0 = @bench.monotonic_clock_start()
let idx = @moonhnsw.Hnsw::new(
dim,
@moonhnsw.Distance::L2,
params=@moonhnsw.HnswParams::new(m~, ef_construction~),
)
for i in 0..