doba

Performance

Benchmarks and scaling characteristics.

Performance

All benchmarks run on Apple M3 Pro using mitata. Numbers will vary on your hardware -- run them yourself to get a feel.

~2ns
Lookup
has() / schema resolution
~505ns
Transform
single hop, validated
~140ns
Per hop
linear scaling
~4.3ms
10k batch
sequential, 1 hop each

Operations

OperationTimeThroughput
has()~2ns500M ops/sec
hasMigration()~45ns22M ops/sec
validate()~108ns9.3M ops/sec
explain()~385ns2.6M ops/sec
transform (1 hop)~505ns2.0M ops/sec
transform (10 hops)~1.8us560K ops/sec
transform (99 hops)~13.9us72K ops/sec
findPath (100 nodes)~7.5us133K ops/sec

Lookups are O(1) regardless of registry size. Transforms scale linearly with chain depth at roughly ~140ns per hop. The graph is built once at registry creation, so path resolution doesn't rebuild anything.

Tradeoffs

Every feature has a cost. Here's what each one adds.

OperationTimeThroughput
Hooks (noop callbacks)+19%519ns -> 620ns
Deprecated migration+25%443ns -> 553ns
Pipe builder vs bare fn~2x484ns -> 903ns
Context calls (3w + 3d)~1.7x485ns -> 815ns

Hooks with empty callbacks add about 19% -- the timing hooks (onTransform, onStep) add performance.now() calls and per-step object allocations. When no timing hooks are registered, doba skips all of that entirely. The pipe builder is about 2x slower than a hand-written migration function because it runs each step (rename, drop, add, map) as a separate object spread. If that matters in your hot path, use bare functions. For most cases the ergonomics are worth it.

Providing an explicit path skips BFS entirely and saves about 52% on longer chains (2.0us vs 4.2us at 25 hops). Worth doing if you're calling the same transform repeatedly and already know the route.

Scaling

DimensionScalingNotes
Chain depthLinear~140ns per additional hop
Schema countO(1)~2ns regardless of registry size
Object sizeConstantpasses references, not copies
Batch sizeLinear~430ns per item
Graph complexityBFS10k+ edges

Edge cases

OperationTimeThroughput
Same-schema (validate: none)~120nsidentity, no work
Same-schema (validate: end)~194nsvalidates only
Unknown schema (error)~103nsfails immediately
No path found (error)~668nsruns BFS, finds nothing
Bidirectional migration~501nssame cost as one-way

Errors fail fast. An unknown schema returns in ~103ns without touching the graph. A missing path is slower (~668ns) because BFS has to exhaust the search before giving up.

Recursive structures

These measure the cost of migrating complex nested data through a single transform. The time is dominated by the migration function itself, not doba's overhead.

OperationTimeThroughput
Tree (364 nodes)~762ns1.3M ops/sec
Linked list (1k nodes)~14.4us69K ops/sec
Nested object (100 levels)~9.2us109K ops/sec
Graph node (1k edges)~5.7us175K ops/sec

Run it yourself

bun run bench              # core operations
bun tests/features.bench.ts   # feature overhead, explain, pipe, hooks
bun tests/stress.bench.ts     # extreme scale, recursive structures

On this page