Performance, measured
10 ms blocks. About 3 ms per order. About 40 ms round trip. Every figure with its sample size, percentiles, origin and method.
An ephemeral rollup is described by three numbers: how often it makes a block, how long a transaction takes next to it, and how long a round trip takes over the network. Here are Interlude’s, each with its scope, because none of them holds everywhere. They come from audited runs on 2026-09-30, 12:55-13:30 and 17:12-17:34 UTC, on the build every production node has run since that day, which takes reads of Monad off the transaction path (the read-ahead build). Percentiles are nearest-rank; values are milliseconds as p50 / p95 / max unless a row says otherwise.
- BLOCKS · ON THE NODE
- 10 ms
- 99.0-100.0 blocks a second on both Tap books and on Kandle's Paris, New York and Tokyo nodes. The California, Singapore, São Paulo and Johannesburg nodes run 82.5-94.0 a second.
- ROWS 22
- PER ORDER · NEXT TO THE NODE
- ~3 ms
- A Tap order placed, cancelled or filled, send to receipt over a WebSocket, from a machine in the book's region: p50 2.4-4.1 ms by book and kind, 58-77 each. Signing excluded. The Paris book, with 77-106 resting orders, is the slower one. A Kandle tap takes 1.6-3.2 ms p50 on its seven tables.
- ROWS 10, 27, 8
- ROUND TRIP · OVER THE NETWORK
- ~40 ms
- Warm round trip, client to node, on IPv6 (the default): p50 38.0-38.8 ms over WebSocket, 42.0 over HTTPS. On IPv4: 34.0 and 36.1-38.0. Requests enter Fly at Frankfurt. Timed end to end over the WebSocket: a Tap order 41.6-41.7 ms p50 (place, cancel, fill), a Kandle tap 39.7.
- ROWS 4, 1, 2, 3, 13, 26, 12
What a call costs, and where the time goes
The node runs a transaction the moment it arrives and answers with the receipt and the return value in the same message (interlude_sendTransaction). The SDK sends it over a WebSocket it keeps open, so a call is one round trip; it falls back to HTTPS only after a lost WebSocket delivery, or where the runtime has no WebSocket (Node 20). The receipt is the node’s: Monad has the change at the node’s next commit, and a batch is final once its challenge window closes.
Next to the node, a call takes a few milliseconds. A Tap order placed, cancelled or filled takes 2.4-4.1 ms p50: a place 4.0 and a fill 4.1 on the Paris book, which held 77-106 resting orders, and 2.7 and 2.5 on the New York book. A crossing order fills inside the transaction that placed it, so the fill is in that transaction’s receipt. A GridBet call that reverts at once takes 2.3-3.0, and a Kandle tap 1.6-3.2 on its seven tables. What still reads Monad is the first transaction of a session, because slots keyed by an address are not read ahead: a join took 43.7-69.3 ms next to a Tap book, and 389.4-1387 on Kandle’s California, Singapore, São Paulo and Johannesburg tables, which sit 72-181 ms from Monad’s RPC.
Over the network, a round trip is about 40 ms: on IPv6, the default, 38.0-38.8 ms p50 over the SDK’s WebSocket and 42.0 over HTTPS; on IPv4, 34.0 and 36.1-38.0. Requests enter Fly at Frankfurt. Timed end to end, a Tap order took 41.7 ms p50 to place, 41.6 to cancel and 41.6 to fill, and a Kandle tap 39.7. A session’s first transaction costs more: a join on a Tap book took 83.6 ms p50. Across the Atlantic the network alone is about 110 ms. The first request on a cold connection costs 108.3-108.9 ms p50 on IPv6 (DNS cached, then TCP, TLS 1.3 and the request).
Next to Monad, not against it
A Monad transaction came back with its receipt in 305.2 ms p50 through Ankr and 369.5 through the public RPC, in one call (eth_sendRawTransactionSync). That receipt is Monad’s Proposed stage; Finalized came 858.5 ms p50 after the send. Monad testnet’s blocks are 303.8 ms apart on average. The two layers do different jobs: Interlude answers between blocks, and Monad settles, then finalizes, what Interlude commits. A round trip to an Interlude node and a Monad transaction are not the same measurement, so this page never divides one by the other.
Blocks
An Interlude node closes a block every 10 ms. Measured: 99.03 and 99.97 blocks a second on the Tap books in Paris and New York, and 99.04, 100.00 and 99.99 on Kandle’s Paris, New York and Tokyo nodes. Its California, Singapore, São Paulo and Johannesburg nodes run 82.5-94.0 a second. Why those four run slower is not established yet.
Kandle’s quick lock
Kandle prices a tap at the first Pyth tick stamped a set delay after its quoter saw the tap. In production, the lock is stored 400-646 ms p50 after the tap lands, by table (517 pooled over the seven, p95 1007), and the page sees it 462-673 ms p50 after sending, about 45 ms after it is stored, at its polling cadence. The quoter quoted every tap measured, and the page saw all but one inside its 1.5 s wait. When the quoter does not quote, the v3 fallback locks the tap at its reference print, and the page sees it after 2.8-3.9 s p50 by table.
What was measured
Kandle’s seven regional GridBet v3.1 tables, among them Paris 0x5B24…7480 at il2-eu-5b2476ebb7f253f3.fly.dev. Tap, the order book, in Paris (0x69b1…d5A6 at il2-eu-69b1ff7d02fb9fc4.fly.dev) and New York (0x1813…f83e). All on hub v3, Monad testnet.
The audited runs measured from a Mac, with client and node in the same region, and Fly machines in the nodes' regions (cdg, ewr, nrt, sjc, sin, gru and jnb). The round trips over the network come from the Mac, and the figures next to the node from the Fly machines. The Monad transactions come from an earlier run on the same Mac. The table gives each row’s origin.
The full table
- A Re-measured in the audited runs.
- R Reported by an earlier run, before the read-ahead build; method checked, not re-run.
- D Derived from other rows, not measured end to end.
01 · A
Warm HTTPS round trip, Kandle Paris node (il2-eu-5b24…), IPv6 (the default)
eth_call quote() 42.0 / 43.9 / 44.1 and 42.0 / 43.1 / 250.2 (two runs); eth_chainId 42.0 / 48.0 / 49.7 and 42.0 / 46.1 / 48.7. The auditor's re-run, eth_call: 41.9 / 46.1 / 53.9
- N
- 60 per series per run, plus 30
- FROM
- Mac, client and node in the same region
- METHOD
- curl 8.7.1, HTTP/2, one process: 61 sequential POSTs on one connection (--next), the first dropped. Only transfers with no new connection and status 200 are kept, timed by time_total. Every fly-request-id ends in fra
- NOTE
- The 250.2 is one excursion on the Kandle node; its cause is not established
02 · A
Warm HTTPS round trip, Tap Paris book (il2-eu-69b1…), IPv6
eth_call midE6() 42.0 / 44.1 / 46.1 and 42.0 / 44.6 / 56.0 (two runs); eth_chainId 42.0 / 44.0 / 44.6 and 42.0 / 44.1 / 45.3. The auditor's re-run, eth_call: 42.0 / 44.0 / 44.0
- N
- 60 per series per run, plus 30
- FROM
- Mac, client and node in the same region
- METHOD
- As row 1
03 · A
Warm HTTPS round trip over IPv4 (curl -4)
Kandle Paris: eth_call 38.0 / 39.8 / 700.8 and 37.8 / 38.1 / 39.1, eth_chainId 38.0 / 38.8 / 49.9 and 36.1 / 39.0 / 69.9. Tap Paris: eth_call 38.0 / 40.0 / 42.0 and 38.0 / 39.2 / 40.0, eth_chainId 38.0 / 40.5 / 54.1 and 38.0 / 39.2 / 41.9. The auditor's re-run: Kandle 36.1 / 38.2 / 42.3, Tap 38.0 / 39.9 / 40.0. p50 across every series: 36.1-38.0
- N
- 60 per series per run, plus 30
- FROM
- Mac, client and node in the same region
- METHOD
- As row 1, with -4
- NOTE
- curl's time_total on this Mac lands on even milliseconds, so curl p50s move in 2 ms steps
04 · A
Warm WebSocket round trip (the SDK's send transport), IPv6 (the default)
Kandle Paris: eth_call 38.5 / 40.2 / 41.9, eth_chainId 38.8 / 41.5 / 42.0. Tap Paris: eth_call 38.2 / 40.9 / 42.8, eth_chainId 38.0 / 40.6 / 43.0. The auditor's re-run: Kandle eth_call 38.1 / 40.2 / 41.1, eth_chainId 38.0 / 40.0 / 40.0; Tap eth_call 38.0 / 40.1 / 41.4, eth_chainId 38.1 / 40.1 / 40.3. Over IPv4: p50 34.0 on all four series (the auditor's 33.9-34.0)
- N
- 60 each, plus 30 each
- FROM
- Mac, client and node in the same region
- METHOD
- Node 20.20.2 WebSocket (undici), one socket per node, 5 unrecorded warm-ups per method, then sequential calls 50 ms apart, timed from send() to the matching message. IPv4 with --dns-result-order=ipv4first
- NOTE
- IPv6 costs about 4 ms more than IPv4, and a WebSocket is about 4 ms faster than curl. A browser run, earlier and not re-run: 40.0 / 41.9 / 61.5, n=60
05 · A
Cold first request (cached DNS, then TCP, TLS 1.3 and the request)
IPv6: p50 108.3-108.9, p95 113.1-115.4 over four series; pooled 108.6 / 114.1 / 1148.9. IPv4: p50 93.3-93.9, p95 97.0-98.2 over two series; pooled 93.7 / 97.8 / 102.4. At p50 on IPv6: DNS 1.9, TCP 29.4-30.1, TLS 34.1-34.3, the request 41.9-42.7. A WebSocket opens in 120.3 / 154.3 / 194.9 (Kandle) and 119.7 / 126.3 / 127.6 (Tap); open plus the first call, p50 158.8-159.6
- N
- 60 per series (240 on IPv6, 120 on IPv4); 60 WebSocket opens per node
- FROM
- Mac, client and node in the same region
- METHOD
- 60 separate curl processes 50 ms apart, --http2, a new connection on 60 of 60. WebSocket: a new socket each time, timed to open
- NOTE
- Not re-run by the auditor. Replaces the n=1 figures and the earlier run's quote
06 · A
Where requests enter Fly
Frankfurt (fra) on 1,591 of 1,591 requests from the Mac, WebSocket upgrades included, and 126 of 126 in the auditor's re-run; fra on 28 of 28 /health requests to the New York node. Fly machines enter at their own region (cdg, ewr or nrt), 60 of 60 per series
- N
- 1,591 + 126 + 28
- FROM
- Mac, client and node in the same region; Fly machines in cdg, ewr and nrt
- METHOD
- fly-request-id suffix
07 · A
CORS preflight on the node
OPTIONS answers 200 with an empty body on both Paris nodes, bare and browser-shaped: allow-origin, -methods and -headers *; access-control-max-age 7200; vary: origin, access-control-request-method, access-control-request-headers; via 2 fly.io, 2 fly.io. No Timing-Allow-Origin, on the preflight or on the POST
- N
- 6
- FROM
- Mac, client and node in the same region
- METHOD
- curl OPTIONS with Origin https://kandle.live
08 · A
Kandle tapAt, send to receipt, in region, WebSocket
All seven tables, 17:12-17:21 UTC: California 2.9 / 5.4 / 6.5, Singapore 3.0 / 5.5 / 7.5, São Paulo 1.6 / 2.3 / 4.2, Johannesburg 2.7 / 4.2 / 7.7, Paris 2.6 / 3.3 / 3.4, New York 2.4 / 6.1 / 7.0, Tokyo 3.0 / 4.8 / 7.4; pooled 2.7 / 4.8 / 7.7. The auditor's re-run: Paris 2.6 / 3.4 / 3.4, São Paulo 3.2 / 5.4 / 5.4. Earlier, 12:55-13:30 UTC: Paris 2.6 / 5.9 / 12.0 (runs 2.6 / 5.6 / 12.0, 2.4 / 5.9 / 6.4 and the auditor's 2.7 / 6.4 / 7.1), New York 2.8 / 4.7 / 12.4 (3.1 / 3.8 / 4.7 and 2.2 / 6.3 / 12.4), Tokyo 2.6 / 4.4 / 7.0 (2.1 / 2.7 / 5.3 and 2.8 / 4.9 / 7.0). The first tap of a fresh session, apart: 6.5-20.3 on Paris, New York, Tokyo and Singapore; California 81.8, São Paulo 148.7 (the auditor's 149.0), Johannesburg 186.5
- N
- 40 per table (280), plus the auditor's 10 + 10; earlier Paris 80 (30 + 30 + 20), New York 60, Tokyo 60; 16 first taps
- FROM
- Fly machine (shared-cpu-1x) in the table's region (cdg, ewr, nrt, sjc, sin, gru, jnb)
- METHOD
- One sequential series of tapAt on a square the board offers, over a raw WebSocket (the SDK's frame), 1 chip, minMult 0 (as the page sends it), 0.8-1.4 s apart, signing excluded, the first dropped. Every tap landed with a Tapped event, none rejected. From 17:12 the page's own reads (rows 14 and 15) ran beside the taps on other sockets
- NOTE
- No serial read of Monad on any node (/health), one batched read per series: the session's first tap. São Paulo's 1.6 comes from one window; the auditor's 10 taps there gave 3.2. The far tables' figures from before the read-ahead build, which timed the serial reads it removed, stay withdrawn
09 · A
A no-op GridBet transaction (join, which reverts AlreadySeated), in region, WebSocket
Paris 2.3 / 3.1 / 5.0, New York 3.0 / 4.0 / 6.0, Tokyo 2.4 / 3.2 / 10.9, pooled; p50 by series 2.0-3.3
- N
- 20 per series, the first kept: Paris 60, New York 40, Tokyo 40
- FROM
- Fly machine in the region
- METHOD
- As row 8, 150 ms apart
- NOTE
- São Paulo (14.1 in an earlier run) was not re-measured
10 · A
Tap order placed or cancelled, in region, WebSocket
Paris book: place 4.0 / 6.5 / 12.4, cancel 2.9 / 4.8 / 8.4 (p50 by series 4.0-4.1 and 2.9-3.6). New York book: place 2.7 / 3.3 / 6.5, cancel 2.4 / 4.0 / 6.1 (by series 2.1-2.8 and 1.9-2.7)
- N
- Paris 77, New York 58, of each kind, pooled over 2-3 series
- FROM
- Fly machine in the book's region (cdg or ewr)
- METHOD
- A bid of 10 at 0.50 rests and is cancelled at once, pairs 150-450 ms apart, over a raw WebSocket (the SDK's frame), signing excluded, the first dropped
- NOTE
- The Paris book held 77-106 resting orders, and a place used 0.89-1.16M gas; the New York book held only the probe's own orders, about 162k. Why Paris is slower is inferred from that, not proven; the node read nothing from Monad during any series. Orders over HTTPS were not re-measured (5.8-7.7 in an earlier run). Fills: rows 26 and 27. A session's first transaction: row 29
11 · R
Node to Monad's RPC (testnet-rpc.monad.xyz), round trip
Paris (cdg) 12.9 / 16.6 / 19.5, served from Frankfurt. p50 elsewhere: New York 8.4, Tokyo 4.8, California 72, Singapore 82, São Paulo 117, Johannesburg 181
- N
- 30 per region
- FROM
- Fly machine in each region
- METHOD
- eth_getStorageAt at an old block, 150 ms apart
- NOTE
- Not re-measured. The read-ahead build added Ankr as a second read endpoint, so this row covers one of the node's two
12 · A
Kandle tap over the network, send to receipt
39.7 / 41.7 / 45.5 (two series: 39.5 / 41.6 / 41.7 and 40.9 / 43.4 / 45.5). The same-socket eth_chainId just before each tap: 39.5 / 40.8 / 43.7. Apart: the first tap of a session, 51.3 and 51.2
- N
- 50 (30 + 20), 5 minutes apart
- FROM
- Mac, client and node in the same region, IPv6
- METHOD
- Node 20.20.2 WebSocket (undici), one send socket. withSession(tapAt(…)), 1 chip, minMult 0, signed by a session key before the clock starts; send() to the receipt message; taps 0.8-1.4 s apart, the first dropped. 50 of 50 landed with a Tapped event
- NOTE
- Was a prediction of about 80. The auditor's runs only. The table's keeper was paused, so the page's own fallback marks, one a second, kept the tape live
13 · A
Tap order placed or cancelled, over the network, send to receipt
Place 41.7 / 45.3 / 95.0 (series 41.5 / 44.7 / 45.2 and 42.4 / 46.7 / 95.0); cancel 41.6 / 44.2 / 109.4 (41.5 / 43.4 / 44.9 and 41.6 / 44.2 / 109.4). The same-socket eth_chainId just before each send: p50 38.1-39.6
- N
- 78 of each, warm (two series of 40)
- FROM
- Mac, client and node in the same region (the Paris book), IPv6
- METHOD
- The SDK's send router (interlude_sendTransaction over a viem WebSocket), timed at ws.send and at the message event, signing excluded; one transaction in flight, 150-450 ms apart, the first dropped. The SDK's own send(), signing included, adds about 0.7
- NOTE
- Was a prediction of about 41-45. The 95-109 maxima fall in 13:06:08-22, when same-socket probes read 60-136 too: the Mac's side
14 · A
Kandle's quick lock: lock stored after the tap landed
Paris 400.4 / 973.8 / 1120.4, New York 447.8 / 840.7 / 992.8, California 494.6 / 646.6 / 983.6, Singapore 515.8 / 776.0 / 1007.2, São Paulo 543.9 / 922.2 / 1266.5, Johannesburg 581.0 / 1125.0 / 1365.1, Tokyo 646.1 / 1225.9 / 1607.4; pooled 517.3 / 1007.2 / 1607.4. The auditor's re-run: Paris 441.4 / 590.6 / 590.6, São Paulo 586.9 / 984.3 / 984.3
- N
- 41 per table (287), plus the auditor's 11 + 11
- FROM
- Fly machine (shared-cpu-1x) in the table's region
- METHOD
- The taps of row 8. Tapped push to Locked push, both arriving on one interlude_subscribe("applied") socket (one clock). The auditor also read priceOf every 25 ms on its own socket: the last read that found no lock was sent 13-18 ms p50 before the Locked push, the first that found it 8-12 ms p50 after
- NOTE
- Every lock was the quoter's: 287 of 287 taps and 22 of 22 quoted. Timed from the send instead: pooled 521.9 / 1011.2 / 1612.0. The lock was below the multiplier at execution on 157 of the 287 and equal to it on 130. One window per table, n=41 each: the Tokyo and Johannesburg tails come from one series
15 · A
Kandle's quick lock: seen by the page, at the page's poll cadence
From the send: Paris 462.0 / 995.2 / 1129.5, New York 504.9 / 920.8 / 1039.2, California 550.8 / 766.0 / 1034.9, Singapore 578.6 / 853.9 / 1075.6, São Paulo 599.6 / 985.6 / 1360.7, Johannesburg 636.7 / 1311.5 / 1380.0, Tokyo 672.7 / 1325.6 / 2266.7; pooled 575.4 / 1046.2 / 2266.7. After the lock was stored: 44.8 / 94.4 / 654.7. The auditor's re-run: Paris 501.2 / 690.1 / 690.1, São Paulo 687.5 / 1006.7 / 1006.7
- N
- 41 per table (287), plus the auditor's 11 + 11
- FROM
- Fly machine (shared-cpu-1x) in the table's region
- METHOD
- The page's own code (lib/quote.ts, lib/keeper.ts), wired as useBoard and useBeat wire it, on its own read socket: after the receipt a tapOf read, then priceOf every 100 ms, one round in flight, for 1.5 s; after that the one-second beat, which reads priceOf once the tape has reached the tap's reference second. Its heartbeat woke the keeper every 10 s. Timed from the send
- NOTE
- 286 of 287 were seen inside the 1.5 s wait, and 22 of 22 in the auditor's. The other, a Tokyo tap, was quoted 1607.4 after it landed (the quoter's counters show one Tokyo tap found by its once-a-second poll, not pushed): its tile said "locking…", and the beat showed the lock at 2266.7, just after the reference print. The page's reads were made next to the node; from a browser, each also crosses the network (row 4)
16 · A
The quoter's own counters (kandle-feed /quoter/<region>)
tapToQuote p50 / p99 after each table's taps: New York 469 / 1099, Singapore 485 / 966, California 527 / 969, São Paulo 561 / 1362, Tokyo 640 / 1360, Johannesburg 646 / 1600; Paris 468 / 1600, 40 earlier taps included. tickAfterSeen p50 101-231. Its own lockQuoted send, per tap of this window: Paris 24 / 563 / 955 (min 6); p50 113-387 on the other tables, max 1205
- N
- 41 per table, Paris 81; 287 sends
- FROM
- kandle-feed, in Paris (cdg), which quotes every table
- METHOD
- Public GET /quoter/<region> before and 12 s after each table's taps; counters since the quoter's restart at about 13:32 UTC. tapToQuote runs from when the quoter saw the tap to its lockQuoted receipt, tickAfterSeen from then to Pyth's stamp on the tick (two clocks). The sends are its recent list, which covered every tap
- NOTE
- Every table: ok and active, socket live, 41 of 41 quoted, none skipped, dropped or refused, no stalls. Its own round trip to each node (rttMs) read 23-406 from one read to the next. Paris's counters include 36 taps from 13:32-17:10 UTC and 4 from a smoke test
17 · A
Kandle's lock when the quoter does not quote (the v3 fallback)
Seen by the page, from the send: Singapore 2835.4 / 4113.4 / 4329.9, São Paulo 3089.2 / 4078.3 / 4560.7, California 3333.0 / 4285.1 / 4574.5, New York 3444.5 / 4268.8 / 5153.4, Paris 3484.6 / 4735.0 / 5049.4, Johannesburg 3780.8 / 5188.1 / 5309.8, Tokyo 3894.0 / 5066.1 / 5664.5; pooled 3423.9 / 4805.4 / 5664.5. A v3.1 page whose quote never comes: pooled 3419.7 / 4804.9 / 5663.2. The reference print, after the tap landed: 2879.2 / 4352.3 / 5657.6. The auditor's re-run (v3 page): Paris 3062.6 / 4628.9 / 4628.9, São Paulo 2862.5 / 4970.0 / 4970.0
- N
- 41 per table (287), plus the auditor's 11 + 11
- FROM
- Fly machine (shared-cpu-1x) in the table's region
- METHOD
- No tap went unquoted, so the fallback was timed on the taps of row 14 by two page models on their own socket, running the page's code: a v3 page ("locking…" from the start; the beat reads priceOf once the tape reaches the tap's reference second) and a v3.1 page whose quote never comes (1.5 s reading lastTime against that second, then the beat). The reference print: Tapped push to the first Marked push at or past that second
- NOTE
- A model of the fallback, not a quoter switched off. The page marked as a page does when the tape was more than 2 s behind (28 marks, 10 on Johannesburg), so the keeper's lag on the far tables is in these times
18 · R
A Monad transaction, send to receipt in one call (eth_sendRawTransactionSync)
Ankr 305.2 / 435.3 / 508.1; public RPC 369.5 / 501.0 / 523.8
- N
- 30 each
- FROM
- Mac, Paris
- METHOD
- A zero-value self-transfer, 21k gas, signed before the clock starts
- NOTE
- Not re-run in the audited run. The receipt comes at Monad's Proposed stage
19 · R
A Monad transaction, send then poll for the receipt
Public RPC 346.4 / 559.0 / 637.9; Ankr 430.4 / 605.1 / 923.4; all four paths pooled 364 / 562 / 923
- N
- 30 each (120 pooled)
- FROM
- Mac, Paris
- METHOD
- Poll every 80 ms, so the time found is an upper bound
20 · R
Monad: Finalized, after the send
858.5 / 1033.6 / 1831.9
- N
- 120
- FROM
- Mac, Paris
- METHOD
- monadNewHeads WebSocket events matched to the receipt's blockHash
21 · R
Monad block time
303.8 mean over the last 10,000 blocks; Proposed spacing 303.3 p50
- N
- 10,000 / 847
- FROM
- Ankr / Mac, Paris
- METHOD
- Chain timestamps; WebSocket arrival times
22 · A
Interlude blocks per second
Kandle: Paris 99.04 (/health 98.99), New York 100.00, Tokyo 99.99; California 93.98 (94.03), Singapore 92.12, São Paulo 86.44, Johannesburg 82.64 (82.97). Tap: Paris 99.03 (99.06), New York 99.97 (100.02)
- N
- One 60 s window per node: 592-596 samples on the Paris nodes, 214-536 on the far ones, because calls were sequential over one WebSocket; 218-596 in the later window
- FROM
- Mac, and each node's /health
- METHOD
- Least-squares slope of eth_blockNumber against each call's send/receive midpoint; in parentheses, the ephemeralBlock delta on /health over the same window
- NOTE
- A second window on Kandle Paris, 13:02:53-13:05:53: 98.84. A later window on every node, 17:22:42-17:23:44 UTC: Kandle Paris 99.06, New York 99.96, Tokyo 100.00, California 93.74, Singapore 91.69, São Paulo 88.98, Johannesburg 82.50; Tap Paris 99.39, New York 100.00. During each Kandle table's taps (row 8), /health alone over 52-60 s: Paris 98.87, New York 99.95, Tokyo 99.79, California 92.93, Singapore 91.05, São Paulo 84.83, Johannesburg 79.96
23 · R
The engine on a laptop (not production)
interlude_sendTransaction 228 µs p50, 508 µs p95; eth_chainId 57 µs; about 3.1k tx/s for one client; a plateau at 2.8-3.1k tx/s
- N
- 1,500
- FROM
- Mac loopback (M5 Pro) against anvil
- METHOD
- Release build of commit 772e66e
- NOTE
- Production throughput has never been measured
24 · R
Commit costs on Monad, at 102 gwei
A heartbeat: 153,759 gas = 0.0157 MON, about every 906 s. An awake Kandle table: 38.66 MON an hour. One player at about 5 taps a minute: 54.1 MON an hour. All in, about 0.046 MON per tap; the quick lock, about 0.0012 MON per tap
- N
- 1,093 / 1,440 / 77 / 65 / 64
- FROM
- Ankr, on chain
- METHOD
- Receipts and balance deltas
25 · R
What the measurement runs cost the validator (0xa375…eF43)
326.80 MON at about 07:31 UTC, against 393.65 at 06:20: about 67 MON spent by the runs' own live tests
- N
- 1
- FROM
- Ankr
- METHOD
- eth_getBalance
- NOTE
- Read in the morning's audited run. What this run cost was not measured in MON
26 · A
Tap fill over the network, send to receipt
41.6 / 45.4 / 99.3. The SDK path, two series: 41.5 / 43.6 / 44.5 and 43.5 / 45.4 / 99.3 (together 42.0 / 45.4 / 99.3). An independent raw-WebSocket client, the auditor's, filling against other makers' orders: 41.5 / 45.4 / 47.1. The same-socket network: p50 38.8-39.3
- N
- 98 warm (78 + 20)
- FROM
- Mac, client and node in the same region (the Paris book), IPv6
- METHOD
- Market buys, market sells and limit orders at the best opposite price, in turn, 2 units at the top of a book of 90-110 resting orders; timed as row 13, signing excluded, the first dropped. 101 of 101 filled, each with exactly one Filled event
- NOTE
- The fill is in the receipt of the order that crossed, so it takes about what a place or cancel takes (row 13)
27 · A
Tap fill in region: a crossing order that fills inside its own placing transaction
Paris book, against the probe's own second seat: 4.1 / 8.2 / 16.9 (runs 3.9 / 6.6 / 11.0, 4.1 / 11.7 / 16.9 and the auditor's 4.2 / 8.2 / 8.2). Paris, against other makers' resting orders: 3.9 / 6.5 / 6.5. New York book, against its own maker: 2.5 / 4.8 / 15.0
- N
- Paris 77 (29 + 29 + 19) and 19; New York 58; warm
- FROM
- Fly machine in the book's region (cdg or ewr)
- METHOD
- As row 8, sent over the WebSocket. 170 of 170 filled
28 · A
Tap fill to the New York book, from across the Atlantic
117.3 / 120.1 / 122.7 (the clean series) and 115.6 / 416.3 / 417.5. The same-socket network: p50 109.9-111.8
- N
- 40 each
- FROM
- The Mac of row 26, across the Atlantic from the book; requests enter Fly at fra
- METHOD
- As row 26. 80 of 80 filled
- NOTE
- One lens only. The second series' 7 samples at 414-418 came in two bursts, in the same seconds as spikes on the Mac's probes to the Paris nodes: the Mac's first hop, not the path or the node
29 · A
First transaction of a new session (join)
Over the network: Tap 83.6 / 85.4 / 85.5 against 39.0 of network, so about 45 on the node (one more, the auditor's: 99.0); Kandle 89.2 and 93.5. In region: Tap 48.2 / 69.3 / 69.3 (43.7-69.3), Kandle 18.1-80.6. Kandle's later window, one join per table: Paris 65.9, New York 47.1, Tokyo 40.4, California 442.7, Singapore 754.9, São Paulo 991.8, Johannesburg 1387; the auditor's Paris 53.9, São Paulo 389.4
- N
- Over the network 21 + 1 (Tap) and 2 (Kandle); in region 11 (Tap) and 7 + 7 + 2 (Kandle)
- FROM
- Mac, client and node in the same region; Fly machine in the node's region
- METHOD
- The first send of each fresh session key, timed as rows 26 and 8
- NOTE
- In the earlier window every join made exactly 2 batched reads of Monad (4 slots read ahead, 4 used, 0 serial): slots keyed by an address are not read ahead. The later joins were not broken down by read; the four far tables sit 72-181 ms from Monad's RPC (row 11)
30 · A
Warm read round trip in region
WebSocket eth_call: p50 1.3-2.4 (the auditor's, Paris: 1.7 on Kandle, 1.9 on Tap). HTTPS keep-alive eth_call: p50 2.3-6.3 (the auditor's 3.5 and 4.1)
- N
- 60 per run; the auditor's 30
- FROM
- Fly machine in the node's region
- METHOD
- As rows 1 and 4, from the machine. The edge is the machine's own region on every request
- NOTE
- One Kandle sample in Paris took 896.9 (the next largest 7.8); its cause is unknown
Caveats the audit found
- 01
IPv4 and IPv6 are different numbers
In the same minutes, the same node answers a WebSocket call in 38.0 ms over IPv6 and 34.0 over IPv4, and an HTTPS call in 42.0 and 36.1-38.0. Both are right for their address family; compare within one.
- 02
A read round trip is not a transaction
The ~40 ms is the network. Next to the node, a Tap order adds about 2-4 ms and a Kandle tap about 3 (rows 8, 10 and 27). Setting a read round trip against a Monad transaction compares different things.
- 03
The SDK's send path rarely pays a CORS preflight
A preflighted HTTPS POST costs about two round trips, but the SDK sends over a persistent WebSocket by default and falls back to HTTP only after a lost WebSocket delivery, or where no WebSocket exists (Node 20). Over HTTP, the node now lets a browser keep the preflight for 7200 s, so only the first preflighted POST in that window pays the extra round trip.
- 04
The Mac's first hop is in the tails
Every p95 and max timed from the Mac depends on its Wi-Fi. A noisy window was measured and set aside; the table has the clean re-run. The 95-109 and 414-418 ms maxima of rows 13 and 28 fell in seconds when the Mac's other probes spiked too. Every order and tap from the Mac followed a read on the same socket, so these are warm figures: a page that sends after an idle gap may pay a Wi-Fi wake-up, which was not measured.
- 05
Time on the node comes from the in-region rows
Timing from the Mac is coarse: curl's clock lands on even milliseconds, and WebSocket times cluster. Subtracting the network from a Mac sample cannot resolve time on the node below about 2 ms, so the node's time is taken from rows 8, 10 and 27.
- 06
Far tables pay for a session's first reads
Warm, a Kandle tap takes the same few milliseconds on all seven tables (row 8). A session's first transactions still read Monad, and the California, Singapore, São Paulo and Johannesburg tables sit 72-181 ms from its RPC (row 11): there a join took 389.4-1387 ms and the first tap 6.5-186.5 (rows 29 and 8). These are single samples, one per table and run.
- 07
Unexplained tails on the Kandle node
5 of 1,020 samples from the Mac took 243-1149 ms, and one in region took 896.9. The node's lock wait stayed at most 0.06 ms and it made no serial read of Monad, so the cause is not established.
- 08
Survivorship in the lock figures
Rows 14 and 15 count only taps the quoter quoted, which was every tap: 287 of 287 on the seven tables and 22 of 22 in the auditor's re-run, each followed until its lock was seen, with no cap. 286 of the 287 were seen inside the page's 1.5 s wait. Since no tap went unquoted, row 17 comes from page models on the same taps.
- 09
A parallel stress run is not a baseline
One run tapped every region at once and overloaded the one shared keeper. It is left out of the table, and the later window tapped the tables one after another.
- 10
The quoter's counters mix periods
They start at the quoter's own 'seen' time, not at the tap's landing, and end at its lockQuoted receipt. Paris's include 40 taps from before the later window; the other tables' are that window's 41 each.
- 11
Two clocks
'Tick stamped after the tap landed' compares Pyth's clock with a Fly machine's clock, so it carries NTP-level error. Rows 14, 15 and 17 are each timed on one machine's clock.
- 12
No single cold sample
A cold first request measured once is not quoted; row 5's n=60 series are.
What this page does not claim
- A Kandle tap, its lock or a Tap order timed from a browser. They were timed over the network from a Node client (rows 12, 13 and 26) or next to the node (rows 14-17); no browser figure was re-run.
- ~40 ms from anywhere. It is the round trip with client and node in the same region; across the Atlantic the network alone measured about 110 ms (row 28).
- 10 ms blocks on every node. They hold on the Paris, New York and Tokyo nodes and on both Tap books, not in California (93.7-94.0 a second), Singapore (91.7-92.1), São Paulo (86.4-89.0) or Johannesburg (82.5-83.0).
- A Monad receipt as final. The receipt comes at the Proposed stage (305-370 ms p50); Finalized comes at about 860 ms p50; blocks are about 304 ms apart.
- Interlude's receipt as settlement. It is the node's receipt, before the commit; Monad has the change at the next commit.
- The laptop engine figures (row 23) as production latency or throughput. Production throughput has not been measured.
- A cause for the read outliers, the slow far-region blocks or kandle-feed's slowness. The symptoms are measured; the causes are hypotheses.
- Per-visit or per-day cost totals. They are models built on row 24, not measurements.
The same table is docs/PERFORMANCE.md in the repository. The trust side of the story is on What is live: v1 is permissioned, and Interlude holds every seat on the bench.
