Download packages/minidb/bench/message-range.ts from SaylorTwift/kimi-code: direct link, hf CLI and curl.
- Browser
- Download file 6.2 kB
-
https://huggingface.co/SaylorTwift/kimi-code/resolve/main/packages/minidb/bench/message-range.ts
- Command line
-
hf download hf://SaylorTwift/kimi-code/packages/minidb/bench/message-range.ts
-
curl -L -o message-range.ts https://huggingface.co/SaylorTwift/kimi-code/resolve/main/packages/minidb/bench/message-range.ts
6.2 kB
| // bench/message-range.ts | |
| // | |
| // Benchmarks the "show me the user's most recent messages" query: | |
| // predicate: role === 'user' AND ts >= since (last 7 days) | |
| // order: ts DESC | |
| // limit: 50 (the typical "recent N" UI pattern) | |
| // | |
| // This is a message-granularity range query with a SMALL LIMIT, which is a | |
| // different shape from the session-level benches: | |
| // - import-kimi-code / search-baseline index whole sessions (one doc/session). | |
| // - the metadata lookups (workspace lookup, dt range over sessions) return | |
| // ALL matches, so at small N a linear filter beats the index. | |
| // Here, an ordered dt index can walk just `limit` entries (O(log N + limit)) | |
| // instead of scanning + sorting the whole set (O(N log M)). We measure at the | |
| // real scale (~7.2k user messages) and at scaled-up sizes to find the | |
| // crossover where the index starts winning. | |
| // | |
| // Run: node --import tsx bench/message-range.ts | |
| import fs from 'node:fs/promises'; | |
| import { existsSync, readFileSync } from 'node:fs'; | |
| import path from 'node:path'; | |
| import os from 'node:os'; | |
| import { MiniDb } from '../src/index.js'; | |
| const fmt = (n: number) => n.toLocaleString('en-US', { maximumFractionDigits: 0 }); | |
| const LIMIT = 50; | |
| const WINDOW_DAYS = 30; // replicated timestamps are spread over the last 30 days | |
| const SINCE_DAYS = 7; // "recent" = last 7 days | |
| interface Msg { | |
| id: string; | |
| ts: number; | |
| role: string; | |
| text: string; | |
| } | |
| function loadRealMessages(): Msg[] { | |
| const DATA = path.join(os.homedir(), '.kimi-code'); | |
| const lines = readFileSync(path.join(DATA, 'session_index.jsonl'), 'utf8').trim().split('\n'); | |
| const out: Msg[] = []; | |
| for (const line of lines) { | |
| let meta: any; | |
| try { | |
| meta = JSON.parse(line); | |
| } catch { | |
| continue; | |
| } | |
| const wire = path.join(meta.sessionDir, 'agents', 'main', 'wire.jsonl'); | |
| if (!existsSync(wire)) continue; | |
| let raw: string; | |
| try { | |
| raw = readFileSync(wire, 'utf8'); | |
| } catch { | |
| continue; | |
| } | |
| for (const ln of raw.split('\n')) { | |
| if (!ln) continue; | |
| let o: any; | |
| try { | |
| o = JSON.parse(ln); | |
| } catch { | |
| continue; | |
| } | |
| if (o.type !== 'context.append_message' || !o.message) continue; | |
| const m = o.message; | |
| const ts = typeof o.time === 'number' ? o.time : 0; | |
| let text = ''; | |
| for (const c of m.content || []) | |
| if (c && c.type === 'text' && typeof c.text === 'string') text += c.text; | |
| out.push({ id: `${meta.sessionId}:${out.length}`, ts, role: m.role || 'user', text }); | |
| } | |
| } | |
| return out; | |
| } | |
| // Replicate the real messages to N, spreading timestamps uniformly over the | |
| // last WINDOW_DAYS so the "last SINCE_DAYS" match fraction stays ~constant. | |
| function scaleTo(real: Msg[], n: number): Msg[] { | |
| const now = Date.now(); | |
| const span = WINDOW_DAYS * 864e5; | |
| const out: Msg[] = Array.from({ length: n }, (_, i) => { | |
| const r = real[i % real.length]!; | |
| return { | |
| id: `m${i}`, | |
| ts: now - Math.floor((i / n) * span), | |
| role: r.role, | |
| text: r.text, | |
| }; | |
| }); | |
| return out; | |
| } | |
| async function buildMinidb(msgs: Msg[]): Promise<{ db: any; buildMs: number; dir: string }> { | |
| const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'minidb-msg-')); | |
| const db = await MiniDb.open({ dir, valueCodec: 'json', fsyncPolicy: 'no', autoCompact: false }); | |
| const t0 = performance.now(); | |
| // concurrent insert in chunks to avoid one giant promise array | |
| const CHUNK = 5000; | |
| for (let s = 0; s < msgs.length; s += CHUNK) { | |
| const p: Promise<unknown>[] = []; | |
| for (let i = s; i < Math.min(s + CHUNK, msgs.length); i++) { | |
| const m = msgs[i]!; | |
| p.push(db.set(m.id, { role: m.role, text: m.text }, { dt: { ts: m.ts } })); | |
| } | |
| await Promise.all(p); | |
| } | |
| return { db, buildMs: performance.now() - t0, dir }; | |
| } | |
| function med(fn: () => void, runs = 9): number { | |
| const t: number[] = []; | |
| for (let i = 0; i < runs; i++) { | |
| const t0 = performance.now(); | |
| fn(); | |
| t.push(performance.now() - t0); | |
| } | |
| t.sort((a, b) => a - b); | |
| return t[(runs / 2) | 0]!; | |
| } | |
| async function runCase(label: string, msgs: Msg[]) { | |
| const since = Date.now() - SINCE_DAYS * 864e5; | |
| // ---- minidb dt index ---- | |
| const { db, buildMs, dir } = await buildMinidb(msgs); | |
| let indexHits = 0; | |
| const indexMs = med(() => { | |
| const res = db.dtRange('ts', { gte: since, reverse: true, limit: LIMIT }); | |
| indexHits = res.length; | |
| }); | |
| // variant: return ALL matches (no limit), to mirror the earlier metadata case | |
| let allHits = 0; | |
| const indexAllMs = med(() => { | |
| const res = db.dtRange('ts', { gte: since }); | |
| allHits = res.length; | |
| }); | |
| if (global.gc) global.gc(); | |
| const heap = process.memoryUsage().heapUsed; | |
| await db.close(); | |
| await fs.rm(dir, { recursive: true, force: true }); | |
| // ---- naive baseline ---- | |
| let naiveHits = 0; | |
| const naiveMs = med(() => { | |
| const res = msgs.filter((m) => m.ts >= since).sort((a, b) => b.ts - a.ts).slice(0, LIMIT); | |
| naiveHits = res.length; | |
| }); | |
| let naiveAllHits = 0; | |
| const naiveAllMs = med(() => { | |
| const res = msgs.filter((m) => m.ts >= since); | |
| naiveAllHits = res.length; | |
| }); | |
| console.log(`\n--- ${label}: N=${fmt(msgs.length)} messages ---`); | |
| console.log(` build: minidb ${(buildMs / 1000).toFixed(2)}s | heap ${(heap / 1024 / 1024).toFixed(0)} MiB`); | |
| console.log( | |
| ` top-${LIMIT} recent: minidb dtRange ${indexMs.toFixed(3)} ms (${indexHits}) vs naive filter+sort ${naiveMs.toFixed(3)} ms (${naiveHits}) -> ${(naiveMs / indexMs).toFixed(1)}x`, | |
| ); | |
| console.log( | |
| ` ALL matches: minidb dtRange ${indexAllMs.toFixed(3)} ms (${fmt(allHits)}) vs naive filter ${naiveAllMs.toFixed(3)} ms (${fmt(naiveAllHits)}) -> ${(naiveAllMs / indexAllMs).toFixed(1)}x`, | |
| ); | |
| } | |
| async function main() { | |
| const real = loadRealMessages(); | |
| console.log(`loaded ${fmt(real.length)} real user messages`); | |
| const sizes = [real.length, 100_000, 1_000_000]; | |
| for (const n of sizes) { | |
| const msgs = n === real.length ? real : scaleTo(real, n); | |
| await runCase(n === real.length ? 'real data' : `scaled`, msgs); | |
| } | |
| console.log('\ndone.'); | |
| } | |
| main().catch((e) => { | |
| console.error(e); | |
| process.exit(1); | |
| }); | |