Merge pull request 'release: tag-triggered binaries and a README that states the measured rates' (#28) from release/binaries-and-readme into main
All checks were successful
ci / fmt (push) Successful in 19s
release / build (push) Successful in 1m21s
deploy / build (push) Successful in 1m31s
ci / clippy (push) Successful in 1m51s
deploy / deploy (1, benjy.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 36s
deploy / deploy (1, quadbrat.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 36s
deploy / deploy (2, beast.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 36s
ci / doc (push) Successful in 2m0s
ci / test (push) Successful in 6m56s
All checks were successful
ci / fmt (push) Successful in 19s
release / build (push) Successful in 1m21s
deploy / build (push) Successful in 1m31s
ci / clippy (push) Successful in 1m51s
deploy / deploy (1, benjy.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 36s
deploy / deploy (1, quadbrat.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 36s
deploy / deploy (2, beast.hanzalova.internal, cuda, bob.hanzalova.internal) (push) Successful in 36s
ci / doc (push) Successful in 2m0s
ci / test (push) Successful in 6m56s
This commit was merged in pull request #28.
This commit is contained in:
@@ -20,6 +20,7 @@ on:
|
||||
- "**.md"
|
||||
- .gitea/workflows/ci.yml
|
||||
- .gitea/workflows/bench.yaml
|
||||
- .gitea/workflows/release.yaml
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
mode:
|
||||
|
||||
78
.gitea/workflows/release.yaml
Normal file
78
.gitea/workflows/release.yaml
Normal file
@@ -0,0 +1,78 @@
|
||||
---
|
||||
# Release binaries for independent miners (quantus/miner#27).
|
||||
#
|
||||
# Pushing a tag `v<upstream version>-lair.<n>` builds the miner and the bench
|
||||
# harness on the cuda-13.0 runner (Fedora 43 like the mining hosts, and the
|
||||
# only runner with nvcc, so the binary carries the sm_86/sm_89/sm_120 cubins
|
||||
# and PTX), records what was built, and publishes a Gitea release with the
|
||||
# binaries, their checksums and the build report attached. The tag's commit
|
||||
# is embedded in `--version` so a downloaded binary identifies itself.
|
||||
#
|
||||
# Deploying to the fleet is deploy.yaml's job and follows main; a release is
|
||||
# only a public snapshot and changes nothing on the hosts.
|
||||
name: release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ["v*-lair.*"]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: cuda-13.0
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: build quantus-miner and quantus-bench
|
||||
env:
|
||||
MINER_BUILD_SHA: ${{ github.sha }}
|
||||
MINER_CUDA_REQUIRE: "1"
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cargo build --release --locked -p miner-cli -p bench-harness
|
||||
./target/release/quantus-miner --version
|
||||
./target/release/quantus-bench --version || true
|
||||
- name: package
|
||||
run: |
|
||||
set -euo pipefail
|
||||
tag="${GITHUB_REF_NAME}"
|
||||
dir="quantus-miner-${tag}-linux-x86_64"
|
||||
mkdir -p "dist/${dir}"
|
||||
cp target/release/quantus-miner target/release/quantus-bench "dist/${dir}/"
|
||||
cp LICENSE "dist/${dir}/"
|
||||
{
|
||||
echo "quantus-miner ${tag}"
|
||||
echo "commit: ${GITHUB_SHA}"
|
||||
echo "built: $(date -u +%Y-%m-%dT%H:%M:%SZ) on $(source /etc/os-release && echo "$PRETTY_NAME"), $(ldd --version | head -1)"
|
||||
echo "cuda: $(nvcc --version | grep -oE 'release [0-9.]+' | head -1)"
|
||||
echo "cubins: sm_86 sm_89 sm_120 + compute_120 PTX"
|
||||
echo "version string: $(./target/release/quantus-miner --version)"
|
||||
} > "dist/${dir}/BUILD.txt"
|
||||
(cd dist && tar -czf "${dir}.tar.gz" "${dir}" && sha256sum "${dir}.tar.gz" > "${dir}.tar.gz.sha256")
|
||||
cat "dist/${dir}/BUILD.txt" "dist/${dir}.tar.gz.sha256"
|
||||
- name: publish release
|
||||
run: |
|
||||
set -euo pipefail
|
||||
tag="${GITHUB_REF_NAME}"
|
||||
dir="quantus-miner-${tag}-linux-x86_64"
|
||||
api="${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}"
|
||||
auth="Authorization: token ${{ secrets.GITHUB_TOKEN }}"
|
||||
body=$(python3 - "$tag" "dist/${dir}/BUILD.txt" "dist/${dir}.tar.gz.sha256" <<'PY'
|
||||
import json, sys, pathlib
|
||||
tag, build, sha = sys.argv[1], pathlib.Path(sys.argv[2]).read_text(), pathlib.Path(sys.argv[3]).read_text().strip()
|
||||
text = (
|
||||
f"quantus-miner {tag}: Linux x86_64, NVIDIA RTX 30/40/50 (sm_86, sm_89, sm_120, plus PTX), "
|
||||
"glibc 2.42 or newer, driver with CUDA 13.0 support.\n\n"
|
||||
"No dev fee, no licence server, no telemetry. Measured rates and how to reproduce them are in the README.\n\n"
|
||||
f"```\n{build}```\n\nsha256: `{sha}`\n"
|
||||
)
|
||||
print(json.dumps({"tag_name": tag, "name": f"quantus-miner {tag}", "body": text, "draft": False, "prerelease": False}))
|
||||
PY
|
||||
)
|
||||
id=$(curl -fsS -X POST -H "$auth" -H "Content-Type: application/json" "${api}/releases" -d "$body" | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')
|
||||
echo "release id ${id}"
|
||||
for f in "dist/${dir}.tar.gz" "dist/${dir}.tar.gz.sha256" "dist/${dir}/BUILD.txt"; do
|
||||
curl -fsS -X POST -H "$auth" "${api}/releases/${id}/assets?name=$(basename "$f")" -F "attachment=@${f}" > /dev/null
|
||||
echo "attached $(basename "$f")"
|
||||
done
|
||||
46
README.md
46
README.md
@@ -2,6 +2,52 @@
|
||||
|
||||
High-performance external mining service for Quantus Network with support for CPU, GPU, and hybrid CPU+GPU mining.
|
||||
|
||||
## This fork
|
||||
|
||||
Like the official miner, but fast: a native CUDA engine for NVIDIA cards, kept
|
||||
bit-exact with the reference hash and measured against every other miner we
|
||||
can get hold of. No dev fee, no licence server, no telemetry, solo or pool.
|
||||
Apache-2.0, like upstream.
|
||||
|
||||
**Measured hashrate**, three interleaved 30-second rounds per card at the
|
||||
enforced power cap, memory clock locked at 810 MHz, CUDA 13.0 driver 580:
|
||||
|
||||
| card | power cap | this fork | upstream `--cuda-gpu` | quantusminer.com qpow-cuda 1.0.7 | quanpool-miner 6.2.4 (5% fee) |
|
||||
|---|---|---|---|---|---|
|
||||
| RTX 5090 | 400 W | **1104 MH/s** | 944 | 1171 | 1234 (1172 after fee) |
|
||||
| RTX 4090 | 250 W | **711 MH/s** | | | |
|
||||
| RTX 3060 | 130 W | **141 MH/s** | | | |
|
||||
|
||||
The 5090 column was measured on the same card in the same session
|
||||
(2026-09-13); the closed pool binaries were run in benchmark mode only. How
|
||||
the pool kernels get their remaining edge, and what has been tried, is
|
||||
written up in [issue #27](https://git.lair.cafe/quantus/miner/issues/27).
|
||||
|
||||
**Binaries** are on the [releases page](https://git.lair.cafe/quantus/miner/releases):
|
||||
one Linux x86_64 `quantus-miner` carrying cubins for sm_86 (RTX 30), sm_89
|
||||
(RTX 40) and sm_120 (RTX 50) plus PTX for anything newer, built on Fedora 43
|
||||
(glibc 2.42 or newer), and `quantus-bench`, the measurement harness. Nothing
|
||||
is compiled at run time; the driver needs CUDA 13.0 support (580.x).
|
||||
|
||||
**Reproduce the numbers.** `quantus-bench` pins the power limit it expects,
|
||||
warms up, reports the median of timed windows with their spread, and checks
|
||||
GPU hashes against the CPU reference for random jobs:
|
||||
|
||||
```bash
|
||||
# hashrate: median of 5 x 30 s windows, one worker thread per card
|
||||
./quantus-bench --duration-secs 30 --runs 5 --workers 1 --expect-power-limit 400
|
||||
|
||||
# parity: 2000 random jobs, every found hash recomputed on the CPU
|
||||
./quantus-bench --duration-secs 3 --runs 1 --parity-jobs 2000
|
||||
```
|
||||
|
||||
Compare builds with interleaved rounds (A, B, A, B, ...) rather than one
|
||||
block of runs each: on a power-capped card, clock drift between blocks looks
|
||||
like a 0.5% code change.
|
||||
|
||||
**Solo mining** against your own node is the `serve` command below with the
|
||||
node's auth token and certificate fingerprint; nothing else is needed.
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
|
||||
@@ -317,6 +317,22 @@ __device__ __forceinline__ u64 gf_canon(u64 a) {
|
||||
#ifndef LAIR_TPB
|
||||
#define LAIR_TPB 256
|
||||
#endif
|
||||
// Minimum resident blocks per SM the compiler must fit (caps registers at
|
||||
// 65536 / (LAIR_TPB * LAIR_MINBLOCKS)); 0 leaves the register budget to ptxas.
|
||||
// Measured 2026-09-13 on beast against the 110-register default (2205 MH/s):
|
||||
// 64 registers by any route (1024 threads, or 256 x 4, or 512 x 2) spills
|
||||
// 216-264 bytes and loses 5% (2091-2107); 80 registers (256 x 3, 52 bytes of
|
||||
// spill) is neutral (2207). The quanpool kernel runs 1024 threads per SM at
|
||||
// 64 registers without spilling because its rounds are loops with the
|
||||
// constants in constant memory; this unrolled kernel cannot get there.
|
||||
#ifndef LAIR_MINBLOCKS
|
||||
#define LAIR_MINBLOCKS 0
|
||||
#endif
|
||||
#if LAIR_MINBLOCKS
|
||||
#define LAIR_LAUNCH_BOUNDS __launch_bounds__(LAIR_TPB, LAIR_MINBLOCKS)
|
||||
#else
|
||||
#define LAIR_LAUNCH_BOUNDS __launch_bounds__(LAIR_TPB)
|
||||
#endif
|
||||
// Decide most nonces on element 0 of the final state before computing the
|
||||
// rest of the last linear layer: at mainnet difficulty the top ~47 bits of
|
||||
// the hash must be zero, and they live in that element.
|
||||
@@ -726,7 +742,7 @@ static_assert(sizeof(MiningUniforms) == 320, "MiningUniforms must match the host
|
||||
__device__ __constant__ u64 LAIR_DIR7[12] = {1, 1, 3, 2, 2, 2, 6, 4, 1, 1, 3, 2};
|
||||
#endif
|
||||
|
||||
extern "C" __global__ void __launch_bounds__(LAIR_TPB)
|
||||
extern "C" __global__ void LAIR_LAUNCH_BOUNDS
|
||||
mining_main(u32* __restrict__ results,
|
||||
const MiningUniforms uni,
|
||||
u32 total_threads,
|
||||
|
||||
Reference in New Issue
Block a user