diff --git a/benchmarks/runtime/README.md b/benchmarks/runtime/README.md index bbb17bec1..9dea0a3ea 100644 --- a/benchmarks/runtime/README.md +++ b/benchmarks/runtime/README.md @@ -16,6 +16,7 @@ Application-shaped workloads for measuring the speed of compiled executables. Ea | `numeric-kernels` | cpu | sieve, hashing, `number[][]` matmul, `Float64Array` | | `validate-errors` | cpu | throw/catch on a fraction of inputs, small call chains | | `csv-numbers` | cpu | `split`, `Number`, `parseInt`, `toFixed` | +| `log-lines` | cpu | 200k immediate `console.log`/`process.stdout.write` lines into the runner's pipe | | `log-summary`, `inventory-report` | cpu | the multi-module applications from `benchmarks/builds`, on 300k-line inputs | | `cli-config` | startup | a `deploy-plan` CLI: read a ~110 KB JSON manifest, validate, interpolate env values, order 80 services into dependency waves, print a summary | | `http-api` | server | a `node:http` JSON API: routing, query parsing, a seeded catalog, per-cart state, typed JSON bodies, `ETag`/`If-None-Match`, auth and request-id headers | diff --git a/benchmarks/runtime/workloads.json b/benchmarks/runtime/workloads.json index 59dfba452..5e7ebfce9 100644 --- a/benchmarks/runtime/workloads.json +++ b/benchmarks/runtime/workloads.json @@ -84,6 +84,13 @@ "1" ] }, + { + "name": "log-lines", + "entry": "workloads/log-lines.ts", + "args": [ + "4" + ] + }, { "name": "log-summary", "entry": "../builds/log-summary/main.ts", diff --git a/benchmarks/runtime/workloads/log-lines.ts b/benchmarks/runtime/workloads/log-lines.ts new file mode 100644 index 000000000..c13d52bff --- /dev/null +++ b/benchmarks/runtime/workloads/log-lines.ts @@ -0,0 +1,62 @@ +// A logging-heavy CLI: one console line per record, written immediately. +// Mixes the common console shapes (a single template literal, string plus +// number arguments, several primitive arguments) with process.stdout.write +// chunks and an occasional console.error line. +interface LogRecord { + id: number; + method: string; + path: string; + status: number; + latency: number; + cached: boolean; +} + +const methods = ["GET", "POST", "PUT", "DELETE"]; +const paths = ["/api/users", "/api/orders", "/health", "/static/app.js", "/api/search?q=caf\u00e9"]; + +let seed = 7; +function random(): number { + seed = (Math.imul(seed, 1103515245) + 12345) >>> 0; + return seed / 4294967296; +} + +function makeRecord(id: number): LogRecord { + const r = random(); + return { + id, + method: methods[id % methods.length]!, + path: paths[Math.floor(random() * paths.length)]!, + status: r < 0.9 ? 200 : r < 0.97 ? 404 : 500, + latency: Math.round(random() * 250000) / 1000, + cached: random() < 0.3, + }; +} + +const scale = Number(process.argv[2] ?? "1"); +const count = Math.round(50000 * scale); +let errors = 0; +let bytes = 0; +for (let i = 0; i < count; i++) { + const rec = makeRecord(i); + switch (i % 4) { + case 0: + console.log(`${rec.method} ${rec.path} ${rec.status} ${rec.latency}ms`); + break; + case 1: + console.log("request", rec.id, rec.method, rec.status); + break; + case 2: + console.log("latency", rec.latency, "cached", rec.cached); + break; + default: { + const chunk = "[" + rec.id + "] " + rec.path + (rec.cached ? " (cached)" : "") + "\n"; + bytes += chunk.length; + process.stdout.write(chunk); + } + } + if (rec.status === 500) { + errors++; + if (errors % 100 === 0) console.error("error budget: " + errors + " server errors at record " + rec.id); + } +} +console.log("records", count, "errors", errors, "chunk chars", bytes);