Experiment 285: The worker that never sleeps

Date: 2026-09-08

Status: Rejected

Category: Moonshot

Direction:stream-rerun-dispatch, result-transfer-shape

Benchmark Run: none (rejected; no runtime code ships) — focused evidence in

benchmark/results/2026-09-08T07-20-00Z-exp285-worker-keep-warm.md

Archive:archive/exp-285

Problem

Exp 284 measured the write hop instead of guessing at it, and the answer

closed nine experiments at once: of the 6.28 µs between await db.execute()

and the identical insert run inline, 5.30 µs is the isolate boundary and

0.98 µs is everything resqlite does (claim 284.1). Its rejection came with an

explicit reopen condition — a candidate that attacks the boundary rather

than the bookkeeping.

Inside that boundary it left one number unexplained. The same executeWrite

call, same statement, same parameters, same handle, costs 7.612 µs on a

running isolate and 9.380 µs on a worker that was parked until the message

arrived (claim 284.3). About 1.8 µs of every write is the woken side running

cold, and nobody had asked whether it has to be.

Hypothesis

Assumption challenged: an idle worker isolate should idle. Every resqlite

worker — the writer and each reader — handles its message, returns to its

event loop, and lets the VM park its thread until the next one. That is the

obvious thing to do and no experiment has ever questioned it. If the 1.8 µs is

the VM releasing and re-acquiring the worker's thread, then a worker that

never runs out of work never pays it, and the boundary is partly a scheduling

artifact rather than a transport cost.

The frontier this attacks is the one exp 284 named. It changes neither what

rides on the message nor who runs SQLite — only the state of the receiver when

the message lands. Exps 271 and 279 were rejected attacking the same boundary

from the completion side and the transport side; neither had this mechanism.

The risk budget allowed for the prototype, stated before any code: a worker

that stays runnable burns a core, which is a real cost in a library that ships

to phones. A win had to be priced in CPU as well as wall time.

Approach

Dart gives a library exactly one way to keep an isolate from going idle: leave

something in its event loop. Microtasks are disqualified — a microtask loop

starves the receive port, so the request it is waiting for is never delivered.

That leaves two primitives, and the harness measures both.

benchmark/experiments/worker_keep_warm.dart extends exp 284's floor writer —

a hand-rolled isolate writer with none of resqlite's machinery, so the isolate

architecture is priced on its own — with four lanes:

window after each request.

token to its own receive port. Tokens and requests share one FIFO queue, so

a request arriving mid-spin waits behind at most one token turn.

isolate, which parks awaiting the reply exactly as the worker parks awaiting

the request. Two parked sides means two wakes per write, so this is the lane

that prices the whole boundary as scheduling.

Each worker times its own executeWrite calls, so the cold tax is read inside

the C call rather than inferred from a round trip. Process CPU is read through

getrusage around every block, because the entire question for a keep-warm

lane is what the wall-clock saving costs in burnt core.

The shippable half was then built into the real writer isolate — a token loop

on writerEntrypoint's own receive port, with the window handed in through

the spawn arguments — and A/B'd through await db.execute(...). That lane

needed a temporary window field on Writer, so it lives at archive/exp-285

rather than in the retained harness.

Results

Full tables in the receipt.

The cold tax is real, and it is recoverable

The identical C call on a worker that never stopped being runnable, medians of

three passes over 6,400 writes each:

workerexecuteWrite µsΔ
cold9.606—
self8.974−6.6%
self (inside both)8.724−9.2%

So the answer to the question is yes: 0.63–0.88 µs of what a parked worker

spends inside SQLite is thread residency, and keeping the isolate scheduled

gets it back. That is smaller than exp 284's 1.8 µs, which was measured

against a single alternating pair rather than a continuously running lane, and

claim 285.1 refines it accordingly.

Recovering it costs more than it is worth

laneµs/writeΔ vs coldCPU µs/writeΔ CPU
cold12.970—14.76—
timer16.218+25.0%23.35+58%
self13.710+5.7%16.69+13%
both12.078−6.9%21.80+48%

