|
| 1 | +# CPU Mining — Thread Count & Affinity Tuning |
| 2 | + |
| 3 | +The CPU resolver scans nonces with a self-owned pool of persistent worker threads (one |
| 4 | +batch of nonces fanned across the pool each iteration). Three flags control it: |
| 5 | + |
| 6 | +| Flag | Default | Meaning | |
| 7 | +|---|---|---| |
| 8 | +| `--cpu` | `false` | Enable the CPU device. | |
| 9 | +| `--cpu_threads` | all logical cores | Number of worker threads (pool size). | |
| 10 | +| `--cpu_affinity` | none | Hex bitmask of logical cores to pin workers to (bit *i* = core *i*). | |
| 11 | + |
| 12 | +`--cpu_affinity` accepts `0xFF` or `FF` (up to 64 cores). `--cpu_threads=0` is treated as |
| 13 | +unset and falls back to all logical cores. |
| 14 | + |
| 15 | +## How the two flags interact |
| 16 | + |
| 17 | +| `--cpu_threads` | `--cpu_affinity` | Result | |
| 18 | +|---|---|---| |
| 19 | +| unset | unset | one worker per logical core, no pinning (default) | |
| 20 | +| unset | set | N = popcount(mask); worker *k* is pinned to the *k*-th set bit | |
| 21 | +| set | unset | N workers, no pinning | |
| 22 | +| set | set | N workers; if N > set bits → pinned round-robin over them; if N < set bits → first N | |
| 23 | + |
| 24 | +## Running CPU mining alongside a GPU |
| 25 | + |
| 26 | +A GPU is not autonomous: its host-side feeder thread and the OpenCL/CUDA driver threads |
| 27 | +(all inside the miner process) must launch kernels and read results continuously to keep |
| 28 | +the GPU busy. If the CPU resolver spawns **one worker per core** (the default), those |
| 29 | +workers saturate every core and starve the GPU's host threads — the GPU can drop to a |
| 30 | +fraction of its solo hashrate while the CPU adds only a tiny amount. |
| 31 | + |
| 32 | +> Example (16-core host + an RX 9070 XT, BLAKE3): GPU-only ≈ **1.70 GH/s**. With default |
| 33 | +> all-core CPU mining also enabled, the GPU collapsed to ≈ **0.4 GH/s** — a far bigger |
| 34 | +> loss than the ≈ 11 MH/s of CPU gained. |
| 35 | +
|
| 36 | +Lowering the thread *count* alone does **not** fix this: unpinned threads roam across all |
| 37 | +cores and still bounce the GPU's feeder. The fix is to **reserve cores for the GPU with |
| 38 | +affinity**: |
| 39 | + |
| 40 | +```sh |
| 41 | +# Pin the CPU pool to cores 0-7; leave cores 8-15 free for the GPU's host threads. |
| 42 | +miner --algo=blake3 --host=<pool> --port=<port> --wallet=<addr> \ |
| 43 | + --amd=true --cpu=true --cpu_threads=8 --cpu_affinity=0xFF |
| 44 | +``` |
| 45 | + |
| 46 | +On the example host this restored the GPU to its full ≈ 1.70 GH/s while still mining |
| 47 | +≈ 7 MH/s on the CPU. |
| 48 | + |
| 49 | +## Recommendations |
| 50 | + |
| 51 | +- On a GPU rig, **GPU-only is usually best** — a modern GPU out-hashes the CPU by orders |
| 52 | + of magnitude, so CPU mining rarely pays for the contention it adds. |
| 53 | +- If you do want CPU hashes too, **reserve cores for the GPU** with `--cpu_threads` + |
| 54 | + `--cpu_affinity`; leave a few cores out of the CPU mask for the GPU's host/driver |
| 55 | + threads, and tune the split to taste. |
| 56 | +- On a **CPU-only** machine, the defaults (all cores, no pinning) are correct. |
| 57 | +- Process-level priority tweaks (e.g. forcing the whole miner to "below normal") do |
| 58 | + **not** relieve GPU+CPU contention — they lower the GPU feeder thread too. The |
| 59 | + reservation has to be per-core (affinity). |
0 commit comments