mkpool vs ckpool: a head to head on AMD EPYC

Both pools built from source and measured on the same server hardware, one pool at a time, pointed at the same backend, driven by the same load, with correctness checked before any number is trusted.

Measured 24 July 2026. Every figure below is reproducible from the harness in this repository.

mkpool validates a share about 14% faster mkpool sustains up to 1.26x the throughput at high load

This is a case study, not a callout. ckpool is a mature, battle tested Stratum server that has run real Bitcoin mining infrastructure for years, and its lean C design sets a genuinely high bar. This comparison exists to understand honestly where mkpool stands against that bar and to learn from it, not to disparage ckpool or the people who built it. It is published openly and reproducibly, in the same spirit ckpool itself is, so anyone can re-run it and check the numbers. Where ckpool is faster, this report says so plainly.

The test machines

RoleMachine
Pool under test (the measured machine)AMD EPYC 9V45, 64 vCPUs, 125 GiB RAM
Load generator (separate machine)AMD EPYC 9V74, 64 vCPUs, 251 GiB RAM
Operating systemUbuntu 24.04.4 LTS, Linux 6.17
Bitcoin nodeBitcoin Core 27.1.0
mkpoolversion 0.4.0, built -O3 -flto -march=native
ckpoolcurrent release, commit 308410d

How the comparison was run

There are two kinds of measurement here, and they answer two different questions.

The first is the cost of validating a single share in isolation, timed on one CPU core against a real Bitcoin mainnet block. This is the pool's core work and it sets how much a single core can handle.

The second is how the running server behaves under load: how many shares per second it validates across a range of connected miners, how quickly it answers, how many connections it holds and at what memory cost, and how fast it accepts reconnecting miners. For this, each pool ran as a live server on the pool machine and was driven by the load generator on the second machine over the network. To keep it honest, the node's block template was served at a real mainnet difficulty, so every submitted share was fully validated by both pools but never trivially counted as a block. The template carried a realistic set of transactions, so the coinbase and merkle work per share matched real conditions. Each measurement was repeated three times and averaged, with each pool run one at a time on the same machine against the same backend.

Per-share validation speed

Rebuild the coinbase, fold it through the merkle branch, assemble the 80 byte header, hash it twice with SHA-256, and compare to target. We timed this three million times, pinned to one core, on real Bitcoin mainnet block 959,143 with its full transaction set and genuine 12 level merkle branch. Before any timing, both sides reproduce that block's published merkle root, so both are proven to be doing the full, correct work.

Shares validated per second, per CPU core (higher is better) 0 150k 300k 450k 600k mkpool: 550,000 shares/sec/core 550k ckpool: 484,000 shares/sec/core 484k mkpool ckpool (its release flags) AMD EPYC 9V45, single core, 3,000,000 shares, real block 959143
Single core, 3,000,000 shares, real block 959143 template (12 merkle levels), zero memory allocations per share.
PoolTime per shareShares per second, per core
mkpool1,818 ns550,000
ckpool, its own release flags (-O2 -msse4.1 -msha)2,065 ns484,000
ckpool, matched flags (-O3 -march=native)2,223 ns450,000

mkpool validates a share about 14% faster than ckpool built the way ckpool ships, and about 22% faster when both are compiled with identical flags. Both do this with zero memory allocations per share, so the difference is in the validation code itself, not framework overhead.

Request throughput under load

With the server live and miners connected, how many shares per second does each pool fully validate? We swept the number of connections from 128 up to 2,048 and measured the sustained validated-share rate at each level.

Validated shares per second vs connected miners (higher is better) 0 30k 60k 90k 120k 117k 128256 5121024 2048 connected miners mkpool ckpool
Sustained validated shares per second, average of three runs, mainnet difficulty, realistic transaction set.
Connected minersmkpoolckpoolFaster pool
12825,41828,569ckpool, 1.12x
25661,72254,868mkpool, 1.12x
51281,34793,809ckpool, 1.15x
1024117,48192,980mkpool, 1.26x
2048103,42692,606mkpool, 1.12x

ckpool holds a small lead at the lowest connection counts, but its throughput flattens near 93,000 shares per second from 512 connections upward. mkpool keeps climbing, reaching 117,000 shares per second at 1,024 connections, about 1.26x ckpool, and stays ahead at 2,048. In other words, mkpool scales better into the connection counts a busy pool actually sees.

Latency, capacity, memory, and reconnection

The remaining measurements are close, and worth stating plainly.

Submit to acknowledge latency

Under a fixed, steady load both pools answer in essentially the same time. This is a tie.

Poolp50p90p99
mkpool66.2 ms66.3 ms66.9 ms
ckpool66.6 ms66.7 ms67.3 ms

Connection capacity and memory

Both pools held all 8,000 requested connections without dropping any. ckpool carries a lighter memory footprint per connection.

PoolConnections heldPool memoryPer connection
mkpool8,000116 MiB14.9 KiB
ckpool8,00084 MiB10.8 KiB

Reconnection handling

Rate of full connect, subscribe, authorize, and disconnect cycles the server accepts per second. ckpool is a little quicker here.

PoolReconnections per second
mkpool676
ckpool766

Summary

MeasurementResult
Per-share validation speedmkpool, about 14 to 22% faster
Request throughput at high loadmkpool, up to 1.26x at 1,024 connections
Submit to acknowledge latencytie, about 66 ms both
Connection capacitytie, both held 8,000
Memory per connectionckpool, about 28% lighter
Reconnection handlingckpool, about 13% faster

mkpool wins the two measurements that most define a pool's capacity: how fast it validates a share, and how many shares per second it sustains as the miner count grows. ckpool keeps a modest edge on memory footprint and on the rate of reconnecting sessions. Latency and raw connection capacity are level.

How to reproduce

The per-share validation benchmark is a single command:

MKPOOL=/path/to/mkpool CKPOOL=/path/to/ckpool CORE=3 ITERS=3000000 bash microbench/compare.sh

It builds and runs both pools' validation code on the same core against real block 959143 and refuses to print a number unless both reproduce the block's published merkle root. For the server measurements, one machine runs the pool and a second runs the load generator; both pools are fed the same node template at mainnet difficulty and driven with the same connection sweep. Full configurations for both pools, and the raw per-run data behind every figure here, are published alongside this report.

ckpool is built from its canonical repository at its release defaults; mkpool at its own Release flags. Numbers vary by processor. Re-run it on your own hardware and treat anything that does not reproduce as wrong.