|
1 | 1 | --- |
2 | | -title: Beats GoCoin 2.3x and uses 2.8x less memory than Core, but Core still leads 2.05x on throughput |
| 2 | +title: Beats GoCoin 2.3x, uses 2.9x less memory than Core, and Core leads 1.42x on throughput once the harness is matched |
3 | 3 | date: 2026-08-07 |
4 | 4 | category: docs/solutions/performance-issues |
5 | 5 | module: node apply path (crates/node/src/apply.rs, crates/consensus/src/verify_tx.rs) |
|
21 | 21 | - replay |
22 | 22 | --- |
23 | 23 |
|
24 | | -# Beats GoCoin 2.3×, uses 2.8× less memory than Core, and Core still leads 2.05× on throughput |
| 24 | +# Beats GoCoin 2.3×, uses 2.9× less memory than Core, and Core leads 1.42× once the harness is matched |
25 | 25 |
|
26 | 26 | ## Context |
27 | 27 |
|
@@ -132,6 +132,27 @@ The first two cleared the tests but not the 1.05× noise floor; the third never |
132 | 132 |
|
133 | 133 | The fourth result retires the "FFI boundary is the remaining lever" hypothesis for its allocation half. Per-input `ScriptPubkey::new` is a ~25-byte `malloc` plus copy; at 3.3M inputs that is real allocator traffic, and removing it entirely changed nothing measurable. What the kernel spends inside `btck_script_pubkey_verify` is secp256k1 work, not marshalling. Marshalling-side micro-optimization is now closed as a class: four separate attempts (parallel prepare, serialize buffer, witness-free skip, prevout reuse) all landed at 0.98–1.00×. |
134 | 134 |
|
| 135 | +## Matching the harness moved the ratio from 2.05× to 1.42× |
| 136 | + |
| 137 | +Every earlier number here fetched blocks over REST from a live `bitcoind`. Core's `-reindex-chainstate` reads its own `blk*.dat` files. That is not a like-for-like harness: the replay paid HTTP round-trips *and* competed for CPU with the process serving them, neither of which Core pays. |
| 138 | + |
| 139 | +`mainnet_prefix_replay --blocks-file` now reads a length-prefixed local file, mirroring what Core does. Same window, same validation posture, pinned 3× medians: |
| 140 | + |
| 141 | +| Source | elapsed | apply | outside apply | |
| 142 | +|---|---|---|---| |
| 143 | +| REST from live `bitcoind` | 121.9s | 82.0s | ~40s | |
| 144 | +| **local block file** | **84.6s** (84.2, 84.6, 86.5) | **76.7s** | 8.0s | |
| 145 | + |
| 146 | +Note that *apply itself* improved, 82.0s → 76.7s, purely from removing the serving node's CPU contention. The harness was distorting the engine measurement, not merely adding a constant. |
| 147 | + |
| 148 | +| Metric | Core 31.0 | bitcoin-rs | | |
| 149 | +|---|---|---|---| |
| 150 | +| elapsed | 59.6s | **84.6s** | **1.42× slower** | |
| 151 | +| apply | 55.80s | 76.7s | 1.37× slower | |
| 152 | +| peak RSS | 643 MB | **224 MB** | **2.87× leaner** | |
| 153 | + |
| 154 | +**Quote 1.42×.** The 2.05× and 2.22× figures earlier in this note measured the harness as much as the engine and are superseded. The lesson is the same one that produced the matched-pair section below: a ratio is only as good as the least-matched thing in it, and here the block source was the least-matched thing for the whole session. |
| 155 | + |
135 | 156 | ## The matched pair — the only ratio worth quoting |
136 | 157 |
|
137 | 158 | Every earlier ratio in this note compared a bitcoin-rs median against Core's **67s** figure captured on 2026-06-09 under unknown load. That reference has now been re-derived, and the honest numbers are worse than the ones this document previously carried. |
|
0 commit comments