aboutsummaryrefslogtreecommitdiffstats
path: root/packages/benchmarks/src/benchmarks.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/benchmarks/src/benchmarks.ts')
-rw-r--r--packages/benchmarks/src/benchmarks.ts158
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("");
+}