This is batched transport capacity, not full app throughput. We measured a 1 MB echo
roundtrip (batch=64, host -> worker -> host), which is 2 MB transferred per call.
Here’s what we saw (GB/s, one-way equivalent):
Runtime
Uint8Array
string
Bun
16.21
11.86
Deno
5.78
3.37
Node
7.50
1.44
We report one-way equivalent (half the roundtrip) to keep units comparable.
Treat these as communication-layer upper bounds: they do not mean your app will be 3.5x faster.
Real gains depend on workload shape and bottlenecks (CPU contention, serialization cost, DB/network waits, etc.).
Shared-memory request/response mailboxes keep the control path cheap; payload buffers and batching keep the data path fast.
The core idea is simple: instead of routing every task through the runtime’s message queue,
Knitting coordinates through shared memory. That keeps task calls cheap and predictable.
Control path: shared mailboxes
There are two mailboxes: one for requests and one for responses. Think of it as a
full-duplex pipe in shared memory.
A call claims a slot, writes a small header, and wakes up a worker.
The worker does the work, writes the result back, and wakes up the host.
Slot ownership uses bitsets, so it’s all lock-free.
Data path: payload buffers
When values are too large for the header, they go into reusable payload buffers.
Each worker gets two SharedArrayBuffers: one for requests and one for returns.
You control the sizes (payload.payloadInitialBytes, payload.payloadMaxByteLength), so you can trade memory for speed.
Here’s how Knitting behaves so you’re not guessing:
Async is deterministic: promises resolve on the host before dispatch, and async results are awaited in the worker before returning. No half-resolved surprises on either side.
Errors stay contained: if a task throws or rejects, that single call fails — the worker keeps going for the next one. One bad call doesn’t take down the pool.
No busy-waiting by default: idle workers spin briefly, trigger a GC if no work comes (so idle time is productive), then park until there’s work. The host dispatcher backs off too. Your CPU isn’t burning cycles on nothing.
Fair by default: round-robin lane selection keeps things balanced. But Knitting won’t preempt a long-running task — if one call takes forever, that lane is busy until it’s done.
Functions aren’t payloads: you can’t send a function to a worker. Knitting catches that early and fails fast before dispatch.
Worker APIs are great for message passing. Knitting is for the cases where you want a
function-call API and lower coordination overhead.
Instead of building message protocols, you write function calls. Instead of paying for runtime IPC on every hop, you coordinate through shared memory. Same API on Node, Deno, and Bun.
Call functions instead of routing messages.
Less IPC overhead, especially for high-frequency workloads.
One programming model across all three runtimes.
You control batching, inlining, and shutdown when you need to.