ztreamer vs lightwalletd
Sync speed, serving and resource use of ztreamer direct (embedded zakura), ztreamer remote (default snapshot, then a remote zakura) and lightwalletd. All three ran on one machine, one after the other, against the same zakura state.
Summary
ztreamer reaches a serving index of the whole chain in 2.4 minutes when it runs zakura in process, and in 5.7 minutes in remote mode from the published snapshot. lightwalletd took 1 hour 44 minutes to build its cache from the same node. Once synced, ztreamer serves compact blocks 3 to 22 times faster than lightwalletd, depending on the request, with a lower tail latency at every concurrency level, and it uses about a tenth of the CPU for each block it serves. Its index is 40 % smaller on disk than the cache of lightwalletd. The two ztreamer modes serve at the same speed. One defect showed up under load: with 256 concurrent connections ztreamerd fails some requests with MDB_READERS_FULL.
- Sync: direct 146 s (44 s to open the embedded zakura, then the historical pass indexed 3,568,258 blocks in 102 s, about 35,000 blocks/s); remote 339 s (an 18.2 GB import of 409 epochs, attested by the node, then a 30 ms tail); lightwalletd 6,218 s. Direct is 42 times and remote 18 times faster than lightwalletd.
- Sync cost: direct used 502 CPU seconds; remote 4,500 (it verifies the imported ranges on about 24 cores); lightwalletd and its node 11,484, of which the node took 9,000 to answer one
getblockat a time. - Wallet download (every compact block from Sapling activation, 3,145,415 blocks, 17.9 GB of protobuf): 22.6 s on both ztreamer modes, 65.2 s on lightwalletd. ztreamer used 4 to 5 CPU seconds for it, lightwalletd 87.
- Ranges: the median 10,000-block GetBlockRange takes 4.8 ms on ztreamer and 107 ms on lightwalletd (22 times); the median 10-block range 37 µs and 232 µs.
- Concurrency: with 10,000-block requests ztreamer levels off at 1.06 to 1.13 M blocks/s from 16 clients; lightwalletd at 0.27 to 0.30 M. At 256 clients the p99 is 4.7 to 5.0 s against 29.6 s. With 100-block requests ztreamer serves 1.0 M blocks/s with a 61 ms p99 at 256 clients; lightwalletd 0.24 M blocks/s with an 814 ms p99.
- Unary RPCs (ghz): ztreamer answers GetLatestBlock and GetLightdInfo at 170,000 to 180,000 requests/s, lightwalletd at 27,000 to 43,000, because lightwalletd asks the node on each call. At 256 workers the zakura RPC server refuses part of those calls with HTTP 429, which lightwalletd passes on.
- GetTreeState at a random height is slow on every server, 170 to 260 ms median, because the tree state comes from zakura's
z_gettreestate. Under load none of the servers exceeds about 80 successful requests/s. - Efficiency: in the concurrency workloads ztreamer served 150,000 to 190,000 blocks per CPU second of server and node, lightwalletd 16,000 to 17,000.
- Memory: the anonymous memory of ztreamerd is 0.4 to 1.2 GiB under load (direct includes the embedded node). Its RSS reaches 17 to 20 GiB because the index is memory-mapped; those are file-backed pages of the page cache, which the kernel can reclaim. lightwalletd uses 0.5 GiB and its node 0.8 GiB.
- Disk: the ztreamer index is 18.7 GB / 17.4 GiB (direct) and 18.5 GB / 17.2 GiB (remote); the lightwalletd cache is 31.1 GB / 29.0 GiB. All three servers also need the zakura state (285 GB here).
- Restart on a finished index: lightwalletd 1.1 s, ztreamer remote 3.1 s, ztreamer direct 47 s (the embedded zakura opens its state first).
- Defect, MDB_READERS_FULL: once 256 ghz workers ran on 256 connections, ztreamerd answered part of the requests with
Internal: MDB_READERS_FULL: Environment maxreaders limit reached, in both modes, and the errors went on in later scenarios with fewer workers. The LMDB environment opens with the default of 126 reader slots (crates/index/src/index/mod.rs, nomax_readers), and with thread-local read transactions each blocking thread holds a slot; the tokio blocking pool can grow to 512 threads. The native suite, at 256 clients with one request each, saw no errors. Fix before the release: raisemax_readersabove the blocking-pool limit, or open the environment without TLS, or cap the blocking threads. - Rare Unavailable: a few GetSubtreeRoots requests failed with
serving snapshot generation N does not match LMDB generation N+1while a new block was written.
Setup
| Value | |
|---|---|
| CPU | AMD Ryzen 9 9950X3D · 16 cores / 32 threads · 128 MiB L3 |
| Memory | 60.4 GiB RAM · 32 GiB swap |
| Disk | WD_BLACK SN850X 2 TB NVMe · ext4 · index, cache and zakura state on the same disk |
| OS | Manjaro Linux · Linux 6.12.112 · x86_64 |
| CPU governor | powersave |
| ztreamer | ztreamerd 0.1.0 · commit af04e99868 · maxperf profile |
| zakura | zakurad 1.8.4+g618e972e5523 (standalone node), the same version embedded in ztreamerd |
| lightwalletd | commit 15456b9043 · Go build, default flags |
| Chain | zakura mainnet state from the archive, tip 3,564,614, synced to the live tip before each run |
| Serving interval | 419,200 to 3,564,614, fixed for every server |
| Clients | native Rust/Tonic serving suite of this repository · ghz v0.120.0 · loopback, plaintext HTTP/2 |
Each server ran alone on the host, in this order: ztreamer direct, ztreamer remote, lightwalletd. Every run started from an empty index. The zakura state came from the same archive and was brought to the live tip before each run, so no server timed the catch-up of its node. All three servers then served the same fixed interval, with the same seeded request sequences.
- ztreamer direct:
ztreamerd --zakura-config. The zakura node runs in the ztreamerd process and the index reads its state directly. - ztreamer remote:
ztreamerd --remote-rpcagainst a standalone zakurad. It imports the sealed ranges of the default snapshot source,https://snapshot.ztreamer.sh/mainnet, has the node attest them, and then reads the tail over JSON-RPC. - lightwalletd: against the same standalone zakurad over JSON-RPC, with
--no-tls-very-insecureand default limits. It builds its compact-block cache from genesis.
One run of each server. The node followed the live chain, so the tip moved a few blocks between runs; every historical request was in the fixed interval. The client shared the host with the server. lightwalletd built its cache from genesis, as does ztreamer. Remote mode depends on the download speed of the snapshot source; here the import moved 18.2 GB in 336 s, about 54 MB/s including verification. Throughput of ghz for streaming RPCs is limited by ghz itself (about 1,750 100-block ranges/s for both ztreamer modes); the native suite gives the server limits.
Sync speed
Time from the start of the server process until its index has the tip of the synced node and its gRPC listener accepts connections.
| Server | Time to tip | Tip | Blocks / s | CPU s | Mean cores | Peak RSS | Disk read | Disk written |
|---|---|---|---|---|---|---|---|---|
| ztreamer direct | 2.4 min | 3,569,248 | 24,410 | 501 | 3.4 | 2.27 GiB | 377.11 GiB | 26.59 GiB |
| ztreamer remote | 5.7 min | 3,569,344 | 10,526 | 4,500 | 13.3 | 4.20 GiB | 10.66 GiB | 35.29 GiB |
| lightwalletd | 103.6 min | 3,569,672 | 574 | 11,484 | 1.8 | 1.23 GiB | 256.21 GiB | 30.45 GiB |
In direct mode ztreamerd reads blocks straight from the zakura state with eight workers, at 1.5 to 6 GB/s from disk. The slow band between heights 1.7 M and 2.1 M is the sandblasting period, whose blocks are large. In remote mode the import fetches the sealed ranges of the snapshot and checks them: the node attests the block hash at the end of each range and the tree roots of each epoch. lightwalletd asks the node for one block at a time over JSON-RPC, so its rate follows block size: about 1,500 blocks/s at the start, 65 blocks/s in the sandblasting period. After the first sync, ztreamerd in direct mode filled the range metadata of the 3,569 sealed ranges for its Replicator. That took 6 minutes 41 s after it began to serve, and raised its RSS by mapping the index. In remote mode the snapshot brought that metadata, so the pass found nothing to do.
| Server | Milestone | After | Height |
|---|---|---|---|
| ztreamer direct | historical pass complete | 2.4 min | 3,568,258 |
| ztreamer direct | gRPC listener open | 2.4 min | – |
| ztreamer remote | import planned | 2.0 s | – |
| ztreamer remote | import complete | 5.6 min | – |
| ztreamer remote | gRPC listener open | 5.6 min | – |
Resource use
A sampler read /proc once a second for each process: CPU time, resident memory and disk bytes. For the remote and lightwalletd runs the totals include the standalone zakurad, which they need; in the direct run the node is inside ztreamerd.
| Server | Workload | Duration | CPU s | Mean cores | Peak RSS |
|---|---|---|---|---|---|
| ztreamer direct | idle | 60.0 s | 0.3 | 0.01 | 19.27 GiB |
| ztreamer direct | latency | 110.1 s | 348.8 | 3.17 | 19.31 GiB |
| ztreamer direct | ranges | 115.0 s | 28.2 | 0.25 | 19.31 GiB |
| ztreamer direct | concurrency 10k | 2.6 min | 652.3 | 4.14 | 19.36 GiB |
| ztreamer direct | concurrency 100 | 2.0 min | 609.3 | 5.01 | 19.36 GiB |
| ztreamer direct | wallet download | 22.6 s | 4.4 | 0.19 | 19.36 GiB |
| ztreamer direct | ghz | 11.2 min | 3,805.8 | 5.65 | 19.47 GiB |
| ztreamer remote | idle | 60.0 s | 0.2 | 0.00 | 2.82 GiB |
| ztreamer remote | latency | 2.7 min | 334.6 | 2.09 | 2.95 GiB |
| ztreamer remote | ranges | 117.9 s | 29.2 | 0.25 | 19.53 GiB |
| ztreamer remote | concurrency 10k | 2.6 min | 601.7 | 3.86 | 19.71 GiB |
| ztreamer remote | concurrency 100 | 2.0 min | 577.8 | 4.75 | 19.74 GiB |
| ztreamer remote | wallet download | 22.6 s | 4.6 | 0.20 | 19.73 GiB |
| ztreamer remote | ghz | 11.2 min | 3,945.5 | 5.87 | 19.74 GiB |
| lightwalletd | idle | 60.0 s | 0.5 | 0.01 | 1.15 GiB |
| lightwalletd | latency | 2.8 min | 371.3 | 2.23 | 1.31 GiB |
| lightwalletd | ranges | 6.2 min | 482.3 | 1.29 | 1.35 GiB |
| lightwalletd | concurrency 10k | 3.9 min | 2,070.3 | 8.82 | 1.81 GiB |
| lightwalletd | concurrency 100 | 2.0 min | 1,448.0 | 11.87 | 1.79 GiB |
| lightwalletd | wallet download | 65.2 s | 87.3 | 1.34 | 1.37 GiB |
| lightwalletd | ghz | 11.1 min | 6,750.0 | 10.10 | 2.40 GiB |
Serving efficiency: compact blocks served per CPU second of the server and its node, in the workloads that serve blocks.
| Workload | ztreamer direct blocks | ztreamer direct CPU s | ztreamer direct blocks / CPU s | ztreamer remote blocks | ztreamer remote CPU s | ztreamer remote blocks / CPU s | lightwalletd blocks | lightwalletd CPU s | lightwalletd blocks / CPU s |
|---|---|---|---|---|---|---|---|---|---|
| ranges | 21,110,000 | 28.2 | 748,847 | 21,110,000 | 29.2 | 722,450 | 21,110,000 | 482.3 | 43,774 |
| concurrency 10k | 110,010,000 | 652.3 | 168,636 | 112,700,000 | 601.7 | 187,287 | 35,460,000 | 2,070.3 | 17,128 |
| concurrency 100 | 90,507,100 | 609.3 | 148,543 | 94,403,600 | 577.8 | 163,387 | 23,806,300 | 1,448.0 | 16,440 |
| wallet download | 3,145,415 | 4.4 | 721,425 | 3,145,415 | 4.6 | 688,275 | 3,145,415 | 87.3 | 36,034 |
While syncing, ztreamer direct read 405 GB from disk in 146 s. In the first 44 s the embedded zakura opened its state on 6.5 cores on average and read 136 GB; the historical pass then read 269 GB of zakura state in 102 s (2.6 GB/s) on about 2 cores, so it was bound by the disk, not the CPU. Remote mode used about 24 cores while it verified the import. lightwalletd used under one core for itself and about 1.5 cores for its node for 1 hour 44 minutes. While serving, ztreamer used 4 to 6 cores under the heaviest workloads and lightwalletd 9 to 12, for a third to a quarter of the blocks. At idle all three use almost no CPU. The peak RSS of ztreamer while serving counts the mapped index; see the split below.
| Server | Process | When | Anonymous | File-backed | RSS |
|---|---|---|---|---|---|
| ztreamer direct | server | idle | 0.88 GiB | 0.57 GiB | 1.45 GiB |
| ztreamer direct | server | load | 1.21 GiB | 17.24 GiB | 18.46 GiB |
| ztreamer remote | server | idle | 0.02 GiB | 0.49 GiB | 0.51 GiB |
| ztreamer remote | server | load | 0.39 GiB | 17.18 GiB | 17.57 GiB |
| ztreamer remote | zakurad node | idle | 0.78 GiB | 0.03 GiB | 0.81 GiB |
| ztreamer remote | zakurad node | load | 0.77 GiB | 0.04 GiB | 0.81 GiB |
| lightwalletd | server | idle | 0.10 GiB | 0.02 GiB | 0.12 GiB |
| lightwalletd | server | load | 0.52 GiB | 0.02 GiB | 0.54 GiB |
| lightwalletd | zakurad node | idle | 0.75 GiB | 0.03 GiB | 0.78 GiB |
| lightwalletd | zakurad node | load | 0.76 GiB | 0.03 GiB | 0.80 GiB |
| Server | Restart to serving | Wallet download after restart | Blocks / s at 64 clients after restart |
|---|---|---|---|
| ztreamer direct | 47.3 s | 22.6 s | 1,087,600 |
| ztreamer remote | 3.1 s | 22.5 s | 1,098,970 |
| lightwalletd | 1.1 s | 64.9 s | 273,830 |
The restart runs restarted each server on the index of its main run, with the node already at the tip, then measured idle and loaded memory and repeated the wallet download. The wallet download after the restart took the same time as in the main run for every server.
Serving overview
Both ztreamer modes serve from the same kind of index, and their serving results agree within a few percent. lightwalletd serves blocks from its cache file and asks the node for everything else: chain info, tree states, transactions and address data.
RPC latencies
500 sequential requests per case from one client, after one warmup. Completion time includes draining the stream and decoding. Address cases use a busy and a quiet transparent address over the whole interval; GetTransaction uses two fixed transactions.
| Case | ztreamer direct p50 | ztreamer direct p99 | ztreamer remote p50 | ztreamer remote p99 | lightwalletd p50 | lightwalletd p99 | LWD ÷ direct, p50 |
|---|---|---|---|---|---|---|---|
| GetLightdInfo | 21 µs | 37 µs | 22 µs | 52 µs | 270 µs | 460 µs | 12.6× |
| GetLatestBlock | 21 µs | 30 µs | 21 µs | 24 µs | 176 µs | 353 µs | 8.2× |
| GetBlock | 32 µs | 429 µs | 52 µs | 405 µs | 85 µs | 789 µs | 2.7× |
| GetTreeState | 173 ms | 764 ms | 247 ms | 1.53 s | 261 ms | 1.50 s | 1.5× |
| GetLatestTreeState | 39 µs | 101 µs | 26 µs | 41 µs | 280 µs | 543 µs | 7.3× |
| GetSubtreeRoots/sapling | 79 µs | 170 µs | 100 µs | 284 µs | 302 µs | 663 µs | 3.8× |
| GetSubtreeRoots/orchard | 384 µs | 732 µs | 447 µs | 751 µs | 954 µs | 1.89 ms | 2.5× |
| GetMempoolTx | 58 µs | 96 µs | 61 µs | 247 µs | 25 µs | 51 µs | 0.4× |
| GetTaddressTxids/busy | 59 µs | 157 µs | 99 µs | 237 µs | 129 µs | 212 µs | 2.2× |
| GetTaddressTransactions/busy | 50 µs | 75 µs | 98 µs | 198 µs | 136 µs | 222 µs | 2.7× |
| GetTaddressBalance/busy | 92 µs | 124 µs | 127 µs | 213 µs | 186 µs | 285 µs | 2.0× |
| GetAddressUtxos/busy | 42 µs | 83 µs | 73 µs | 149 µs | 120 µs | 218 µs | 2.8× |
| GetTaddressTxids/quiet | 96 µs | 191 µs | 327 µs | 598 µs | 420 µs | 751 µs | 4.4× |
| GetTaddressTransactions/quiet | 95 µs | 191 µs | 267 µs | 369 µs | 461 µs | 775 µs | 4.9× |
| GetTaddressBalance/quiet | 96 µs | 185 µs | 124 µs | 195 µs | 181 µs | 302 µs | 1.9× |
| GetAddressUtxos/quiet | 43 µs | 107 µs | 71 µs | 124 µs | 123 µs | 207 µs | 2.9× |
| GetTransaction/0 | 39 µs | 81 µs | 108 µs | 168 µs | 140 µs | 223 µs | 3.6× |
| GetTransaction/1 | 36 µs | 66 µs | 102 µs | 153 µs | 167 µs | 267 µs | 4.7× |
Range sizes
GetBlockRange over 1,000 seeded random ranges per size (100 at 100,000 blocks), one client, sequential.
| Range | ztreamer direct p50 | ztreamer direct p99 | ztreamer direct blocks/s | ztreamer remote p50 | ztreamer remote p99 | ztreamer remote blocks/s | lightwalletd p50 | lightwalletd p99 | lightwalletd blocks/s |
|---|---|---|---|---|---|---|---|---|---|
| 10 blocks | 37 µs | 1.50 ms | 87,063 | 53 µs | 2.03 ms | 61,814 | 232 µs | 2.39 ms | 27,220 |
| 100 blocks | 108 µs | 14.34 ms | 122,197 | 165 µs | 19.04 ms | 99,587 | 1.19 ms | 22.81 ms | 43,087 |
| 1,000 blocks | 660 µs | 136 ms | 118,376 | 788 µs | 177 ms | 105,460 | 10.53 ms | 228 ms | 43,953 |
| 10,000 blocks | 4.79 ms | 1.20 s | 161,933 | 5.06 ms | 1.23 s | 157,381 | 107 ms | 1.94 s | 52,812 |
| 100,000 blocks | 51.93 ms | 4.56 s | 228,570 | 56.26 ms | 4.59 s | 228,977 | 997 ms | 8.72 s | 63,674 |
Concurrent serving
Closed loop: each client keeps one GetBlockRange outstanding for 20 s per level, on its own connection. Two request sizes: 10,000 blocks (a wallet catching up) and 100 blocks (a wallet that is nearly synced).
10,000 blocks per request
| Clients | ztreamer direct blocks/s | ztreamer direct p50 | ztreamer direct p99 | ztreamer direct errors | ztreamer remote blocks/s | ztreamer remote p50 | ztreamer remote p99 | ztreamer remote errors | lightwalletd blocks/s | lightwalletd p50 | lightwalletd p99 | lightwalletd errors |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 143,597 | 5.10 ms | 1.26 s | 0 | 144,500 | 4.88 ms | 1.26 s | 0 | 54,569 | 97.49 ms | 1.78 s | 0 |
| 4 | 551,460 | 5.43 ms | 1.34 s | 0 | 547,232 | 5.42 ms | 1.35 s | 0 | 160,833 | 135 ms | 2.50 s | 0 |
| 16 | 1,057,428 | 12.56 ms | 2.63 s | 0 | 1,109,207 | 11.44 ms | 2.48 s | 0 | 301,068 | 305 ms | 4.81 s | 0 |
| 64 | 1,062,686 | 402 ms | 3.06 s | 0 | 1,105,806 | 398 ms | 2.83 s | 0 | 265,853 | 1.28 s | 18.31 s | 0 |
| 128 | 1,065,055 | 1.06 s | 3.64 s | 0 | 1,101,892 | 979 ms | 3.44 s | 0 | 270,335 | 2.50 s | 23.91 s | 0 |
| 256 | 1,050,676 | 2.21 s | 4.97 s | 0 | 1,126,993 | 2.14 s | 4.71 s | 0 | 279,868 | 5.09 s | 29.63 s | 0 |
100 blocks per request
| Clients | ztreamer direct blocks/s | ztreamer direct p50 | ztreamer direct p99 | ztreamer direct errors | ztreamer remote blocks/s | ztreamer remote p50 | ztreamer remote p99 | ztreamer remote errors | lightwalletd blocks/s | lightwalletd p50 | lightwalletd p99 | lightwalletd errors |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 120,269 | 110 µs | 14.98 ms | 0 | 121,169 | 108 µs | 14.92 ms | 0 | 44,685 | 1.12 ms | 22.19 ms | 0 |
| 4 | 421,846 | 136 µs | 16.66 ms | 0 | 426,791 | 132 µs | 16.40 ms | 0 | 147,161 | 1.41 ms | 26.86 ms | 0 |
| 16 | 955,560 | 378 µs | 25.63 ms | 0 | 985,647 | 353 µs | 25.12 ms | 0 | 260,213 | 3.45 ms | 57.42 ms | 0 |
| 64 | 1,012,788 | 4.61 ms | 41.62 ms | 0 | 1,073,153 | 4.27 ms | 40.47 ms | 0 | 244,512 | 14.10 ms | 215 ms | 0 |
| 128 | 1,006,291 | 11.10 ms | 48.54 ms | 0 | 1,061,435 | 10.50 ms | 47.58 ms | 0 | 244,896 | 26.12 ms | 397 ms | 0 |
| 256 | 1,001,417 | 24.19 ms | 61.50 ms | 0 | 1,045,461 | 23.09 ms | 60.71 ms | 0 | 243,437 | 26.41 ms | 814 ms | 0 |
Wallet download
One client downloads every compact block from Sapling activation to the end of the interval, 10,000 blocks per request. This is the network part of a new wallet's first sync; it excludes trial decryption, scanning and storage.
| Server | Time | Blocks | Protobuf bytes | Blocks / s |
|---|---|---|---|---|
| ztreamer direct | 22.6 s | 3,145,415 | 16.66 GiB | 139,121 |
| ztreamer remote | 22.6 s | 3,145,415 | 16.66 GiB | 139,188 |
| lightwalletd | 65.2 s | 3,145,415 | 16.66 GiB | 48,276 |
Throughput per RPC
ghz drives one RPC at a time with 1, 16, 64 and 256 concurrent workers, one connection each, for 15 s per level. Requests with heights cycle through 5,000 seeded random heights. The first request of each worker is not counted. Throughput counts successful requests only.
| RPC | Workers | ztreamer direct req/s | ztreamer direct p50 | ztreamer direct p99 | ztreamer direct errors | ztreamer remote req/s | ztreamer remote p50 | ztreamer remote p99 | ztreamer remote errors | lightwalletd req/s | lightwalletd p50 | lightwalletd p99 | lightwalletd errors |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| GetBlock | 1 | 9,394 | 37 µs | 1.31 ms | 0 | 9,377 | 37 µs | 1.31 ms | 0 | 5,308 | 71 µs | 1.80 ms | 0 |
| GetBlock | 16 | 66,165 | 83 µs | 2.23 ms | 0 | 66,736 | 82 µs | 2.22 ms | 0 | 41,205 | 149 µs | 3.18 ms | 0 |
| GetBlock | 64 | 88,755 | 351 µs | 3.95 ms | 0 | 89,051 | 351 µs | 3.91 ms | 0 | 53,075 | 705 µs | 5.87 ms | 0 |
| GetBlock | 256 | 73,655 | 1.65 ms | 7.79 ms | 343,433 | 73,331 | 1.69 ms | 7.83 ms | 359,656 | 52,692 | 2.95 ms | 16.07 ms | 0 |
| GetBlockNullifiers | 1 | 10,019 | 36 µs | 593 µs | 53,904 | 9,001 | 36 µs | 591 µs | 78,393 | 10,198 | 42 µs | 760 µs | 0 |
| GetBlockNullifiers | 16 | 66,539 | 79 µs | 934 µs | 352,755 | 59,057 | 78 µs | 922 µs | 504,500 | 63,853 | 104 µs | 1.33 ms | 0 |
| GetBlockNullifiers | 64 | 94,241 | 269 µs | 1.75 ms | 476,550 | 84,514 | 246 µs | 1.67 ms | 716,751 | 87,542 | 361 µs | 2.75 ms | 0 |
| GetBlockNullifiers | 256 | 91,795 | 1.33 ms | 4.92 ms | 545,373 | 81,818 | 1.28 ms | 4.79 ms | 718,777 | 88,182 | 1.52 ms | 9.24 ms | 0 |
| GetBlockRange | 1 | 124 | 674 µs | 91.63 ms | 124,416 | 178 | 701 µs | 99.68 ms | 0 | 166 | 1.28 ms | 100 ms | 0 |
| GetBlockRange | 16 | 1,662 | 1.13 ms | 181 ms | 0 | 1,676 | 1.12 ms | 178 ms | 0 | 1,200 | 3.06 ms | 222 ms | 0 |
| GetBlockRange | 64 | 1,746 | 26.32 ms | 244 ms | 0 | 1,769 | 25.62 ms | 244 ms | 0 | 1,222 | 18.65 ms | 657 ms | 0 |
| GetBlockRange | 256 | 1,703 | 143 ms | 357 ms | 0 | 1,712 | 143 ms | 355 ms | 0 | 1,205 | 29.33 ms | 2.62 s | 0 |
| GetBlockRangeNullifiers | 1 | 46 | 4.16 ms | 280 ms | 0 | 46 | 4.05 ms | 283 ms | 0 | 31 | 11.15 ms | 288 ms | 0 |
| GetBlockRangeNullifiers | 16 | 475 | 7.30 ms | 487 ms | 0 | 480 | 7.18 ms | 483 ms | 0 | 227 | 35.14 ms | 562 ms | 0 |
| GetBlockRangeNullifiers | 64 | 514 | 91.14 ms | 699 ms | 0 | 515 | 91.48 ms | 695 ms | 0 | 226 | 152 ms | 2.23 s | 0 |
| GetBlockRangeNullifiers | 256 | 490 | 484 ms | 1.05 s | 0 | 497 | 480 ms | 1.05 s | 0 | 216 | 703 ms | 6.45 s | 0 |
| GetLatestBlock | 1 | 22,887 | 18 µs | 51 µs | 0 | 23,230 | 18 µs | 48 µs | 0 | 4,974 | 162 µs | 319 µs | 0 |
| GetLatestBlock | 16 | 116,737 | 53 µs | 297 µs | 0 | 117,535 | 53 µs | 284 µs | 0 | 34,895 | 363 µs | 931 µs | 0 |
| GetLatestBlock | 64 | 168,329 | 180 µs | 853 µs | 0 | 173,631 | 172 µs | 751 µs | 0 | 43,127 | 1.27 ms | 3.16 ms | 0 |
| GetLatestBlock | 256 | 176,055 | 614 µs | 3.01 ms | 0 | 179,053 | 580 µs | 2.88 ms | 0 | 42,342 | 3.25 ms | 7.05 ms | 2,813 |
| GetLatestTreeState | 1 | 15,108 | 36 µs | 122 µs | 0 | 19,213 | 24 µs | 94 µs | 0 | 2,887 | 289 µs | 732 µs | 0 |
| GetLatestTreeState | 16 | 81,421 | 106 µs | 471 µs | 0 | 105,411 | 66 µs | 347 µs | 0 | 22,445 | 610 µs | 1.31 ms | 0 |
| GetLatestTreeState | 64 | 91,642 | 528 µs | 1.61 ms | 0 | 154,033 | 208 µs | 899 µs | 0 | 26,660 | 2.22 ms | 4.22 ms | 0 |
| GetLatestTreeState | 256 | 94,665 | 2.29 ms | 6.01 ms | 0 | 152,887 | 697 µs | 3.55 ms | 0 | 25,724 | 5.28 ms | 10.02 ms | 161 |
| GetLightdInfo | 1 | 22,124 | 20 µs | 60 µs | 0 | 22,324 | 20 µs | 53 µs | 0 | 2,849 | 304 µs | 571 µs | 0 |
| GetLightdInfo | 16 | 114,142 | 56 µs | 318 µs | 0 | 113,628 | 56 µs | 306 µs | 0 | 23,467 | 589 µs | 1.18 ms | 0 |
| GetLightdInfo | 64 | 166,420 | 187 µs | 854 µs | 0 | 168,005 | 183 µs | 812 µs | 0 | 27,806 | 2.15 ms | 4.39 ms | 0 |
| GetLightdInfo | 256 | 170,627 | 629 µs | 3.15 ms | 0 | 173,215 | 614 µs | 3.00 ms | 0 | 26,851 | 4.98 ms | 9.16 ms | 0 |
| GetSubtreeRoots | 1 | 2,486 | 356 µs | 705 µs | 42,215 | 3,144 | 358 µs | 716 µs | 0 | 1,378 | 872 µs | 1.53 ms | 0 |
| GetSubtreeRoots | 16 | 20,541 | 477 µs | 1.70 ms | 373,070 | 22,850 | 548 µs | 2.67 ms | 9 | 9,682 | 1.54 ms | 3.47 ms | 0 |
| GetSubtreeRoots | 64 | 25,302 | 1.24 ms | 3.66 ms | 497,235 | 35,894 | 1.52 ms | 4.14 ms | 29 | 10,233 | 5.11 ms | 19.63 ms | 0 |
| GetSubtreeRoots | 256 | 19,382 | 6.10 ms | 15.11 ms | 669,921 | 27,840 | 5.22 ms | 11.89 ms | 294,586 | 10,087 | 9.24 ms | 123 ms | 2 |
| GetTaddressBalance | 1 | 7,981 | 93 µs | 178 µs | 0 | 6,559 | 119 µs | 226 µs | 0 | 4,222 | 201 µs | 362 µs | 0 |
| GetTaddressBalance | 16 | 53,528 | 221 µs | 550 µs | 0 | 41,707 | 296 µs | 739 µs | 0 | 29,527 | 431 µs | 1.16 ms | 0 |
| GetTaddressBalance | 64 | 72,107 | 671 µs | 2.11 ms | 0 | 55,643 | 974 µs | 2.24 ms | 0 | 41,732 | 1.31 ms | 3.16 ms | 0 |
| GetTaddressBalance | 256 | 72,189 | 2.90 ms | 8.09 ms | 0 | 58,271 | 1.74 ms | 202 ms | 0 | 42,073 | 3.01 ms | 7.43 ms | 0 |
| GetTransaction | 1 | 14,771 | 40 µs | 92 µs | 0 | 6,572 | 117 µs | 245 µs | 0 | 4,379 | 189 µs | 375 µs | 0 |
| GetTransaction | 16 | 92,392 | 88 µs | 375 µs | 0 | 45,590 | 270 µs | 590 µs | 0 | 33,536 | 377 µs | 964 µs | 0 |
| GetTransaction | 64 | 131,406 | 275 µs | 1.10 ms | 0 | 69,435 | 759 µs | 1.79 ms | 0 | 48,835 | 1.09 ms | 2.76 ms | 0 |
| GetTransaction | 256 | 134,669 | 1.33 ms | 4.46 ms | 0 | 70,992 | 1.51 ms | 4.67 ms | 0 | 49,370 | 2.50 ms | 6.19 ms | 15,911 |
| GetTreeState | 1 | 5 | 134 ms | 753 ms | 49 | 2 | 302 ms | 1.25 s | 12 | 3 | 301 ms | 1.32 s | 0 |
| GetTreeState | 16 | 33 | 362 ms | 1.68 s | 212 | 28 | 408 ms | 2.27 s | 0 | 25 | 467 ms | 2.27 s | 0 |
| GetTreeState | 64 | 35 | 1.19 s | 8.89 s | 223 | 62 | 198 ms | 7.06 s | 0 | 30 | 843 ms | 10.21 s | 0 |
| GetTreeState | 256 | 31 | 6.28 s | 10.79 s | 207 | 81 | 10.90 ms | 9.06 s | 1,705 | 22 | 2.73 s | 10.16 s | 491,456 |
Raw data
All scenario summaries · Request samples (without the 2 M samples of the 100-block concurrency suite) · ghz results · Resource samples · Index sizes · Phases · All numbers. The harness is in benchmarks/2026-11-lwd-comparison. The previous page, ztreamer v0.1.0 against v0.0.1, is archived here.