remove unnecessary data structs Dict+MapArray
This commit is contained in:
@@ -1,22 +0,0 @@
|
||||
structure,size,latency_ns_avg,latency_ns_med
|
||||
MapArray,100,0.03,0.03
|
||||
Dictionary,100,0.00,0.00
|
||||
Map,100,0.00,0.00
|
||||
MapArray,250,0.05,0.05
|
||||
Dictionary,250,0.00,0.00
|
||||
Map,250,0.00,0.00
|
||||
MapArray,500,0.09,0.09
|
||||
Dictionary,500,0.00,0.00
|
||||
Map,500,0.00,0.00
|
||||
MapArray,999,0.16,0.16
|
||||
Dictionary,999,0.01,0.00
|
||||
Map,999,0.00,0.00
|
||||
MapArray,2500,0.42,0.42
|
||||
Dictionary,2500,0.01,0.01
|
||||
Map,2500,0.01,0.01
|
||||
MapArray,5000,0.81,0.80
|
||||
Dictionary,5000,0.02,0.02
|
||||
Map,5000,0.01,0.01
|
||||
MapArray,10000,1.66,1.66
|
||||
Dictionary,10000,0.05,0.04
|
||||
Map,10000,0.02,0.02
|
||||
|
@@ -1,89 +0,0 @@
|
||||
// bench/churn-sweep.ts
|
||||
import { Bench } from "tinybench";
|
||||
import { type Box, box } from "../engine/lib/box.ts";
|
||||
import {
|
||||
createDictionary,
|
||||
deleteEntry,
|
||||
setEntry,
|
||||
} from "../engine/lib/Dictionary.ts";
|
||||
import MapArray from "../engine/lib/MapArray.ts";
|
||||
|
||||
const SIZES = [100, 250, 500, 999, 2500, 5000, 10000];
|
||||
const CHURN_N = 100;
|
||||
const churnKeys = Array.from({ length: CHURN_N }, (_, i) => `churn-${i}`);
|
||||
|
||||
let sink = 0;
|
||||
|
||||
function makeBaseData(size: number): Array<[string, Box<number>]> {
|
||||
const data: Array<[string, Box<number>]> = [];
|
||||
let i = size;
|
||||
while (i-- > 0) data.push([String(i), box(i)]);
|
||||
return data;
|
||||
}
|
||||
|
||||
type Row = {
|
||||
structure: string;
|
||||
size: number;
|
||||
latencyNsAvg: number;
|
||||
latencyNsMed: number;
|
||||
};
|
||||
const rows: Row[] = [];
|
||||
|
||||
for (const size of SIZES) {
|
||||
const baseData = makeBaseData(size);
|
||||
|
||||
const ma = new MapArray(structuredClone(baseData));
|
||||
const dict = createDictionary(structuredClone(baseData));
|
||||
const map = new Map(structuredClone(baseData));
|
||||
|
||||
const bench = new Bench({ name: `churn @ size=${size}`, time: 500 });
|
||||
|
||||
bench
|
||||
.add("MapArray", () => {
|
||||
for (const key of churnKeys) ma.set(key, box(1));
|
||||
for (const key of churnKeys) ma.delete(key);
|
||||
sink += ma.entries().length;
|
||||
})
|
||||
.add("Dictionary", () => {
|
||||
for (const key of churnKeys) setEntry(dict, [key, box(1)]);
|
||||
for (const key of churnKeys) deleteEntry(dict, key);
|
||||
sink += dict.entries.length;
|
||||
})
|
||||
.add("Map", () => {
|
||||
for (const key of churnKeys) map.set(key, box(1));
|
||||
for (const key of churnKeys) map.delete(key);
|
||||
sink += map.size;
|
||||
});
|
||||
|
||||
await bench.run();
|
||||
|
||||
for (const task of bench.tasks) {
|
||||
const res = task.result;
|
||||
if (!res) continue;
|
||||
rows.push({
|
||||
structure: task.name,
|
||||
size,
|
||||
latencyNsAvg: res.latency.mean,
|
||||
latencyNsMed: res.latency.p50 ?? res.latency.mean,
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`-- size=${size} done --`);
|
||||
console.table(bench.table());
|
||||
}
|
||||
|
||||
// CSV output
|
||||
const header = "structure,size,latency_ns_avg,latency_ns_med";
|
||||
const csvLines = rows.map(
|
||||
(r) =>
|
||||
`${r.structure},${r.size},${r.latencyNsAvg.toFixed(2)},${r.latencyNsMed.toFixed(2)}`,
|
||||
);
|
||||
const csv = [header, ...csvLines].join("\n");
|
||||
|
||||
(await Deno.writeTextFile?.("churn-sweep.csv", csv)) ??
|
||||
(await import("node:fs/promises")).then((fs) =>
|
||||
fs.writeFile("churn-sweep.csv", csv),
|
||||
);
|
||||
|
||||
console.log("\nWrote churn-sweep.csv");
|
||||
console.log("sink (ignore):", sink);
|
||||
@@ -1,77 +0,0 @@
|
||||
// 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<number>]> = [];
|
||||
{
|
||||
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);
|
||||
+4
-46
@@ -1,53 +1,11 @@
|
||||
import { Bench } from "tinybench";
|
||||
import { type Box, box } from "./../engine/lib/box.ts";
|
||||
import {
|
||||
createDictionary,
|
||||
forEntry,
|
||||
getEntry,
|
||||
} from "./../engine/lib/Dictionary.ts";
|
||||
import MapArray from "./../engine/lib/MapArray.ts";
|
||||
|
||||
const bench = new Bench({ name: "MapArray get", time: 3000 });
|
||||
const bench = new Bench({ name: "", time: 1000 });
|
||||
let sink = 0;
|
||||
|
||||
const testData: Array<[string, Box<number>]> = [];
|
||||
let i = 999;
|
||||
while (i-- > 0) {
|
||||
testData.push([String(i), box(i)]);
|
||||
}
|
||||
|
||||
const ma = new MapArray(structuredClone(testData));
|
||||
const dict = createDictionary(structuredClone(testData));
|
||||
const map = new Map(structuredClone(testData));
|
||||
|
||||
bench
|
||||
.add("MapArray iter/get reads", () => {
|
||||
let sum = 0;
|
||||
|
||||
for (const [key] of ma.entries()) {
|
||||
sum += ma.get(key)!.value;
|
||||
}
|
||||
|
||||
sink += sum;
|
||||
})
|
||||
.add("Dictionary iter/get reads", () => {
|
||||
let sum = 0;
|
||||
|
||||
forEntry(dict, (_, key) => {
|
||||
sum += getEntry(dict, key)!.value;
|
||||
});
|
||||
|
||||
sink += sum;
|
||||
})
|
||||
.add("Map iter/get reads", () => {
|
||||
let sum = 0;
|
||||
|
||||
for (const [key] of map.entries()) {
|
||||
sum += map.get(key)!.value;
|
||||
}
|
||||
|
||||
sink += sum;
|
||||
});
|
||||
bench.add("", () => {
|
||||
sink += 1;
|
||||
});
|
||||
|
||||
await bench.run();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user