perf: parameter-bank uniforms and per-arch carry path (+20.5% sm_120) #22
Reference in New Issue
Block a user
Delete Branch "perf/sm120-spills"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes the first half of the gap against
qpow-cuda/1.0.7. Two kernel changes that measure, one dead end recorded so it is not retried.Measured, parity verified
Three interleaved rounds per figure, spread <=0.2%, parity against the CPU on every host.
Fleet ~1.67 -> ~1.91 GH/s.
1. Launch uniforms in the parameter bank
midstate,targetand the nonce base were device buffers copied into per-thread register arrays and held live across the whole nonce loop -- 56 registers the permutation's working set needed more. ptxas spilled 104 bytes per thread on sm_120 and 44 on sm_86/89, with 26LDL+ 26STLin the hot loop.They are launch-uniform, so they now ride in a by-value
MiningUniformsparameter: constant memory, broadcast and cached, no register residency, and it arrives with the launch so threememcpy_htodcalls per batch go with it. 128 -> 80/80/106 registers, zero spill everywhere. Both sidesstatic_assertthe 224-byte layout, so a mismatch is a build error and not a silent mining bug.2. Per-architecture carry path
An
asmblock is opaque to nvcc's optimiser, so carry arithmetic written as inline PTX blocks CSE and strength reduction across neighbouring field operations. On Blackwell that costs a quarter of the kernel. On Ada and Ampere the hand-written sequence still wins, by a similar margin the other way:PTX_CARRY=1PTX_CARRY=0So the default is chosen per device pass. sm_86/89 keep the exact configuration they had -- identical macro values, identical 37,027 instructions -- so those cards are unchanged by construction.
LAIR_PTX_ACCis now 1 everywhere rather than followingLAIR_PTX_CARRY: in theAccaccumulators the add-with-carry pair is the whole operation, and on sm_120 with the carry path off, Acc PTX on measured 1333.3 against 1285.8 with it off.PR #17 measured the PTX carry path as a win on every card. That was true of the kernel it was written for, which spilled 104 bytes per thread; once the spills went the trade reversed on Blackwell only.
3. Recorded, not applied
a^2 = a0^2 + a0*a1*2^33 + a1^2*2^64is three 32x32 multiplies where the general product needs four, and nvcc does not find it. Implemented, verified bit-exact against__int128over 40M values plus limb and power-of-two boundaries, measured 5.7% slower (1072.9 vs 1137.7). It saves 6.7% of widening multiplies and costs 11% more instructions; four formulations all compiled to the same 5 IMAD.WIDE in 56 instructions against 6 in 48, so that is the floor.LAIR_INT_UNROLL=1on sm_120. Better by every static measure (78 registers instead of 106, three resident blocks instead of two) and 0.92% slower on beast. These cards are power-bound: the extra warps buy power draw and the card clocks down to pay for it.Both are comments in
mining.cuso the next person does not rediscover them.How this was found
Profiling, not reading.
ncuput us at 43,700 executed instructions per nonce against the rival binary's 22,229 on the same card -- a 1.97x ratio matching the 2.05x throughput gap -- with 82% of the excess on the ALU pipe, not the multiply pipe. That ruled out the sparse-path theory, the multiply itself, occupancy and launch shape in one measurement, and pointed straight at our own carry machinery.Standing after this PR: 35,785 instructions per nonce against their 22,229. The multiply pipe is near parity (8,405 vs 7,030, 1.20x); ALU is still 26,586 vs 14,413, so 89% of the remaining gap is ALU.
gf_reduce's borrow/mask/carry-fold chain is the largest single term, roughly 7 ALU ops across 1472 multiplies per nonce.Risk
Kernel arithmetic only, plus the host-side uniform struct. No protocol or engine-selection changes, so the coupling rules in #1 hold. Every knob remains overridable through
MINER_NVCC_FLAGS. Parity ran on all three architectures: 25/25 on quadbrat and benjy, 60/60 on beast.Refs #3.
🤖 Generated with Claude Code
https://claude.ai/code/session_017ytCkttecH9SNTUEw8VT9D
An `asm` block is opaque to nvcc's optimiser, so carry arithmetic written as inline PTX blocks common-subexpression elimination and strength reduction across neighbouring field operations. On Blackwell that costs a quarter of the kernel. On Ada and Ampere the hand-written sequence still wins, by a similar margin in the other direction, so the default is now chosen per device pass. Whole-kernel instructions (ptxas + cuobjdump, CUDA 13.0) and measured hashrate, three interleaved rounds each: sm_120 sm_86/89 PTX_CARRY=1 37,432 1127.1 MH/s 37,027 benjy 461.6 quadbrat 85.5 PTX_CARRY=0 27,837 1358.0 MH/s 41,196 benjy 401.5 quadbrat 76.3 sm_86/89 keep the exact configuration they had, so those cards are unchanged by construction -- identical macro values, identical 37,027 instructions. Parity 60/60 against the CPU on beast. LAIR_PTX_ACC is now 1 everywhere rather than following LAIR_PTX_CARRY. In the Acc accumulators the add-with-carry pair is the whole operation, so there is nothing around it for the optimiser to fold; on sm_120 with the carry path off, Acc PTX on measured 1333.3 against 1285.8 with it off. PR #17 measured the PTX carry path as a win on every card. That was true of the kernel it was written for, which spilled 104 bytes per thread; once the spills went the trade reversed on Blackwell only. Found by profiling rather than reading: ncu puts us at 43,700 executed instructions per nonce against the rival binary's 22,229 on the same card, a 1.97x ratio that matches the 2.05x throughput gap, with 82% of the excess on the ALU pipe rather than the multiply pipe. This lands 35,785. The remaining gap is still ALU: 26,586 against their 14,413, while the multiply pipe is now close to parity at 8,405 against 7,030. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ytCkttecH9SNTUEw8VT9Dquantus-bench
575c4d0abdon benjy.hanzalova.internaltotal median: 456.92 MH/s (batch 1000000, 5 x 30s, engine gpu-cuda)
--- resume miner ---
active