Warming the worker alone is a net loss at every window tested (5, 15, 40 and

200 µs) and in four independent passes. The tax it recovers inside SQLite is

smaller than the delivery latency it adds: a request that arrives mid-spin

waits behind a token turn, and the spinning worker contends for the core the

caller needs. timer loses much worse because its turns cost 6.50 µs against

the self-sent message's 1.85 µs.

The same loop inside resqlite's real writer agrees, and by a wider margin —

+9.6% at a 15 µs window, +9.0% at 40 µs, +6.2% in a third collection, with

CPU up 13–17%. A control at a 5 µs window, where the round trip outlasts the

window so the loop exits on its first turn and never engages (spins=0),

reads flat: 14.567 against 14.523. The cost and the tax both come from the

loop actually running.

The win is real and it belongs to the caller

both is the only lane that beats cold, and it beats it by 6.9% (12.0% in

the fourth pass). Against the inline reference it cuts the isolate boundary

from 5.117 µs to 4.225 µs — 17% of the thing claim 284.1 prices at 5.30 µs.

The boundary really is substantially scheduling rather than transport.

But the half that wins is the caller's, and resqlite may not have it. The

calling isolate in a Flutter application is the UI isolate. A token loop in

its event loop is a continuous stream of events between the framework and its

frames — precisely the main-isolate blocking exp 280 rejected a candidate for

(claim 280.2), in a library whose contract is that database work does not touch

the UI isolate. There is no version of this that ships.

The corollary is the useful part

cold's caller parks between writes because the benchmark gives it nothing

else to do, and about a sixth of the boundary it pays is its own wake. A real

application's isolate has frames, gestures and futures pending; it is closer

to both's caller than to cold's.

That is a caveat on the one public row where resqlite loses to a peer. The

release suite's Single Inserts (100 sequential) lane awaits each write in

turn and does nothing in between, so it charges resqlite for a caller-side

wake that an application issuing the same hundred writes from a live isolate

would not pay. The row is a fair worst case; it is not the typical one.

Decision

Rejected. The mechanism works — a worker kept runnable executes SQLite

6.6% faster, and a boundary with neither side parked is 17% cheaper — and no

form of it can be shipped. Worker-side keep-warm, the only half a database

library controls, is 6–10% slower and 13–17% more expensive in CPU in the

shipping path, reproduced across three windows and nine passes. The half that

wins requires spinning the application's own isolate.

Runtime reverted; prototype and the e2e lane at archive/exp-285; the four

floor lanes retained as benchmark/experiments/worker_keep_warm.dart.

Would reopen if the VM offers a way to keep an isolate's thread hot

without keeping the isolate runnable — a spin-before-park window in the

isolate scheduler, or thread affinity for a long-lived isolate. That is a Dart

SDK-shaped change, not a library-shaped one, and this run is the evidence for

what it would be worth: roughly 0.9 µs per request on the worker side, plus

whatever the caller's wake costs an application that is genuinely idle. Short

of that, the boundary is not collectible from inside a package.

Do not reopen by making the window adaptive, the token cheaper, or the

loop smarter. The window sweep already covers 5 µs to 200 µs and the shape is

flat: self is above cold everywhere. The self-sent port message is already

the cheapest keep-alive Dart exposes at 1.85 µs a turn, and the tax it is

buying back is 0.9 µs.

Future Notes

proposal about the isolate boundary should run it first, because it

separates the boundary's two wakes and shows how much of a benchmark's hop

is the benchmark's own idle caller.

turn is 1.85 µs on a busy isolate and 0.65 µs on an idle one, a

zero-duration Timer.run turn is 6.50 µs. Anything that proposes to keep a

Dart event loop occupied is paying one of those.

Database.open. The e2e lane's first result was inverted by assuming

otherwise, and the two lanes' spawn order is only visible with a print

inside the entrypoint. Worth knowing for any future A/B that configures a

worker at spawn time.