diff options
Diffstat (limited to 'packages/benchmarks/src/benchmarks.ts')
| -rw-r--r-- | packages/benchmarks/src/benchmarks.ts | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/packages/benchmarks/src/benchmarks.ts b/packages/benchmarks/src/benchmarks.ts new file mode 100644 index 00000000..f2883246 --- /dev/null +++ b/packages/benchmarks/src/benchmarks.ts @@ -0,0 +1,158 @@ +import type { TaskResult } from "tinybench"; +import { Bench } from "tinybench"; + +import type { SeedResult } from "./seed"; +import { logInfo, logStep, logSuccess } from "./log"; +import { formatMs, formatNumber } from "./utils"; + +// Type guard for completed task results +type CompletedTaskResult = Extract<TaskResult, { state: "completed" }>; + +export interface BenchmarkRow { + name: string; + ops: number; + mean: number; + p75: number; + p99: number; + samples: number; +} + +export interface BenchmarkOptions { + timeMs?: number; + warmupMs?: number; +} + +export async function runBenchmarks( + seed: SeedResult, + options?: BenchmarkOptions, +): Promise<BenchmarkRow[]> { + const bench = new Bench({ + time: options?.timeMs ?? 1000, + warmupTime: options?.warmupMs ?? 300, + }); + + const sampleTag = seed.tags[0]; + const sampleList = seed.lists[0]; + const sampleIds = seed.bookmarks.slice(0, 50).map((b) => b.id); + + bench.add("bookmarks.getBookmarks (page)", async () => { + await seed.trpc.bookmarks.getBookmarks.query({ + limit: 50, + }); + }); + + if (sampleTag) { + bench.add("bookmarks.getBookmarks (tag filter)", async () => { + await seed.trpc.bookmarks.getBookmarks.query({ + limit: 50, + tagId: sampleTag.id, + }); + }); + } + + if (sampleList) { + bench.add("bookmarks.getBookmarks (list filter)", async () => { + await seed.trpc.bookmarks.getBookmarks.query({ + limit: 50, + listId: sampleList.id, + }); + }); + } + + if (sampleList && sampleIds.length > 0) { + bench.add("lists.getListsOfBookmark", async () => { + await seed.trpc.lists.getListsOfBookmark.query({ + bookmarkId: sampleIds[0], + }); + }); + } + + bench.add("bookmarks.searchBookmarks", async () => { + await seed.trpc.bookmarks.searchBookmarks.query({ + text: seed.searchTerm, + limit: 20, + }); + }); + + bench.add("bookmarks.getBookmarks (by ids)", async () => { + await seed.trpc.bookmarks.getBookmarks.query({ + ids: sampleIds.slice(0, 20), + includeContent: false, + }); + }); + + logStep("Running benchmarks"); + await bench.run(); + logSuccess("Benchmarks complete"); + + const rows = bench.tasks + .map((task) => { + const result = task.result; + + // Check for errored state + if ("error" in result) { + console.error(`\n⚠️ Benchmark "${task.name}" failed with error:`); + console.error(result.error); + return null; + } + + // Check if task completed successfully + if (result.state !== "completed") { + console.warn( + `\n⚠️ Benchmark "${task.name}" did not complete. State: ${result.state}`, + ); + return null; + } + + return toRow(task.name, result); + }) + .filter(Boolean) as BenchmarkRow[]; + + renderTable(rows); + logInfo( + "ops/s uses tinybench's hz metric; durations are recorded in milliseconds.", + ); + + return rows; +} + +function toRow(name: string, result: CompletedTaskResult): BenchmarkRow { + // The statistics are now in result.latency and result.throughput + const latency = result.latency; + const throughput = result.throughput; + + return { + name, + ops: throughput.mean, // ops/s is the mean throughput + mean: latency.mean, + p75: latency.p75, + p99: latency.p99, + samples: latency.samplesCount, + }; +} + +function renderTable(rows: BenchmarkRow[]): void { + const headers = ["Benchmark", "ops/s", "avg", "p75", "p99", "samples"]; + + const data = rows.map((row) => [ + row.name, + formatNumber(row.ops, 1), + formatMs(row.mean), + formatMs(row.p75), + formatMs(row.p99), + String(row.samples), + ]); + + const columnWidths = headers.map((header, index) => + Math.max(header.length, ...data.map((row) => row[index].length)), + ); + + const formatRow = (cells: string[]): string => + cells.map((cell, index) => cell.padEnd(columnWidths[index])).join(" "); + + console.log(""); + console.log(formatRow(headers)); + console.log(columnWidths.map((width) => "-".repeat(width)).join(" ")); + data.forEach((row) => console.log(formatRow(row))); + console.log(""); +} |
