Benchmarks
September 9 (Asia/Kolkata) refresh of zmdb and two selected peers per category, using the product after the latest performance fixes. HTTP covers Node, Bun and Deno. Both database comparisons use a freshly pulled PostgreSQL 18.6 image.
The tables rank only the selected participants, not every framework in the ecosystem. Timed captures run serially; each comparison uses the same resources for zmdb and its peers. p95 and p99 are shown where the runner records request or operation latencies. Validation's upstream throughput benchmark does not measure those percentiles.
The ORM fixture's projections and shared indexes were corrected before this replay. Earlier ORM numbers used unequal response shapes, and earlier HTTP captures used different CPU allocations; those historical values cannot establish a before/after speedup against this refresh.
Validation
zmdb registered as two participants in moltar/typescript-runtime-type-benchmarks and run by the upstream runner, one forked process per library, alongside the selected leaders ts-runtypes and ts-runtime-checks. zmdb is the runtime validator walking a descriptor; zmdb-aot is the same public API with the transformer applied, so the benchmarked code is transformer output rather than a hand-tuned lookalike.
Provenance & methodology
| Upstream suite | https://github.com/moltar/typescript-runtime-type-benchmarks |
|---|---|
| Grafted commit | 25a1d18491 |
| Measured | 2026-09-08T23:21:01.408Z |
| Product revision | a86e01861b7609e8c3bc76aa9a1d85ced96cdb9f |
| Harness revision | d8534b48110dede2a9ab98898c524d276d59deb5 |
| Machine | AMD Ryzen 7 7840U w/ Radeon 780M Graphics |
| Runtime | v26.8.1 |
| Load | Existing upstream run-internal invoked once per selected library in isolated child processes. Existing benny/Benchmark.js defaults; prebuilt official generators; fixed upstream data; returned values discarded by upstream Benchmark.run. Throughput and upstream relative margin only; no p95/p99 or DCE-safe claim. CPU4. Selected peers: @ts-runtypes0.10.0 and ts-runtime-checks0.6.3; no other competitors were rerun. |
parseSafe is unsupported for zmdb and zmdb-aot: those APIs retain unknown properties instead of returning the stripped copy required by upstream. Historical numbers for those two cells are invalid and are not carried forward. Throughput uses a fixed input with discarded return values; this is not a per-call latency or dead-code-elimination-safe claim.
All four cases at once
The same numbers without a tab in the way — useful because a library can win one case and lose another, and the tabbed view makes that easy to miss.
A dash means the case is unsupported. zmdb and zmdb-aot are excluded from parseSafe:
the upstream contract requires a copy with unknown properties removed, while these APIs validate and return the input.
The upstream runner measures repeated calls on one fixed object and discards returned values. It provides throughput
and error margins, not individual-call p95/p99 latency.
ORM HTTP replay
k6 replay over the upstream drizzle-team/drizzle-benchmarks 13-route request list, comparing zmdb, Drizzle and Kysely. The corrected fixture uses matching projections and response shapes, one shared dataset and index set, and the same PostgreSQL service, pg driver and connection-pool size for every participant.
Provenance & methodology
| Upstream suite | https://github.com/drizzle-team/drizzle-benchmarks |
|---|---|
| Grafted commit | 2ae27415a6 |
| Measured | 2026-09-08T23:27:56.304499+00:00 |
| Product revision | a86e01861b7609e8c3bc76aa9a1d85ced96cdb9f |
| Harness revision | c6269bedcebf222ee8372b8d9101a9fabaad2041 |
| Machine | AMD Ryzen 7 7840U w/ Radeon 780M Graphics |
| Database | postgres (PostgreSQL) 18.6 (Debian 18.6-1.pgdg13+2); image digest sha256:7341002d2b8c7c5bdd7542a671a95b36196c0b5b888daf454ae4fc33ba5346d7 |
| Dataset | Northwind:10,000 customers,200 employees,1,000 suppliers,5,000 products,50,000 orders,308,224 details |
| Driver | pg8.23.0; shared pool size12; Drizzle0.45.2; Kysely0.29.5; Node26.8.1 |
| Load | Three rotated passes per candidate,5s warmup then25s k6 ramp0→200→400VUs,426,999-entry upstream13-route request list. Server CPU4; k6 CPUs12-15; PostgreSQL CPUs6-11,memory2GiB,shm256MiB,default durability. |
Matching full projections and response shapes passed27 parity cases before timing. Both shared search GIN indexes are available to every participant. All rows use unprepared unnamed statements. These are short co-located, closed-loop runs without coordinated-omission correction; their request mixes depend on throughput. Historical projection-mismatched captures cannot establish a before/after speedup.
Route coverage
Throughput is only comparable between participants that answer the same routes. A participant that cannot express a route is marked missing here rather than being quietly dropped from the replay.
HTTP framework
Captured @zmdb/web and peer workloads under the the-benchmarker/web-frameworks shared contract — GET /, GET /user/:id, POST /user. zmdb runs on Node, Bun and Deno; the selected peers are Hono on Node and Deno, and Elysia on Bun. Each server uses one process on the same CPU.
Provenance & methodology
| Upstream suite | https://github.com/the-benchmarker/web-frameworks |
|---|---|
| Grafted commit | 2968d22c8d |
| Measured | 2026-09-08T23:48:19.553Z |
| Product revision | a86e01861b7609e8c3bc76aa9a1d85ced96cdb9f |
| Harness revision | ed507604aa8d0cf67b93ddd5bb02d8729c50dd30 |
| Peers measured | 2026-09-08T23:48:19.553Z |
| Machine | linux x64, 16× AMD Ryzen 7 7840U w/ Radeon 780M Graphics, 61 GiB, node v26.8.1 |
| Load | Three rotated repetitions per candidate and each of the three published routes at c256; one server process on CPU4, oha on CPUs10,12,14, keepalive off. Each repetition has 1s warmup and 5s measurement. Saturation is closed-loop without coordinated-omission correction; fixed-rate uses -q20000 with --latency-correction. Each displayed row is the median-throughput repetition with its own percentiles. Other source builds, tests and benchmark captures were stopped during timing. |
| Peer load | Three rotated repetitions per candidate and each of the three published routes at c256; one server process on CPU4, oha on CPUs10,12,14, keepalive off. Each repetition has 1s warmup and 5s measurement. Saturation is closed-loop without coordinated-omission correction; fixed-rate uses -q20000 with --latency-correction. Each displayed row is the median-throughput repetition with its own percentiles. Other source builds, tests and benchmark captures were stopped during timing. |
| Contract | Every measured participant passed the three shared-route status/body checks before and after load. |
Recorded runtimes: 3 —
node v26.8.1: 1 worker, measured 2026-09-08T23:48:19.553Z; bun 1.4.0: 1 worker, measured 2026-09-08T23:48:19.553Z; deno deno 2.9.6 (stable, release, x86_64-unknown-linux-gnu)
v8 15.0.245.2-rusty
typescript 6.0.3: 1 worker, measured 2026-09-08T23:48:19.553Z.
Every candidate uses one server process, the same server CPU and separate load-generator CPUs. Saturation measures maximum throughput with closed-loop load; its latency is not coordinated-omission corrected. The fixed-rate capture offers 20,000 requests per second to every candidate and enables oha latency correction. Each displayed row keeps throughput and percentiles from the same median-throughput repetition. Brackets show the range of the three run summaries, not pooled percentiles or confidence intervals; raw downloads retain all repetitions.
Shared contract
Verified against every participant, ours included, before load is applied. A framework that fails this is not measured — a fast wrong answer is not a result.
| Method | Route | Status | Body | Verified |
|---|---|---|---|---|
GET | / | 200 | yes | |
GET | /user/:id | 200 | 42 (for /user/42) | yes |
POST | /user | 200 | yes |
Repository and relation loading
Current generated schemas, the same PostgreSQL data and indexes, pg driver and pool of 12 connections. The nested workload returns 25 users, 100 posts and 300 comments. Kysely and the zmdb builder use three queries and the same assembler; repository and deferred loading use the public repository APIs. Native Drizzle uses one relational query with JSON aggregation. Result equality is checked before timing.
Provenance & methodology
| Measured | 2026-09-08T23:35:00.608Z |
|---|---|
| Product revision | a86e01861b7609e8c3bc76aa9a1d85ced96cdb9f |
| Harness revision | 3c1c84dc5d1b5c41277f77983697dcaa8f660083 |
| Machine | AMD Ryzen 7 7840U w/ Radeon 780M Graphics |
| Runtime | v26.8.1 |
| Database | postgres (PostgreSQL) 18.6 (Debian 18.6-1.pgdg13+2) |
| Driver | pg8.23.0; pool12; Drizzle0.45.2; Kysely0.29.5 |
| Load | Direct SQL-builder comparison uses each library to compile statements, then one common pg Pool transport and one common manual assembler. Repository/deferred lanes use the real BaseRepository. The separately labeled drizzle-relational strategy executes native findMany with one lateral JSON aggregation statement and its own result mapper through the same pool. Same selected columns and bound predicates; unordered graph equality is checked outside timing. All statements use the unprepared unnamed extended protocol. Trace timings are instrumented and excluded from clean ranking; roundtrip includes driver/network/server. Other time includes JS attachment, uninstrumented native relational compilation and async scheduling, not pure CPU. Three rotated rounds,1s warmup and4s measurement per cell; caller CPU4 and PostgreSQL CPUs6-11. |
Each row is the median-throughput repetition with its own p95/p99, followed by the throughput range across all three repetitions. These are closed-loop client-operation latencies, without coordinated-omission correction.
lookup · 1 concurrent operations
| Participant | ops/s | p95 ms | p99 ms | ops/s range |
|---|---|---|---|---|
| zmdb-builder | 16,764.71 | 0.07 | 0.1 | 16,584.09–17,017.8 |
| kysely | 16,437.34 | 0.07 | 0.1 | 16,435.35–16,555.82 |
| repository | 15,762.71 | 0.08 | 0.12 | 15,657.91–16,432 |
nested · 1 concurrent operations
| Participant | ops/s | p95 ms | p99 ms | ops/s range |
|---|---|---|---|---|
| zmdb-builder | 1,464 | 0.92 | 1.07 | 1,443.91–1,515.76 |
| kysely | 1,463.13 | 0.92 | 1.08 | 1,424.86–1,517.76 |
| repository | 1,242.41 | 1.05 | 1.25 | 1,183.91–1,270.38 |
| deferred | 1,215.78 | 1.06 | 1.27 | 1,208.82–1,242.72 |
| drizzle-relational | 1,086.36 | 1.28 | 1.48 | 1,048.84–1,087.5 |
lookup · 48 concurrent operations
| Participant | ops/s | p95 ms | p99 ms | ops/s range |
|---|---|---|---|---|
| zmdb-builder | 48,183.92 | 1.57 | 2.17 | 47,721.75–48,284.18 |
| kysely | 44,505.02 | 1.68 | 2.07 | 42,060.13–45,722.13 |
| repository | 41,179.5 | 1.88 | 2.29 | 38,581.41–41,624.8 |
nested · 48 concurrent operations
| Participant | ops/s | p95 ms | p99 ms | ops/s range |
|---|---|---|---|---|
| zmdb-builder | 2,930.75 | 18.99 | 20.42 | 2,895.58–2,976.53 |
| kysely | 2,757.32 | 20.38 | 22.48 | 2,683.63–2,801.26 |
| repository | 2,021.26 | 27.14 | 29.07 | 2,020.42–2,085.94 |
| drizzle-relational | 1,882.28 | 32.05 | 44.94 | 1,829.36–1,949.26 |
| deferred | 1,802.63 | 30.73 | 34.67 | 1,746.77–1,829.17 |
Latest optimizations — 9 September 2026 (Asia/Kolkata)
Before/after measurements of the changes integrated in 4c9cf2f4, using
9c8c206f as the baseline. These short, sequential runs compare zmdb with itself.
They do not update competitor rankings or establish an industry-wide lead.
AMD Ryzen 7 7840U; Node 26.8.1, Bun 1.4.0, Deno 2.9.6; project compiler TypeScript 7.0.2; PostgreSQL 18.6, pg 8.23.0; oha 1.16.0. One application process on logical CPU 4. Timed runs were sequential with no concurrent owned builds or tests; ordinary desktop services remained running.
Values in brackets are observed sample ranges, not confidence intervals. The p99 columns show ranges of individual run percentiles; they are not pooled percentiles.
Serialization and generated validation
Five 80 ms samples per case and version, alternating order after 150 ms warmup, using the same 32 rotating inputs and consumed outputs. Lower time is better. These are the actual generated predicates and public serializer; no peer implementation was loaded.
| Workload | Before (median ns/op) | After (median ns/op) | Time reduction |
|---|---|---|---|
| Numeric validation: valid | 20.46 [20.43–21.06] | 19.61 [19.3–20.39] | +4.13% |
| Numeric validation: late invalid | 20.21 [19.76–20.38] | 19.76 [19.14–21.33] | +2.27% |
| Strict validation: valid | 34.1 [33.33–34.5] | 34.16 [32.6–41.86] | -0.19% |
| Strict validation: excess property | 33.61 [32.77–35.36] | 33.12 [31.76–37.91] | +1.47% |
| JSON: plain strings | 1,199.19 [1,109.37–1,347.7] | 534.18 [490.79–616.34] | +55.46% |
| JSON: escaped strings | 1,499.54 [1,401.27–1,520.13] | 710.33 [659.71–816.85] | +52.63% |
Native serialization took 53–55% less time on these inputs. Numeric-validation differences were small and several sample ranges overlap; no general validator speedup is established.
HTTP and request data
Two four-second samples per version after one-second warmup, in before/after/after/before order, at 64 connections with keep-alive. One server process on CPU 4; client on CPUs 10, 12 and 14. Closed-loop maximum throughput; latency is not coordinated-omission corrected.
| Workload | Before (mean req/s) | After (mean req/s) | Throughput change | Before p99 (ms) | After p99 (ms) |
|---|---|---|---|---|---|
| Node POST validation | 39,326.86 [36,328.05–42,325.67] | 43,298.56 [41,769.06–44,828.05] | +10.1% | 4.25–5.22 | 3.28–3.76 |
| Bun POST validation | 42,862.17 [42,614.35–43,109.99] | 45,274.44 [43,365.94–47,182.95] | +5.63% | 2.88–3.15 | 2.54–3 |
| Deno POST validation | 51,406.19 [50,746.64–52,065.74] | 61,561.37 [60,167.55–62,955.19] | +19.75% | 4.76–4.89 | 4.16–4.51 |
| Deno GET parameter | 78,532.97 [77,607.8–79,458.15] | 93,252.92 [91,542.56–94,963.29] | +18.74% | 2.06–2.12 | 1.64–1.75 |
These results combine Fetch header/response conversion and the shared lazy request-data getter; their individual contributions are not isolated. Node throughput ranges and Bun p99 ranges overlap. The bounded request-body reader is unchanged.
Nested relation loading
Two four-second samples per version after one-second warmup, in before/after/after/before order, at concurrency 48 with a 12-connection pg pool. Each read returns 25 users, 100 posts and 300 comments in three queries. Medians of two samples are their midpoint. PostgreSQL runs on CPUs 6–11 on the same machine.
| Workload | Before (median operations/s) | After (median operations/s) | Throughput change | Before p99 (ms) | After p99 (ms) |
|---|---|---|---|---|---|
| Nested repository | 1,425.94 [1,403.86–1,448.02] | 1,572.69 [1,512.89–1,632.5] | +10.29% | 60.54–74.77 | 63.92–70.51 |
| Deferred nested loading | 1,409.09 [1,403.03–1,415.14] | 1,488.57 [1,370.15–1,607] | +5.64% | 62.24–63.85 | 40.84–68.98 |
Repository throughput increased about 10% in this probe. Deferred results were more variable, and neither lane establishes a consistent p99 improvement.
Database comparison fairness
The corrected 13-route ORM fixture uses matching projections, response structures, data, driver and pool settings across zmdb, Drizzle and Kysely. Both full-text GIN indexes are created by the shared seed and are available to every participant. All 27 functional response-parity cases passed, and PostgreSQL selected both indexes. This is a correctness result, not a newly measured peer ranking.
Summary and provenance (JSON) · Per-run measurements and source hashes (gzip JSON) · Earlier comparative benchmarks and profiling report
The linked investigation measured revision 07b3ec80 before these optimizations.
Its peer comparisons and experimental ablations retain their original conditions; experimental gains are not substituted for the implemented measurements above.
Engineering costs — earlier capture
Recorded zmdb measurements at revision cd6cdcc965a38d6ddc5b40a91b30f2f66bde4571.
Each row uses the committed metric name, unit, median and min–max range. Values are displayed to three decimal places;
full precision and every sample are available in the downloads.
| Capture | 2026-09-08T12:00:09.163Z → 2026-09-08T12:08:20.206Z |
|---|---|
| Node / npm / Yarn | v26.8.1 / 12.0.2 / 4.18.0 |
| TypeScript / SQLite | 7.0.2 / 3.53.4 |
| PostgreSQL | PostgreSQL 18.6 (Debian 18.6-1.pgdg13+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit |
| Load generator | k6 v2.2.0 (commit/00a9a1b7f5, go1.26.5, linux/amd64) |
| Platform | linux 7.1.8-gentoo x64 |
| CPU | AMD Ryzen 7 7840U w/ Radeon 780M Graphics (16 logical CPUs) |
| Memory | 65,044,914,176 bytes |
| Initial load average | 1.93, 5.03, 3.35 |
| Rounds | 7 |
Repeated current-revision zmdb measurements. Every sample is retained; summaries report median and range. Clean build means no emitted output; cached build retains filesystem/dependency caches. OS caches are not flushed. Compiler and installation tools are included in consumer size. PostgreSQL and load generator share this machine. No competitor reruns or comparative ranking.
OS caches are warm or unflushed, including clean-build runs; this capture does not measure a cold OS cache. PostgreSQL and the load generator are co-located. These zmdb-only results are separate from the historical competitor, PostgreSQL 16, Bun and Deno measurements below.
Summary and per-metric samples (JSON) · Complete raw runs and command output (gzip JSON) · Aggregation and metric definitions
Editor and compiler
Public product and server-core consumer projects: language-service operations and clean, incremental and affected-edit compilation. Measurement source.
| Metric | Unit | Samples | Median | Min–max |
|---|---|---|---|---|
editor/consumer-product/load | ms | 7 | 302.748 | 283.341–320.011 |
editor/consumer-product/diagnostics | ms | 7 | 3.733 | 3.612–3.997 |
editor/consumer-product/completion | ms | 7 | 1.813 | 1.666–2.581 |
editor/consumer-product/quickInfo | ms | 7 | 2.406 | 2.172–2.886 |
editor/consumer-product/edit-and-diagnostics | ms | 7 | 3.419 | 2.825–4.142 |
editor/consumer-product/undo-and-diagnostics | ms | 7 | 2.626 | 2.153–4.108 |
compiler/consumer-product/clean | ms | 7 | 385.64 | 363.161–389.522 |
compiler/consumer-product/incremental | ms | 7 | 78.666 | 75.85–83.303 |
compiler/consumer-product/affected-edit | ms | 7 | 96.272 | 93.52–106.009 |
editor/consumer-server-core/load | ms | 7 | 290.54 | 276.926–337.389 |
editor/consumer-server-core/diagnostics | ms | 7 | 1.888 | 1.855–2.083 |
editor/consumer-server-core/completion | ms | 7 | 1.331 | 1.236–1.812 |
editor/consumer-server-core/quickInfo | ms | 7 | 1.509 | 1.343–1.806 |
editor/consumer-server-core/edit-and-diagnostics | ms | 7 | 2.823 | 2.516–3.046 |
editor/consumer-server-core/undo-and-diagnostics | ms | 7 | 2.368 | 1.901–2.704 |
compiler/consumer-server-core/clean | ms | 7 | 213.454 | 207.274–249.67 |
compiler/consumer-server-core/incremental | ms | 7 | 56.84 | 55.174–60.577 |
compiler/consumer-server-core/affected-edit | ms | 7 | 79.76 | 77.483–87.144 |
Build, package and consumer
Clean builds remove emitted output; cached builds repeat the same build command with filesystem and dependency caches retained. Installed size and package count include TypeScript and installation tooling. Measurement source.
| Metric | Unit | Samples | Median | Min–max |
|---|---|---|---|---|
build/clean | ms | 7 | 6,613.607 | 6,294.841–6,791.917 |
build/cached | ms | 7 | 6,628.487 | 6,362.771–6,703.697 |
package/pack-required-closure | ms | 7 | 4,546.847 | 4,439.198–4,596.987 |
package/archive-size | bytes | 7 | 2,491,144 | 2,491,144–2,491,144 |
consumer/installMs | ms | 7 | 6,070.039 | 5,366.784–6,354.656 |
consumer/importMs | ms | 7 | 81.447 | 76.673–83.763 |
consumer/typecheckAndEmitMs | ms | 7 | 380.582 | 369.229–391.566 |
consumer/processMs | ms | 7 | 151.191 | 149.722–154.255 |
consumer/installed-size-with-typescript | bytes | 7 | 72,500,531 | 72,500,531–72,500,531 |
consumer/installed-package-count-with-typescript | count | 7 | 21 | 21–21 |
Startup and first work
Separate application initialization, process readiness, first HTTP request, first application query through SQLite and shutdown intervals. Measurement source.
| Metric | Unit | Samples | Median | Min–max |
|---|---|---|---|---|
startup/applicationInitMs | ms | 7 | 0.115 | 0.102–0.12 |
startup/processToReadinessMs | ms | 7 | 124.036 | 122.7–127.135 |
startup/firstHttpMs | ms | 7 | 16.613 | 16.107–17.06 |
startup/firstApplicationQueryMs | ms | 7 | 0.408 | 0.397–0.515 |
startup/shutdownMs | ms | 7 | 0.829 | 0.802–0.857 |
PostgreSQL replay
zmdb-only HTTP replay. Latency medians and p90/p95/p99 rows are medians of per-run percentiles; they are not percentiles of a pooled request distribution. The range is the minimum and maximum run summary. Measurement source.
| Metric | Unit | Samples | Median | Min–max |
|---|---|---|---|---|
postgres/requests-per-second | req/s | 7 | 3,690.018 | 3,625.136–3,715.487 |
postgres/latency-avg | ms | 7 | 80.79 | 80.34–82.396 |
postgres/latency-med | ms | 7 | 89.813 | 89.248–91.072 |
postgres/latency-p(90) | ms | 7 | 118.105 | 117.407–120.263 |
postgres/latency-p(95) | ms | 7 | 126.806 | 125.976–129.723 |
postgres/latency-p(99) | ms | 7 | 156.108 | 151.042–158.424 |
Reproduce this workload
Use an idle machine, a fresh work directory outside the repository, an installed k6 executable and a PostgreSQL server
seeded with the Northwind loader.
The capture records: node benchmarks/scripts/baseline.mjs --work-dir <outside-workspace> --rounds 7 (K6 and PGURL must identify the load generator and seeded PostgreSQL server).
K6=/absolute/path/to/k6 PGURL='postgres://user:password@localhost:55432/bench' \
node benchmarks/scripts/baseline.mjs --work-dir ../zmdb-engineering-run --rounds 7
node --import ./scripts/ts-specifier-hook.mjs docs-site/build.mjs
The baseline script invokes the linked editor/compiler, lifecycle and PostgreSQL workloads and retains all rounds. The docs build renders the committed files. See the reproduction guide for setup and individual diagnostic commands.
Raw data: validation.json · orm.json · framework.json · repository.json · Fresh peer comparison samples and provenance · orm-results.json · framework-results.json (node) · framework-results-bun.json · framework-results-deno.json · peers-results.json · interleaved-results.json · interleaved-measurements.csv (every individual measurement, with the CPU clock it was taken at) · validation-matrix.json (standalone harness cross-check)