// bench/churn.ts import { Bench } from "tinybench"; import { type Box, box } from "./../engine/lib/box.ts"; import { createDictionary, deleteEntry, getEntry, setEntry, } from "./../engine/lib/Dictionary.ts"; import MapArray from "./../engine/lib/MapArray.ts"; const bench = new Bench({ name: "churn", time: 1000 }); const SIZE = 999; const baseData: Array<[string, Box]> = []; { let i = SIZE; while (i-- > 0) baseData.push([String(i), box(i)]); } // Churn keys: a rotating window of "new" entities not in the base set, // so every set/delete pair actually does real insert+remove work // rather than repeatedly hitting the same already-resident key. const CHURN_N = 100; const churnKeys = Array.from({ length: CHURN_N }, (_, i) => `churn-${i}`); let sink = 0; // anti-DCE: something observable reads this later function freshMapArray() { return new MapArray(structuredClone(baseData)); } function freshDictionary() { return createDictionary(structuredClone(baseData)); } function freshMap() { return new Map(structuredClone(baseData)); } // Each task rebuilds nothing per-iteration except doing insert+remove // on a fixed churn window against a persistent SIZE-element base structure — // this mirrors "N entities steady-state, M entering/leaving per frame". const ma = freshMapArray(); const dict = freshDictionary(); const map = freshMap(); bench .add("MapArray churn (set+delete)", () => { for (const key of churnKeys) { ma.set(key, box(1)); } for (const key of churnKeys) { ma.delete(key); } sink += ma.entries().length; }) .add("Dictionary churn (set+delete)", () => { for (const key of churnKeys) { setEntry(dict, [key, box(1)]); } for (const key of churnKeys) { deleteEntry(dict, key); } sink += dict.entries.length; }) .add("Map churn (set+delete)", () => { for (const key of churnKeys) { map.set(key, box(1)); } for (const key of churnKeys) { map.delete(key); } sink += map.size; }); await bench.run(); console.table(bench.table()); console.log("sink (ignore):", sink);