RTX 5060 Ti (16 GB, Razer Core X V2 Thunderbolt) on PC 2: detection, G1 bit-exact on class v4 sub-version 1 and the v3 control, the 10-minute stock window, the bench-log entry, the card-picker entry and the public table row
The first 16 GB card and the first Thunderbolt card. The app's first poll listed it (vendor nvidia, 16,311 MiB,
driver 610.47 shared with the 5090, kind discrete: the app does not know it is an eGPU) and started a miner on
it by itself. Job run-5060ti-bench-20261007-b (tools/bench-5060ti/pc2-5060ti-bench.ps1, the card alone under
the runner's --cards-off, the 5090 mining, no power limit written: 180 W default read back unchanged, PC 2 lost
power twice that day): G1 PASS with 867dbc45cfb36b4d (sub-version 1) and 90f794dd556f7a3b equal to the Mac's;
the window 1,105 dispatches of 2^24 in 602 s at 30.882 MH/s, 114.8 W mean at utilisation 90 percent and over,
0.269 MH/W, PCIe 4.0 x4 through the enclosure (the hash is latency-bound, the link costs nothing measurable).
Owed: the efficient point (the app's Ember Tune; PC 2's Power Helper refuses every request since the restart)
and the prove-beside read (the shipped WSL2 host has no IGNEUM_CUDA_DEVICE selector; the floor server is on
PC 2; the prover-floor host is the cut). The kit (tools/bench-5060ti/make-kit.sh) takes the sub-version 1 packs
from ca3-v4-amend 0a62293a, since master's packs-ca3-v4 is the 6 October stream.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
4af544c833
commit
8c019aa259
6 changed files with 396 additions and 1 deletions
|
|
@ -2652,3 +2652,32 @@ in the same shape and reports a box behind its wanted binary.
|
||||||
| Rig | the same, and a rig that leaves is itself a weight removal: at 459 MH/s on tonight's devnet it is about 20 percent of the weight, over the hour's budget by itself |
|
| Rig | the same, and a rig that leaves is itself a weight removal: at 459 MH/s on tonight's devnet it is about 20 percent of the weight, over the hour's budget by itself |
|
||||||
| Pool | a pool node is one voter carrying its members' whole weight; a pool restart is the largest single removal on the network and must be sliced like the fleet's |
|
| Pool | a pool node is one voter carrying its members' whole weight; a pool restart is the largest single removal on the network and must be sliced like the fleet's |
|
||||||
| The network | finality by miner weight is only as steady as the miners' uptime; until public hash dwarfs the fleet, the fleet's supervisor is a consensus component |
|
| The network | finality by miner weight is only as steady as the miners' uptime; until public hash dwarfs the fleet, the fleet's supervisor is a consensus component |
|
||||||
|
|
||||||
|
## 7 October 2026, the first 16 GB card: an RTX 5060 Ti in a Thunderbolt enclosure on PC 2 (branch bench-5060ti)
|
||||||
|
|
||||||
|
Machine: PC 2 (`1ccfe586`, Windows 11), an ASUS Dual GeForce RTX 5060 Ti (16 GB GDDR7, Blackwell sm_120, PnP `PCI\VEN_10DE&DEV_2D04&SUBSYS_8A111043`) in a Razer Core X V2 Thunderbolt enclosure ("USB4 Router (2.0), Razer - Core X V2", bus `0B:00.0`), beside the RTX 5090 on its own supply; NVIDIA driver 610.47 (WDDM 32.0.16.1047, the 5090's driver, nothing installed for the new card); the installed app 0.3.19 and its own `igneum-worker-cuda.exe` (NVRTC 12.8). Jobs `fetch-5060ti-packs-20261007` (the kit: `tools/bench-5060ti/make-kit.sh`, the class v4 pack at sub-version 1 and the v3 control, sha256 `fd8393ed...`, 105,892 bytes) and `run-5060ti-bench-20261007-b` (`tools/bench-5060ti/pc2-5060ti-bench.ps1`, 14:44:19Z, ran 14:45:05 to 14:58:11Z, exit 0; the 5060 Ti alone through the runner's `--cards-off`, the 5090 mining throughout; run `-a` died in 1 s on an argument-binding fault in the nvidia-smi query and is void). PC 2 lost power twice that day, so the job WRITES NO POWER LIMIT: it reads `power.limit` against `power.default_limit` (180 W = 180 W, range 150 to 198 W) and the row says `limit_is_stock=yes`. Read back with `node tools/jobs.mjs run-5060ti-bench-20261007-b`.
|
||||||
|
|
||||||
|
**Detection** (the app's first poll after the restart, run `win-1ccfe586-20261007-143611`, 14:36:16Z): `GPUs: NVIDIA GeForce RTX 5090 (CUDA); NVIDIA GeForce RTX 5060 Ti (CUDA); AMD Radeon(TM) Graphics (OpenCL, gfx1036)` and `cards: NVIDIA GeForce RTX 5090 [discrete, off] | NVIDIA GeForce RTX 5060 Ti [discrete, off] | ...`; the app started a miner on it by itself (`nvidia-1ccfe586-2`, `--device 1`, 8 identities, the default). The kind reads `discrete`, not `external`: the app does not know it is an eGPU. nvidia-smi in the job: index 1, 16,311 MiB, PCIe link gen 4 x4 current against gen 4 x16 maximum (the Thunderbolt link: a quarter of the slot's lanes), 43 C idle. The freeze lane's cause class for the 15:10 UK hang on the first boot with the card: not the card (no TDR, no Thunderbolt or PCIe link event; Kernel-Power 41 + 6008, no bugcheck, the power shape again).
|
||||||
|
|
||||||
|
**G1 and the window** (the installed CUDA worker, `--bench --batch-log2 24 --block-warps 1`, the card alone, `CUDA_VISIBLE_DEVICES` on its UUID so every row names the device; nvidia-smi every 2 s on the card, the loaded samples at utilisation 90 percent and over):
|
||||||
|
|
||||||
|
| Pack | Dispatches of 2^24 | Self-test | Fingerprint 2^24 at base 0 | MH/s |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| mx8-devnet-epoch0 (the class v3 control) | 5 | PASS | 90f794dd556f7a3b (= the control everywhere) | 30.895 |
|
||||||
|
| v4-devnet-epoch0 (class v4, sub-version 1, program id 1a4230699a6b9c60) | 5 | PASS | 867dbc45cfb36b4d (= Metal, Apple OpenCL, the RTX 5090) | 30.879 |
|
||||||
|
| v4-devnet-epoch0, the 10-minute window at the stock limit | 1,105 (602 s) | PASS | 867dbc45cfb36b4d | 30.882 |
|
||||||
|
|
||||||
|
| Row | Value |
|
||||||
|
|---|---|
|
||||||
|
| NVIDIA RTX 5060 Ti 16 GB, class v4, CUDA (NVRTC), driver 610.47, PCIe 4.0 x4 through the enclosure | 30.9 MH/s over 10 minutes on the card alone |
|
||||||
|
| Watts at the stock limit (180 W default, unchanged) | 114.8 W mean, 115 W p50 over the window; 0.269 MH/W; SM 2,753 MHz, memory 13,801 MHz, 60 C maximum |
|
||||||
|
| The class v4 shadow against the control | 0.1 percent (the 5090 paid 0.2, the 9070 XT 3, the B580 0.1) |
|
||||||
|
| The efficient point | OWED to the app's Ember Tune: nothing set by the job; PC 2's Power Helper refused every request since the restart ("the helper did not run sequence 0 within 15 s", 14:39Z), so no ladder ran on either card |
|
||||||
|
| Prove beside the miner (16 GB tier) | BLOCKED, not measured: the shipped WSL2 host (sha `71bc2438...`) carries no `IGNEUM_CUDA_DEVICE` selector, so aimed at anything it proves on CUDA device 0 (the 5090) through the app's own socket `/tmp/sp1-cuda-0.sock`; the selector lives in the prover-floor host (`proof_system.rs`, branch prover-floor) and is the owed cut. The job's inventory: the floor server IS on PC 2 (`/opt/igneum-floor/bin/sp1-gpu-server`, 6.8.1 build `e911facb...`, 166,665,880 bytes) beside the stock one (`~/.sp1/bin`, `c2642ad1...`), WSL sees the card as CUDA device 1 |
|
||||||
|
| Card-picker entry (`site/yourcard.js`) | `['NVIDIA RTX 5060 Ti', 30.9]`, added; the public table row in `site/miner-bench.json` |
|
||||||
|
|
||||||
|
Against the 5090 on the same PC (122 MH/s at 308 W, 0.396 MH/W): 25.3 percent of its hash at 37 percent of its draw, 68 percent of its hash per watt. The dependent-read ceiling was not probed (the memprobe step is not in this job); at 128 loads a hash 30.9 MH/s is 3.95 G dependent reads a second, between the 9070 XT (2.4 to 2.7 G) and the 5090 (16 to 18 G).
|
||||||
|
|
||||||
|
Consequences per tier (the rule of 5 October 2026): a 5060 Ti owner (16 GB, Windows) mines at 30.9 MH/s and 115 W from the box with nothing to set: about 5,100 blocks a day at the 522 MH/s the devnet showed at 14:44Z (one every 17 s, approximate: the network rate moves), about a quarter of a 5090 owner's 20,200, for 2.76 kWh a day (£0.79 at 28.5 p against the 5090's £2.11); through a Thunderbolt enclosure the x4 link costs nothing measurable (the hash is bound by the card's own memory latency, not the link; the 5090's PCIe-slot rows are the comparison), so a laptop with a Thunderbolt 4 port and this enclosure is a 31 MH/s miner. The 8 GB 5060 Ti: the same hash is the expectation (the 1 GiB dataset fits), a line owed. Proving on the 16 GB tier: the fleet's 4060 Ti 16 GB row (9.0 GB peak beside the miner on the patched server) says this card would mine and prove with about 7 GB spare, approximate until the host with the device selector ships; today the app's prover default leaves it off ("a full shard needs a 24 GB card") and the measured read is owed to the prover-floor host cut. Linux and HiveOS take the same CUDA worker (owed a line). What the lane does next: the prover-floor host's selector into the shipped WSL2 bundle, then the prove-beside read on this card; the Power Helper fault on PC 2 to the Ember lane (no efficient point on any PC 2 card until it answers).
|
||||||
|
|
||||||
|
Found on the way: inside a PowerShell `@( ... )` the comma binds before `+`, so `'--query-gpu=' + $f, '--format=csv'` is one argument (run a, void in 1 s; the query string is built first now); a bare string inside a function that also returns a value is swallowed into the caller's variable (the sampler line; `[Console]::Out.WriteLine` now); the app's `kind` for a Thunderbolt card reads `discrete` (a word for the Cards page to earn: `external`, which the state already names).
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,17 @@
|
||||||
"source": "bench log: 4 October 2026, first outside machine on the devnet: an Apple silicon laptop through the Igneum Miner app",
|
"source": "bench log: 4 October 2026, first outside machine on the devnet: an Apple silicon laptop through the Igneum Miner app",
|
||||||
"by": "reported by the fleet",
|
"by": "reported by the fleet",
|
||||||
"note": "21.0 MH/s average over 7 minutes, 24.3 MH/s at the moment of the report, 33 accepted blocks"
|
"note": "21.0 MH/s average over 7 minutes, 24.3 MH/s at the moment of the report, 33 accepted blocks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"card": "NVIDIA RTX 5060 Ti (16 GB)",
|
||||||
|
"generator": "v2",
|
||||||
|
"mh_s": 30.9,
|
||||||
|
"mh_per_w": 0.269,
|
||||||
|
"miner": "Igneum Miner 0.3.19 package, prebuilt NVRTC worker (bench mode, class v4 program)",
|
||||||
|
"date": "2026-10-07",
|
||||||
|
"source": "bench log: 7 October 2026, the first 16 GB card: an RTX 5060 Ti in a Thunderbolt enclosure (run b)",
|
||||||
|
"by": "measured by the team",
|
||||||
|
"note": "class v4 program, 128 loads per hash, 1 GiB dataset, 10-minute window on the card alone at the stock 180 W limit: 114.8 W mean; PCIe 4.0 x4 through the enclosure"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
second is the target, so the wait is network hashes per second over the card's rate. The rates are the bench table's
|
second is the target, so the wait is network hashes per second over the card's rate. The rates are the bench table's
|
||||||
measured numbers (site/miners.html); nothing here is a promise. Reads /api/live every 15 s. */
|
measured numbers (site/miners.html); nothing here is a promise. Reads /api/live every 15 s. */
|
||||||
(function () {
|
(function () {
|
||||||
var CARDS = [['NVIDIA RTX 5090', 127.7], ['NVIDIA RTX 4070', 28.8], ['Apple M5 Max', 26.7], ['AMD RX 9070 XT', 17.8]];
|
var CARDS = [['NVIDIA RTX 5090', 127.7], ['NVIDIA RTX 5060 Ti', 30.9], ['NVIDIA RTX 4070', 28.8], ['Apple M5 Max', 26.7], ['AMD RX 9070 XT', 17.8]];
|
||||||
var els = document.querySelectorAll('[data-yourcard]'); if (!els.length) return;
|
var els = document.querySelectorAll('[data-yourcard]'); if (!els.length) return;
|
||||||
var net = null, chosen = (function () { try { return localStorage.getItem('igneum-card'); } catch (e) { return null; } })();
|
var net = null, chosen = (function () { try { return localStorage.getItem('igneum-card'); } catch (e) { return null; } })();
|
||||||
function dur(sec) { if (!(sec > 0)) return 'pending'; if (sec < 90) return Math.round(sec) + ' s'; if (sec < 5400) return Math.round(sec / 60) + ' min'; if (sec < 172800) { var h = Math.floor(sec / 3600), m = Math.round((sec % 3600) / 60); return h + ' h' + (m ? ' ' + m + ' min' : ''); } return Math.round(sec / 86400) + ' days'; }
|
function dur(sec) { if (!(sec > 0)) return 'pending'; if (sec < 90) return Math.round(sec) + ' s'; if (sec < 5400) return Math.round(sec / 60) + ' min'; if (sec < 172800) { var h = Math.floor(sec / 3600), m = Math.round((sec % 3600) / 60); return h + ' h' + (m ? ' ' + m + ' min' : ''); } return Math.round(sec / 86400) + ' days'; }
|
||||||
|
|
|
||||||
32
tools/bench-5060ti/README.md
Normal file
32
tools/bench-5060ti/README.md
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# The RTX 5060 Ti in a Razer Core X V2 on PC 2: the jobs (7 October 2026, branch bench-5060ti)
|
||||||
|
|
||||||
|
The card: an ASUS Dual GeForce RTX 5060 Ti (16 GB, PnP `PCI\VEN_10DE&DEV_2D04&SUBSYS_8A111043`) in a Razer Core X V2 Thunderbolt enclosure ("USB4 Router (2.0), Razer - Core X V2") on PC 2 (`1ccfe586`, DESKTOP-KMCV30N), beside the RTX 5090 on its own supply. PC 2 lost power twice on 7 October, so NOTHING here writes a power limit: the playbook reads `power.limit` against `power.default_limit` and says whether they were equal; it carries no `nvidia-smi -pl`, `-lgc` or `-lmc`. Every job is a signed `run` job through `packaging/ota/publish-jobs.sh` from the main checkout (its signer binary lives there), published under the PC 2 mkdir lock (`/tmp/igneum-devnet/pc2-ca3.lock`, a holder note inside, removed when the bench ends), with the RUNNER's `--cards-off` for the 5060 Ti alone (the 5090 keeps mining), never `--stop-miners`, never pausing, resuming or quitting the installed app (`tools/ci/playbook-quit-check.sh`).
|
||||||
|
|
||||||
|
## 1. Detection (the intake, no job needed)
|
||||||
|
|
||||||
|
The app's first poll after the restart that followed the 15:10 UK freeze (run `win-1ccfe586-20261007-143611`, 14:36:16Z): `GPUs: NVIDIA GeForce RTX 5090 (CUDA); NVIDIA GeForce RTX 5060 Ti (CUDA); AMD Radeon(TM) Graphics (OpenCL, gfx1036)` and `cards: NVIDIA GeForce RTX 5090 [discrete, off] | NVIDIA GeForce RTX 5060 Ti [discrete, off] | AMD Radeon(TM) Graphics [integrated, off]`; the app then started a miner on it (`nvidia-1ccfe586-2`, `--device 1`, 8 identities, the default). The kind reads `discrete`, not `external`: the app does not know it is an eGPU. The freeze lane's `pc2-freeze-check-20261007` (14:38:22Z): nvidia-smi index 1, driver 610.47 (WDDM 32.0.16.1047, the 5090's driver, nothing to install), 16,311 MiB, bus `0B:00.0`, status OK; the freeze was NOT the card (no TDR, no Thunderbolt or PCIe link event; Kernel-Power 41 + 6008, no bugcheck: the power shape again).
|
||||||
|
|
||||||
|
## 2. The kit and the bench
|
||||||
|
|
||||||
|
The kit (packs only; the INSTALLED `igneum-worker-cuda.exe` is the worker under test): the class v4 pack `v4-devnet-epoch0` at SUB-VERSION 1 (the stream 0.3.20 ships at object byte 5, program id 1a4230699a6b9c60, fingerprint 867dbc45cfb36b4d) and the v3 control `mx8-devnet-epoch0` (90f794dd556f7a3b), both as commit a0aaca92 on ca3-v4-amend exported them (master's `proto-cuda/packs-ca3-v4` is the older 6 October stream, f410c731b6bc2d31, not the one to measure against).
|
||||||
|
|
||||||
|
tools/bench-5060ti/make-kit.sh "$TMPDIR/igneum-5060ti-packs.zip" # prints sha256 and bytes; checks the vectors prefix 756301bf
|
||||||
|
packaging/ota/publish-jobs.sh add --kind fetch --target 1ccfe586 --id fetch-5060ti-packs-20261007 \
|
||||||
|
--file "$TMPDIR/igneum-5060ti-packs.zip" --dir jobs --extract --title "RTX 5060 Ti bench packs (class v4 sub-version 1 and the v3 control)" --deploy
|
||||||
|
|
||||||
|
Published 14:40:24Z: sha256 `fd8393ed37c4f311b4e34b131440f9e606e0557b7006e12d1696686753d3ba71`, 105,892 bytes; landed 14:41:11Z, sha256 ok (an app update wipes the jobs folder: republish under a new id after one).
|
||||||
|
|
||||||
|
Then the bench, the live network rate and the kit id substituted:
|
||||||
|
|
||||||
|
NET=$(curl -s "https://igneum.network/api/live?window=30" | python3 -c "import json,sys;print(round(json.load(sys.stdin)['state']['hashes_per_second_estimate']/1e6,1))")
|
||||||
|
sed -e "s/__NETWORK_MHS__/$NET/" -e "s/__KIT_ID__/fetch-5060ti-packs-20261007/" tools/bench-5060ti/pc2-5060ti-bench.ps1 > "$TMPDIR/pc2-5060ti-bench.ps1"
|
||||||
|
packaging/ota/publish-jobs.sh add --kind run --target 1ccfe586 --id run-5060ti-bench-<date> \
|
||||||
|
--cards-off "nvidia:NVIDIA GeForce RTX 5060 Ti,nvidia:1:NVIDIA GeForce RTX 5060 Ti" \
|
||||||
|
--script "$TMPDIR/pc2-5060ti-bench.ps1" --timeout-minutes 45 --title "RTX 5060 Ti (Razer Core X V2) on PC 2: ..." --deploy
|
||||||
|
|
||||||
|
What it prints (`node tools/jobs.mjs <job id>`): `RESULT gpu ...` (every card's nvidia-smi row: driver, VRAM, the PCIe gen and width = the Thunderbolt link, power.limit against power.default_limit), `RESULT DETECT ...`, `RESULT app_card ...` (read only), `RESULT card ... quiet` (no compute process on the card's UUID: the runner's `--cards-off` took), `RESULT G1 pack=... fingerprint= match=yes|no` for both packs (the worker sees the 5060 Ti alone through `CUDA_VISIBLE_DEVICES` on its UUID and every row names the device it ran on; a row on another card is void), `RESULT sampler stock ...` (nvidia-smi every 2 s on the card over the 10-minute window: mean, p50 and max watts at utilisation 90 percent and over, clocks, temperature), `RESULT bench v4-stock-window ...` (the worker's `--bench` with enough 2^24 dispatches for 600 s: the MH/s is the mean over them), `RESULT tune ...` (the app's own TUNE lines for the card, read only: the efficient point is Ember Tune's to find, its ladder only steps DOWN), `RESULT PROVE ...` (16 GB only: the app's WSL2 host aimed at the 5060 Ti with `IGNEUM_CUDA_DEVICE` on one live shard while a bench loop hashes on the card, VRAM peak from the sampler; BLOCKED with the reason when the shipped host carries no selector, since aiming the stock host proves on CUDA device 0, the 5090, through the app's own socket), then `RESULT ROW ...`, `RESULT PICKER ['NVIDIA RTX 5060 Ti', <mhs>]`, `RESULT CONSEQUENCES ...` and a `SUMMARY {json}` line.
|
||||||
|
|
||||||
|
| Run | Published | Outcome |
|
||||||
|
|---|---|---|
|
||||||
|
| `run-5060ti-bench-20261007` | 14:42:27Z | VOID in 1 s: the nvidia-smi query and its format flag were one argument (inside `@( ... )` the comma binds before `+`), "temperature.gpu --format=csv is not a valid field"; the query string is built first now. Run b's sampler line was swallowed the same way the Intel lane's Bench lines were (a bare string in a function that returns a value goes into the caller's variable); `[Console]::Out.WriteLine` now |
|
||||||
|
| `run-5060ti-bench-20261007-b` | 14:44:19Z | ran 14:45:05 to 14:58:11Z, exit 0: the card alone from 0 s, G1 PASS with 867dbc45cfb36b4d and 90f794dd556f7a3b equal to the Mac's (30.879 and 30.895 MH/s at five dispatches), the window 1,105 dispatches in 602 s at 30.882 MH/s and 114.8 W mean (p50 115) at the stock 180 W limit, 0.269 MH/W, SM 2,753 MHz, 60 C; no TUNE line for the card; the prove read BLOCKED (the shipped host has no `IGNEUM_CUDA_DEVICE` selector; the floor server is on PC 2 at `/opt/igneum-floor/bin`, WSL device 1); the bench-log entry, the picker entry and the table row |
|
||||||
34
tools/bench-5060ti/make-kit.sh
Executable file
34
tools/bench-5060ti/make-kit.sh
Executable file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# The packs kit for tools/bench-5060ti/pc2-5060ti-bench.ps1 (7 October 2026, branch bench-5060ti): the class v4 pack
|
||||||
|
# v4-devnet-epoch0 at SUB-VERSION 1 (generator 4, program id 1a4230699a6b9c60, the stream 0.3.20 ships at object byte 5;
|
||||||
|
# 2^24 fingerprint 867dbc45cfb36b4d, Metal = Apple OpenCL = the RTX 5090 on PC 2, job run-ca3-v4-amend-g1-pc2-20261007)
|
||||||
|
# and the class v3 control mx8-devnet-epoch0 (73bcbfe8ccf988f1, fingerprint 90f794dd556f7a3b), both as commit a0aaca92
|
||||||
|
# on ca3-v4-amend exported them (the branch moved on to sub-versions 2 and 3; master's proto-cuda/packs-ca3-v4 is the
|
||||||
|
# 6 October stream c120d7963abdcd96, fingerprint f410c731b6bc2d31, which is NOT the one the bench measures against).
|
||||||
|
# Packs only: the INSTALLED igneum-worker-cuda.exe on PC 2 is the worker under test, so no exe travels.
|
||||||
|
# tools/bench-5060ti/make-kit.sh [out.zip] default $TMPDIR/igneum-5060ti-packs.zip
|
||||||
|
# Prints the zip's sha256 and size: the README's fetch line takes them. Checks the vectors.json sha256 prefixes the
|
||||||
|
# ledger recorded for sub-version 1 (756301bf for the devnet epoch 0 pack) before packing.
|
||||||
|
set -euo pipefail
|
||||||
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
ROOT="$(cd "$HERE/../.." && pwd)"
|
||||||
|
OUT="${1:-${TMPDIR:-/tmp}/igneum-5060ti-packs.zip}"
|
||||||
|
COMMIT=a0aaca92
|
||||||
|
STAGE="$(mktemp -d)"
|
||||||
|
git -C "$ROOT" cat-file -e "$COMMIT^{commit}" 2>/dev/null || { echo "commit $COMMIT (ca3-v4-amend, the sub-version 1 packs) is not in this repository" >&2; exit 1; }
|
||||||
|
git -C "$ROOT" archive "$COMMIT" proto-cuda/packs-ca3-v4/mx8-devnet-epoch0 proto-cuda/packs-ca3-v4/v4-devnet-epoch0 | tar -x -C "$STAGE"
|
||||||
|
mkdir -p "$STAGE/packs"
|
||||||
|
mv "$STAGE/proto-cuda/packs-ca3-v4/mx8-devnet-epoch0" "$STAGE/proto-cuda/packs-ca3-v4/v4-devnet-epoch0" "$STAGE/packs/"
|
||||||
|
rm -rf "$STAGE/proto-cuda"
|
||||||
|
v="$(shasum -a 256 "$STAGE/packs/v4-devnet-epoch0/vectors.json" | cut -c1-8)"
|
||||||
|
[ "$v" = 756301bf ] || { echo "v4-devnet-epoch0/vectors.json sha256 prefix $v, expected 756301bf (sub-version 1)" >&2; exit 1; }
|
||||||
|
grep -q '"sub_version": 1' "$STAGE/packs/v4-devnet-epoch0/program.json" || { echo "v4-devnet-epoch0 is not sub-version 1" >&2; exit 1; }
|
||||||
|
grep -q '"program_id": "0x1a4230699a6b9c60"' "$STAGE/packs/v4-devnet-epoch0/program.json" || { echo "v4-devnet-epoch0 program id is not 1a4230699a6b9c60" >&2; exit 1; }
|
||||||
|
# a copied tree is re-stamped before anything uses it (CLAUDE.md, the stale-build class); the packs are read, not built
|
||||||
|
find "$STAGE" -type f -exec touch {} +
|
||||||
|
( cd "$STAGE" && find . -type f ! -name SHA256SUMS | sort | xargs shasum -a 256 > SHA256SUMS )
|
||||||
|
rm -f "$OUT"
|
||||||
|
( cd "$STAGE" && zip -X -q -r "$OUT" . )
|
||||||
|
rm -rf "$STAGE"
|
||||||
|
echo "kit $OUT (packs from $COMMIT: v4-devnet-epoch0 sub-version 1, mx8-devnet-epoch0 the control)"
|
||||||
|
echo "sha256 $(shasum -a 256 "$OUT" | cut -c1-64) bytes $(wc -c < "$OUT" | tr -d ' ')"
|
||||||
289
tools/bench-5060ti/pc2-5060ti-bench.ps1
Normal file
289
tools/bench-5060ti/pc2-5060ti-bench.ps1
Normal file
|
|
@ -0,0 +1,289 @@
|
||||||
|
# Igneum run job: the ASUS Dual GeForce RTX 5060 Ti in a Razer Core X V2 (Thunderbolt) on PC 2 (machine 1ccfe586),
|
||||||
|
# 7 October 2026 (branch bench-5060ti). Published as a signed `run` job with the RUNNER's --cards-off <the 5060 Ti's key>
|
||||||
|
# (NOT --stop-miners): the runner switches the 5060 Ti off through the app's own card path before this script and puts
|
||||||
|
# it back on ANY exit; the RTX 5090 keeps mining. This script never switches a card, never stops, holds or ends the
|
||||||
|
# installed app (the playbook-quit rule, tools/ci/playbook-quit-check.sh); it reads api/state once, read only.
|
||||||
|
# POWER RULE (the project lead, 7 October 2026: PC 2 lost power twice today): nothing here raises any card's power limit. There is
|
||||||
|
# no nvidia-smi -pl, -lgc or -lmc anywhere in this file; the limits are READ (power.limit, power.default_limit) and the
|
||||||
|
# row says whether they were equal. The efficient point is the app's own Ember Tune's to find (its ladder only steps
|
||||||
|
# DOWN from the stock limit); this job sets nothing and reads the app's TUNE lines for the card, if any.
|
||||||
|
# After the fetch job that places the two packs (tools/bench-5060ti/make-kit.sh: v4-devnet-epoch0 at sub-version 1,
|
||||||
|
# the stream 0.3.20 ships, fingerprint 867dbc45cfb36b4d; mx8-devnet-epoch0 the class v3 control, 90f794dd556f7a3b).
|
||||||
|
# Steps: detect (nvidia-smi: name, VRAM, driver, the PCIe link gen and width = the Thunderbolt link; the Windows
|
||||||
|
# adapter rows; the app's own card row, read only), the card quiet (no compute process on its UUID), G1 (self-test and
|
||||||
|
# the 2^24 fingerprint on both packs, bit-exact against the Mac), the 10-minute window at the stock limit (the worker's
|
||||||
|
# --bench with enough dispatches for 600 s, nvidia-smi sampled every 2 s on the card alone: MH/s and mean watts at
|
||||||
|
# utilisation 90 percent and over), the app's tune lines (read only), then, on a 16 GB card, the prove-beside-miner
|
||||||
|
# read: the app's own WSL2 prover host aimed at the 5060 Ti (IGNEUM_CUDA_DEVICE, the prover-floor host's selector)
|
||||||
|
# on one live shard while a bench loop hashes on the card, VRAM peak from the sampler; when the shipped host carries
|
||||||
|
# no selector the read is BLOCKED and the line says so (aiming the stock host would prove on the 5090 instead).
|
||||||
|
# Lines start with RESULT; a SUMMARY {json} line ends the job. Read back with `node tools/jobs.mjs <job id>`.
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
$jobName = 'pc2-5060ti-bench'
|
||||||
|
$kitId = '__KIT_ID__' # the publisher substitutes the fetch job id that placed the packs
|
||||||
|
$started = Get-Date
|
||||||
|
function Stamp { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') }
|
||||||
|
function Summary([string] $status, [hashtable] $extra) {
|
||||||
|
$o = [ordered]@{ job = $jobName; status = $status; duration_s = [int]((Get-Date) - $started).TotalSeconds; finished_at = (Stamp) }
|
||||||
|
foreach ($k in $extra.Keys) { $o[$k] = $extra[$k] }
|
||||||
|
'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4)
|
||||||
|
}
|
||||||
|
$expected = @{ 'v4-devnet-epoch0' = '867dbc45cfb36b4d'; 'mx8-devnet-epoch0' = '90f794dd556f7a3b' } # sub-version 1: Metal = Apple OpenCL = the RTX 5090 (job run-ca3-v4-amend-g1-pc2-20261007)
|
||||||
|
$networkMhs = [double] '__NETWORK_MHS__' # the publisher substitutes live_state.hashes_per_second_estimate / 1e6; 0 = unknown, the line says so
|
||||||
|
if ($networkMhs -isnot [double] -or [double]::IsNaN($networkMhs)) { $networkMhs = 0 }
|
||||||
|
$subsidyIgn = 31.69 # IGN a block, spec 02 section 2.5 (1 block a second)
|
||||||
|
$ref5090Mhs = 122.0; $ref5090W = 308.0 # the 5090's row on PC 2 (main's figures for the per-tier sentence)
|
||||||
|
$windowS = 600
|
||||||
|
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION network_mhs=$networkMhs"
|
||||||
|
$jobs = Split-Path $env:IGNEUM_JOB_DIR
|
||||||
|
$kit = Join-Path $jobs $kitId
|
||||||
|
if (-not (Test-Path (Join-Path $kit 'packs\v4-devnet-epoch0\program.h'))) { "RESULT error kit missing at $kit (the fetch job $kitId runs first; an app update wipes the jobs folder: republish it under a new id after one)"; Summary 'failed' @{ error = 'kit missing' }; exit 2 }
|
||||||
|
$job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-5060ti' }; New-Item -ItemType Directory -Force -Path $job | Out-Null
|
||||||
|
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-worker-cuda.exe') } | Select-Object -First 1
|
||||||
|
$appDir = if ($env:IGNEUM_APP_DIR) { $env:IGNEUM_APP_DIR } else { Join-Path $env:LOCALAPPDATA 'igneum\app' }
|
||||||
|
if (-not $inst) { "RESULT error no installed igneum-worker-cuda.exe"; Summary 'failed' @{ error = 'no worker' }; exit 2 }
|
||||||
|
$exe = Join-Path $inst 'igneum-worker-cuda.exe'
|
||||||
|
"RESULT app install=$inst app_dir=$appDir worker=$exe sha256=$((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower())"
|
||||||
|
foreach ($f in (Get-ChildItem (Join-Path $kit 'packs') -Recurse -File | Sort-Object FullName)) { "RESULT kitfile $($f.FullName.Substring($kit.Length + 1).Replace('\', '/')) sha256 $((Get-FileHash -Algorithm SHA256 $f.FullName).Hash.ToLower()) bytes $($f.Length)" }
|
||||||
|
|
||||||
|
# ---- detect: nvidia-smi's rows (the Thunderbolt link is the PCIe gen and width the card reports), Windows' adapter rows, the app's row ----
|
||||||
|
function Smi([string[]] $argv) { try { @(& nvidia-smi @argv 2>&1 | ForEach-Object { "$_" }) } catch { @("nvidia-smi failed: $($_.Exception.Message)") } }
|
||||||
|
$gpuFields = 'index,uuid,name,driver_version,memory.total,memory.used,pcie.link.gen.current,pcie.link.gen.max,pcie.link.width.current,pcie.link.width.max,power.draw,power.limit,power.default_limit,power.min_limit,power.max_limit,clocks.max.sm,clocks.max.mem,temperature.gpu'
|
||||||
|
# the query is ONE string built first: inside @( ... ) the comma binds before +, so '--query-gpu=' + $f, '--format=...'
|
||||||
|
# became a single argument carrying the format words (run a, 14:43:29Z: "temperature.gpu --format=csv is not a valid field")
|
||||||
|
$gpuQuery = "--query-gpu=$gpuFields"
|
||||||
|
$rows = @(Smi @($gpuQuery, '--format=csv,noheader,nounits'))
|
||||||
|
$rows | ForEach-Object { "RESULT gpu $_" }
|
||||||
|
$card = $null
|
||||||
|
foreach ($r in $rows) {
|
||||||
|
$c = @($r -split ',\s*')
|
||||||
|
if ($c.Count -ge 18 -and $c[2] -match '5060 Ti') { $card = [pscustomobject]@{ index = [int]$c[0]; uuid = $c[1]; name = $c[2]; driver = $c[3]; mem_total = [int]$c[4]; gen = $c[6]; gen_max = $c[7]; width = $c[8]; width_max = $c[9]; plimit = [double]$c[11]; pdefault = [double]$c[12]; pmin = $c[13]; pmax = $c[14]; clk_sm_max = $c[15]; clk_mem_max = $c[16] }; break }
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
foreach ($a in @(Get-CimInstance Win32_VideoController -ErrorAction Stop)) { "RESULT adapter name=`"$($a.Name)`" driver=$($a.DriverVersion) status=$($a.Status) code=$($a.ConfigManagerErrorCode) pnp=$($a.PNPDeviceID)" }
|
||||||
|
} catch { "RESULT adapter error=$($_.Exception.Message)" }
|
||||||
|
if ($null -eq $card) { "RESULT error no RTX 5060 Ti in nvidia-smi's list (the rows above are what the driver sees; is the enclosure's Thunderbolt link up and the card seated?)"; Summary 'failed' @{ error = 'no 5060 Ti in nvidia-smi' }; exit 2 }
|
||||||
|
$tier = if ($card.mem_total -ge 15000) { 16 } elseif ($card.mem_total -ge 7000) { 8 } else { [math]::Round($card.mem_total / 1024) }
|
||||||
|
"RESULT DETECT name=`"$($card.name)`" vendor=nvidia vram_mib=$($card.mem_total) tier_gb=$tier driver=$($card.driver) pcie_link=gen$($card.gen)x$($card.width) pcie_max=gen$($card.gen_max)x$($card.width_max) power_limit_w=$($card.plimit) power_default_w=$($card.pdefault) power_range_w=$($card.pmin)-$($card.pmax) limit_is_stock=$(if ($card.plimit -eq $card.pdefault) { 'yes' } else { 'NO' }) smi_index=$($card.index) uuid=$($card.uuid)"
|
||||||
|
# READ ONLY: the app's view of the card (its key is what the publisher's --cards-off named; the runner, not this script, switched it)
|
||||||
|
$cardKey = ''; $appDevice = ''
|
||||||
|
try {
|
||||||
|
$urlFile = Join-Path $appDir 'app.url'
|
||||||
|
if (Test-Path $urlFile) {
|
||||||
|
$url = (Get-Content -LiteralPath $urlFile -Raw).Trim()
|
||||||
|
$st = Invoke-RestMethod -Uri ($url + 'api/state') -Method GET -TimeoutSec 10
|
||||||
|
$all = $st.mining.cards; if (-not $all) { $all = $st.cards }
|
||||||
|
foreach ($c in @($all | Where-Object { $_.name -match '5060' })) { $cardKey = $c.key; $appDevice = "$($c.device)"; "RESULT app_card key=$($c.key) vendor=$($c.vendor) kind=$($c.kind) enabled=$($c.enabled) identities=$($c.identities) state=$($c.state) device=$($c.device) hash_now=$($c.hash_now) vram_mb=$($c.vram_mb) reason=`"$($c.reason)`" message=`"$($c.message)`"" }
|
||||||
|
if (-not $cardKey) { "RESULT app_card none: the app's state lists no card named 5060 (cards: $(@($all | ForEach-Object { $_.name }) -join ' | '))" }
|
||||||
|
} else { "RESULT app_card no app.url under $appDir" }
|
||||||
|
} catch { "RESULT app_card error=$($_.Exception.Message)" }
|
||||||
|
|
||||||
|
# the card quiet? read only, by the driver's compute list on the card's UUID: the runner's --cards-off took, or not
|
||||||
|
function CardApps { @(Smi @('--query-compute-apps=pid,process_name,used_memory,gpu_uuid', '--format=csv,noheader,nounits') | Where-Object { $_ -match [regex]::Escape($card.uuid) }) }
|
||||||
|
$cardAlone = $false; $t = 0
|
||||||
|
while ($t -lt 150) { if ((@(CardApps)).Count -eq 0) { $cardAlone = $true; break }; Start-Sleep -Seconds 5; $t += 5 }
|
||||||
|
if ($cardAlone) { "RESULT card $(Stamp) quiet after $t s: no compute process on $($card.uuid) (the card to itself)" } else { "RESULT card $(Stamp) UNCONFIRMED after $t s: a compute process still runs on the card ($((CardApps) -join '; ')); was the job published with --cards-off $cardKey ? the rows below are LOADED-card figures" }
|
||||||
|
|
||||||
|
# The worker sees ONLY the 5060 Ti (CUDA_VISIBLE_DEVICES on its UUID) and takes --device 0; every RESULT line names
|
||||||
|
# the device it ran on, and a line naming another card voids the row. The 5090's process is never touched.
|
||||||
|
$env:CUDA_VISIBLE_DEVICES = $card.uuid
|
||||||
|
# Runs the worker as a child under a wall-clock cap; a child past its cap is ended by ITS OWN pid (taskkill /PID, never
|
||||||
|
# a name). Output comes back through a file; a trailing "timeout <cap>s" line says when the cap fired.
|
||||||
|
function Capped([string] $tag, [string[]] $argv, [int] $capS) {
|
||||||
|
$out = Join-Path $job ("$tag.out"); $errf = Join-Path $job "$tag.err"
|
||||||
|
Remove-Item -LiteralPath $out, $errf -Force -ErrorAction SilentlyContinue
|
||||||
|
$p = Start-Process -FilePath $exe -ArgumentList $argv -NoNewWindow -PassThru -RedirectStandardOutput $out -RedirectStandardError $errf
|
||||||
|
$done = $p.WaitForExit($capS * 1000)
|
||||||
|
if (-not $done) { try { & taskkill /T /F /PID $p.Id 2>&1 | Out-Null } catch { }; Start-Sleep -Seconds 2 }
|
||||||
|
$lines = @(); if (Test-Path -LiteralPath $out) { $lines = @(Get-Content -LiteralPath $out -ErrorAction SilentlyContinue) }
|
||||||
|
if (Test-Path -LiteralPath $errf) { $lines += @(Get-Content -LiteralPath $errf -ErrorAction SilentlyContinue) }
|
||||||
|
if (-not $done) { $lines += "timeout ${capS}s: the worker was ended by its pid $($p.Id)" }
|
||||||
|
if ($done) { try { $p.Refresh() } catch { } }
|
||||||
|
$script:lastExit = if ($done) { $p.ExitCode } else { 124 }
|
||||||
|
return $lines
|
||||||
|
}
|
||||||
|
$script:lastExit = 0
|
||||||
|
# the nvidia-smi sampler on the card's index, 2 s, to a file; ended by its pid
|
||||||
|
function SamplerStart([string] $tag) {
|
||||||
|
$f = Join-Path $job "$tag.csv"; Remove-Item -LiteralPath $f -Force -ErrorAction SilentlyContinue
|
||||||
|
$p = Start-Process -FilePath 'nvidia-smi' -ArgumentList @('-i', "$($card.index)", '--query-gpu=timestamp,power.draw,power.limit,clocks.sm,clocks.mem,temperature.gpu,utilization.gpu,memory.used', '--format=csv,noheader,nounits', '-lms', '2000') -NoNewWindow -PassThru -RedirectStandardOutput $f -RedirectStandardError (Join-Path $job "$tag.smi.err")
|
||||||
|
return [pscustomobject]@{ pid = $p.Id; file = $f }
|
||||||
|
}
|
||||||
|
function SamplerStop($s) { try { Stop-Process -Id $s.pid -Force -ErrorAction SilentlyContinue } catch { }; Start-Sleep -Seconds 1 }
|
||||||
|
# the window's numbers from a sampler file: samples at utilisation 90 and over are the loaded ones
|
||||||
|
function SamplerRead($s, [string] $tag) {
|
||||||
|
$all = @(); if (Test-Path -LiteralPath $s.file) { $all = @(Get-Content -LiteralPath $s.file -ErrorAction SilentlyContinue | Where-Object { $_ -match ',' }) }
|
||||||
|
$pw = @(); $pl = @(); $sm = @(); $mem = @(); $tmp = @(); $used = @(); $n = 0
|
||||||
|
foreach ($l in $all) { $c = @($l -split ',\s*'); if ($c.Count -lt 8) { continue }; $n++; $u = [double]$c[6]; $used += [double]$c[7]; if ($u -ge 90) { $pw += [double]$c[1]; $pl += [double]$c[2]; $sm += [double]$c[3]; $mem += [double]$c[4]; $tmp += [double]$c[5] } }
|
||||||
|
$o = [ordered]@{ samples = $n; loaded = $pw.Count; w_mean = 0; w_p50 = 0; w_max = 0; limit_max = 0; sm_mhz = 0; mem_mhz = 0; temp_max = 0; mem_used_peak_mib = 0 }
|
||||||
|
if ($pw.Count -gt 0) { $sorted = $pw | Sort-Object; $o.w_mean = [math]::Round(($pw | Measure-Object -Average).Average, 1); $o.w_p50 = [math]::Round($sorted[[int][math]::Floor($sorted.Count / 2)], 1); $o.w_max = [math]::Round(($pw | Measure-Object -Maximum).Maximum, 1); $o.limit_max = ($pl | Measure-Object -Maximum).Maximum; $o.sm_mhz = [math]::Round(($sm | Measure-Object -Average).Average); $o.mem_mhz = [math]::Round(($mem | Measure-Object -Average).Average); $o.temp_max = ($tmp | Measure-Object -Maximum).Maximum }
|
||||||
|
if ($used.Count -gt 0) { $o.mem_used_peak_mib = ($used | Measure-Object -Maximum).Maximum }
|
||||||
|
# the line goes to the host, not the pipeline: a bare string here was swallowed into the caller's variable (run b, 14:55Z: the ROW carried the watts, the sampler line never reached the report)
|
||||||
|
[Console]::Out.WriteLine("RESULT sampler $tag samples=$($o.samples) loaded=$($o.loaded) watts_mean=$($o.w_mean) watts_p50=$($o.w_p50) watts_max=$($o.w_max) limit_seen_w=$($o.limit_max) sm_mhz=$($o.sm_mhz) mem_mhz=$($o.mem_mhz) temp_max=$($o.temp_max) mem_used_peak_mib=$($o.mem_used_peak_mib) file=$($s.file)")
|
||||||
|
return $o
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- the benches ----
|
||||||
|
# A function's uncaptured output is its return value: Bench writes its lines into $script:benchOut; Flush prints them.
|
||||||
|
$script:benchOut = @()
|
||||||
|
function Say([string] $l) { $script:benchOut += $l }
|
||||||
|
function Flush { $script:benchOut | ForEach-Object { $_ }; $script:benchOut = @() }
|
||||||
|
function Bench([string] $label, [string] $packDir, [int] $batches, [int] $capS) {
|
||||||
|
if (-not (Test-Path $packDir)) { Say "RESULT bench $label error=pack missing at $packDir"; return $null }
|
||||||
|
$t0 = (Get-Date).ToUniversalTime()
|
||||||
|
Say "RESULT bench $label start $(Stamp) pack=$packDir batches=$batches"
|
||||||
|
$lines = @(Capped "bench-$label" @('--bench', '--pack', $packDir, '--batches', "$batches", '--batch-log2', '24', '--block-warps', '1', '--device', '0') $capS)
|
||||||
|
$rc = $script:lastExit
|
||||||
|
$wall = [int]((Get-Date).ToUniversalTime() - $t0).TotalSeconds
|
||||||
|
$lines | Where-Object { $_ -match '^RESULT|^warm-up|^info|^pack |^nvrtc|^self-test|FAIL|error|MISMATCH|^timeout' } | Select-Object -First 30 | ForEach-Object { Say "RESULT bench $label $_" }
|
||||||
|
Say "RESULT bench $label end $(Stamp) exit=$rc wall_s=$wall"
|
||||||
|
$res = $lines | Where-Object { $_ -match '^RESULT pack=' } | Select-Object -First 1
|
||||||
|
if ($null -eq $rc -or "$rc" -eq '') { $rc = 0 } # a null exit after WaitForExit on a -PassThru process: the RESULT line is the verdict
|
||||||
|
if (-not $res -or $rc -ne 0) {
|
||||||
|
$err = ($lines | Where-Object { $_ -match 'FAIL|error|MISMATCH' } | Select-Object -First 1); if (-not $err) { $err = "exit $rc, no RESULT line" }
|
||||||
|
Say "RESULT G1 pack=$label error=$($err -replace '\s+', ' ')"
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
$fp = ''; $mhs = ''; $check = ''; $dev = ''
|
||||||
|
if ($res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }
|
||||||
|
if ($res -match 'mhs=([\d.]+)') { $mhs = $Matches[1] }
|
||||||
|
if ($res -match 'check=(\S+)') { $check = $Matches[1] }
|
||||||
|
if ($res -match 'device=(\S+)') { $dev = $Matches[1] }
|
||||||
|
if ($dev -notmatch '5060') { Say "RESULT G1 pack=$label error=ran on device `"$dev`", not the 5060 Ti: the row is void"; return $null }
|
||||||
|
$want = $expected[$label]
|
||||||
|
$match = if ($want) { if ($fp -eq $want) { 'yes' } else { 'no' } } else { 'n/a' }
|
||||||
|
Say "RESULT G1 pack=$label fingerprint=$fp match=$match expected=$want check=$check device=$dev batches=$batches wall_s=$wall harness=cuda-installed"
|
||||||
|
return [pscustomobject]@{ label = $label; mhs = [double]$mhs; fp = $fp; match = $match; check = $check; wall_s = $wall; device = $dev }
|
||||||
|
}
|
||||||
|
# G1: five dispatches each, the fingerprints
|
||||||
|
$v4 = Bench 'v4-devnet-epoch0' (Join-Path $kit 'packs\v4-devnet-epoch0') 5 300; Flush
|
||||||
|
$v3 = Bench 'mx8-devnet-epoch0' (Join-Path $kit 'packs\mx8-devnet-epoch0') 5 300; Flush
|
||||||
|
# the 10-minute window at the stock limit: as many 2^24 dispatches as the five-batch rate says fit in 600 s
|
||||||
|
$stock = $null; $stockSmi = $null
|
||||||
|
if ($v4 -and $v4.mhs -gt 0) {
|
||||||
|
$n = [int][math]::Ceiling($windowS * $v4.mhs * 1e6 / 16777216); if ($n -lt 50) { $n = 50 }; if ($n -gt 6000) { $n = 6000 }
|
||||||
|
$s = SamplerStart 'stock'
|
||||||
|
Start-Sleep -Seconds 4
|
||||||
|
$stock = Bench 'v4-stock-window' (Join-Path $kit 'packs\v4-devnet-epoch0') $n ($windowS + 300); Flush
|
||||||
|
SamplerStop $s
|
||||||
|
$stockSmi = SamplerRead $s 'stock'
|
||||||
|
} else { "RESULT window skipped: no class v4 rate from G1" }
|
||||||
|
|
||||||
|
# ---- the efficient point: the app's Ember Tune's, read only (this job sets nothing; the ladder only ever steps DOWN) ----
|
||||||
|
try {
|
||||||
|
$logDir = $env:IGNEUM_LOG_DIR; if (-not $logDir) { $logDir = Join-Path $appDir 'logs' }
|
||||||
|
$newest = Get-ChildItem -Path $logDir -Filter 'app-*.log' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||||
|
$tuneLines = @()
|
||||||
|
if ($newest) { $tuneLines = @(Get-Content -LiteralPath $newest.FullName -ErrorAction SilentlyContinue | Where-Object { $_ -match '^\d+(\.\d+)? (TUNE|tune) ' -and $_ -match '5060' } | Select-Object -Last 8) }
|
||||||
|
if ($tuneLines.Count -gt 0) { $tuneLines | ForEach-Object { "RESULT tune $_" } } else { "RESULT tune none: the app has no TUNE line for the 5060 Ti yet (the card was off for this job; the app's tune runs when the card mines and the Power Helper answers)" }
|
||||||
|
} catch { "RESULT tune error=$($_.Exception.Message)" }
|
||||||
|
|
||||||
|
# ---- prove beside the miner, 16 GB only: the app's own WSL2 host aimed at the 5060 Ti while a bench loop hashes on it ----
|
||||||
|
$prove = [ordered]@{ ran = $false; verdict = 'not attempted'; peak_mib = 0; base_mib = 0; prove_s = 0; why = '' }
|
||||||
|
if ($tier -ge 16) {
|
||||||
|
$hostWin = Join-Path $inst 'wsl2\bin\igneum-prove-host'
|
||||||
|
if (-not (Test-Path -LiteralPath $hostWin)) { $prove.why = "no WSL2 host at $hostWin"; "RESULT prove blocked why=$($prove.why)" }
|
||||||
|
else {
|
||||||
|
function WslPath($p) { $w = (& wsl.exe -d Ubuntu-24.04 -- wslpath -a ($p -replace '\\', '/') 2>$null); if ($w) { ($w -replace "`0", '').Trim() } else { '/mnt/c' + ($p.Substring(2) -replace '\\', '/') } }
|
||||||
|
$hostW = WslPath $hostWin; $jobW = WslPath $job
|
||||||
|
# the fixture: one live block from PC 2's own node (read only, the exec RPC on loopback), cut with the app's exporter
|
||||||
|
$evmPort = $null
|
||||||
|
foreach ($p in 26790, 26800, 26810) { try { $r = Invoke-RestMethod -Method Post -Uri "http://127.0.0.1:$p" -ContentType 'application/json' -Body '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}' -TimeoutSec 20; if ($r.result) { $evmPort = $p; $tipHex = $r.result; break } } catch { } }
|
||||||
|
if (-not $evmPort) { $prove.why = 'no exec RPC on 26790/26800/26810'; "RESULT prove blocked why=$($prove.why)" }
|
||||||
|
else {
|
||||||
|
$tip = [Convert]::ToInt64($tipHex, 16); $blk = $tip - 30
|
||||||
|
$body = (@{jsonrpc='2.0'; id=1; method='igneum_exportSegments'; params=@('0x0', ('0x{0:x}' -f $blk))} | ConvertTo-Json -Compress)
|
||||||
|
$bodyFile = Join-Path $job 'export-request.json'; $seqFile = Join-Path $job 'seq.json'
|
||||||
|
[IO.File]::WriteAllText($bodyFile, $body, (New-Object System.Text.UTF8Encoding $false))
|
||||||
|
& curl.exe -s -S -m 600 -X POST "http://127.0.0.1:$evmPort" -H 'Content-Type: application/json' --data-binary "@$bodyFile" -o $seqFile 2>&1 | ForEach-Object { "RESULT prove curl $_" }
|
||||||
|
$seqLen = if (Test-Path $seqFile) { (Get-Item $seqFile).Length } else { 0 }
|
||||||
|
"RESULT prove export port=$evmPort tip=$tip block=$blk bytes=$seqLen"
|
||||||
|
# the bash side (LF, no BOM, run as the WSL default user = the app's prover user, never as the root user: the
|
||||||
|
# socket rule of tools/ci/prover-socket-check.sh; the server this run starts is ended by the pid on ITS socket).
|
||||||
|
# Inventory first; the prove only when the host carries the selector and the card is not WSL device 0 (device 0's
|
||||||
|
# socket /tmp/sp1-cuda-0.sock is the app's own server on the 5090 and is never touched).
|
||||||
|
$bashBody = @'
|
||||||
|
set -uo pipefail
|
||||||
|
H="$1"; JOB="$2"; UUID="$3"; BLK="$4"
|
||||||
|
X="$(dirname "$H")/igneum-prove-export"
|
||||||
|
CUDA_DIR="$(ls -d /usr/local/cuda-12.* 2>/dev/null | sort -V | tail -1 || true)"; [ -n "$CUDA_DIR" ] && export PATH="$CUDA_DIR/bin:$PATH" && export LD_LIBRARY_PATH="$CUDA_DIR/lib64:/usr/lib/wsl/lib:${LD_LIBRARY_PATH:-}"
|
||||||
|
export PATH="$HOME/.sp1/bin:$PATH"
|
||||||
|
stamp() { date -u +%Y-%m-%dT%H:%M:%SZ; }
|
||||||
|
echo "RESULT prove host=$H exists=$([ -x "$H" ] && echo yes || echo no) sha=$(sha256sum "$H" 2>/dev/null | cut -c1-16) export_exists=$([ -x "$X" ] && echo yes || echo no) user=$(id -un) home=$HOME"
|
||||||
|
SEL=$(grep -c IGNEUM_CUDA_DEVICE "$H" 2>/dev/null || true); SEL=${SEL:-0}
|
||||||
|
echo "RESULT prove selector IGNEUM_CUDA_DEVICE strings_in_host=$SEL"
|
||||||
|
for s in "$HOME/.sp1/bin/sp1-gpu-server" /opt/igneum-floor/bin/sp1-gpu-server; do if [ -x "$s" ]; then echo "RESULT prove server $s bytes=$(stat -c %s "$s") sha=$(sha256sum "$s" | cut -c1-16) version=$("$s" --version 2>&1 | head -1)"; else echo "RESULT prove server $s absent"; fi; done
|
||||||
|
echo "RESULT prove sockets_before $(ls /tmp/sp1-cuda-*.sock 2>/dev/null | tr '\n' ' ')"
|
||||||
|
nvidia-smi --query-gpu=index,uuid,name,memory.total --format=csv,noheader 2>/dev/null | sed 's/^/RESULT prove wslgpu /'
|
||||||
|
IDX="$(nvidia-smi --query-gpu=index,uuid --format=csv,noheader 2>/dev/null | awk -F', ' -v u="$UUID" '$2==u {print $1}' | head -1)"
|
||||||
|
echo "RESULT prove wsl_index_of_5060ti=${IDX:-none}"
|
||||||
|
if [ "$SEL" = 0 ]; then echo "RESULT prove blocked why=the shipped host has no IGNEUM_CUDA_DEVICE selector: aimed at nothing it proves on CUDA device 0 (the 5090) through the app's own socket; the prover-floor host carries the selector (proof_system.rs) and is the owed cut"; exit 0; fi
|
||||||
|
if [ -z "$IDX" ] || [ "$IDX" = 0 ]; then echo "RESULT prove blocked why=the 5060 Ti is WSL CUDA device '${IDX:-none}': device 0 is the app's own prover socket and is never touched by a job"; exit 0; fi
|
||||||
|
SOCK="/tmp/sp1-cuda-$IDX.sock"
|
||||||
|
rm -f "$SOCK"
|
||||||
|
python3 -c "import json; d=json.load(open('$JOB/seq.json')); json.dump(d['result'], open('$JOB/export.json','w'))" || { echo "RESULT prove blocked why=the export reply did not unwrap"; exit 0; }
|
||||||
|
FIX="$JOB/block-$BLK.json"
|
||||||
|
if ! "$X" "$JOB/export.json" "$BLK" "$FIX" --source "PC 2 live devnet export, the 5060 Ti prove-beside read" 2>&1 | tail -1 | sed 's/^/RESULT prove cut /'; then echo "RESULT prove blocked why=the cut failed"; exit 0; fi
|
||||||
|
echo "RESULT prove start $(stamp) fixture=$(basename "$FIX") device=$IDX socket=$SOCK"
|
||||||
|
t0=$(date +%s)
|
||||||
|
IGNEUM_CUDA_DEVICE="$IDX" SP1_PROVER=cuda RUST_LOG=off timeout 900 "$H" "$FIX" --mode compressed --shard 0 --prover 0xCAfc6e74000000000000000000000000000000c2 --out "$JOB/prove-5060ti.json" > "$JOB/prove-5060ti.log" 2>&1; rc=$?
|
||||||
|
echo "RESULT prove end $(stamp) rc=$rc wall_s=$(( $(date +%s) - t0 ))"
|
||||||
|
grep -E '^(RESULT|STAGE|segment proof|FLOOR|thread|Error|error)' "$JOB/prove-5060ti.log" | head -20 | sed 's/^/RESULT prove host-out /'
|
||||||
|
# the server this run started answers on ITS socket: ended by that pid, never by a name; the socket unlinked
|
||||||
|
PID="$(fuser "$SOCK" 2>/dev/null | tr -d ' ' || true)"
|
||||||
|
if [ -n "$PID" ]; then kill "$PID" 2>/dev/null; sleep 2; kill -9 "$PID" 2>/dev/null; echo "RESULT prove server_ended pid=$PID"; fi
|
||||||
|
rm -f "$SOCK"
|
||||||
|
echo "RESULT prove sockets_after $(ls /tmp/sp1-cuda-*.sock 2>/dev/null | tr '\n' ' ')"
|
||||||
|
'@
|
||||||
|
$bashFile = Join-Path $job 'prove-5060ti.sh'
|
||||||
|
[IO.File]::WriteAllText($bashFile, ("#!/bin/bash`n" + ($bashBody -replace "`r`n", "`n").TrimEnd() + "`n"), (New-Object System.Text.UTF8Encoding $false))
|
||||||
|
$bashW = WslPath $bashFile
|
||||||
|
# the miner-shaped load on the 5060 Ti for the read: a bench loop of about six minutes, by pid, file output
|
||||||
|
$loopN = if ($v4 -and $v4.mhs -gt 0) { [int][math]::Ceiling(360 * $v4.mhs * 1e6 / 16777216) } else { 300 }
|
||||||
|
$loopOut = Join-Path $job 'beside-loop.out'
|
||||||
|
$loop = Start-Process -FilePath $exe -ArgumentList @('--bench', '--pack', (Join-Path $kit 'packs\v4-devnet-epoch0'), '--batches', "$loopN", '--batch-log2', '24', '--block-warps', '1', '--device', '0') -NoNewWindow -PassThru -RedirectStandardOutput $loopOut -RedirectStandardError (Join-Path $job 'beside-loop.err')
|
||||||
|
$s2 = SamplerStart 'beside'
|
||||||
|
Start-Sleep -Seconds 45 # the loop's build, self-test and warm-up: the base reading is the miner-shaped resident set
|
||||||
|
$baseRow = @(Get-Content -LiteralPath $s2.file -ErrorAction SilentlyContinue | Where-Object { $_ -match ',' } | Select-Object -Last 1)
|
||||||
|
if ($baseRow.Count -gt 0) { $c = @($baseRow[0] -split ',\s*'); if ($c.Count -ge 8) { $prove.base_mib = [double]$c[7] } }
|
||||||
|
"RESULT prove base_mib=$($prove.base_mib) (the bench loop resident on the card before the host starts)"
|
||||||
|
$wslOut = @(& wsl.exe -d Ubuntu-24.04 -- bash $bashW $hostW $jobW $card.uuid "$blk" 2>&1 | ForEach-Object { ("$_" -replace "`0", '') })
|
||||||
|
$wslOut | ForEach-Object { $_ }
|
||||||
|
$prove.ran = ($wslOut | Where-Object { $_ -match '^RESULT prove end ' }).Count -gt 0
|
||||||
|
$endLine = $wslOut | Where-Object { $_ -match '^RESULT prove end ' } | Select-Object -First 1
|
||||||
|
if ($endLine -and $endLine -match 'rc=(\d+) wall_s=(\d+)') { $prove.prove_s = [int]$Matches[2]; $prove.verdict = if ($Matches[1] -eq '0') { 'proved' } else { "failed rc=$($Matches[1])" } }
|
||||||
|
$blocked = $wslOut | Where-Object { $_ -match '^RESULT prove blocked ' } | Select-Object -First 1
|
||||||
|
if ($blocked) { $prove.verdict = 'blocked'; $prove.why = ($blocked -replace '^RESULT prove blocked why=', '') }
|
||||||
|
if (-not $loop.HasExited) { $loop.WaitForExit(120000) | Out-Null }
|
||||||
|
if (-not $loop.HasExited) { try { & taskkill /T /F /PID $loop.Id 2>&1 | Out-Null } catch { } }
|
||||||
|
SamplerStop $s2
|
||||||
|
$besideSmi = SamplerRead $s2 'beside'
|
||||||
|
$prove.peak_mib = $besideSmi.mem_used_peak_mib
|
||||||
|
$loopRes = @(Get-Content -LiteralPath $loopOut -ErrorAction SilentlyContinue | Where-Object { $_ -match '^RESULT pack=' } | Select-Object -First 1)
|
||||||
|
$loopMhs = if ($loopRes.Count -gt 0 -and $loopRes[0] -match 'mhs=([\d.]+)') { $Matches[1] } else { 'none' }
|
||||||
|
"RESULT prove loop_mhs=$loopMhs (the bench loop's mean over its dispatches, the prover beside it when it ran)"
|
||||||
|
$own = if ($prove.ran -and $prove.verdict -eq 'proved') { [math]::Round(($prove.peak_mib - $prove.base_mib) / 1024, 1) } else { 0 }
|
||||||
|
"RESULT PROVE tier_gb=$tier verdict=$($prove.verdict) prove_s=$($prove.prove_s) peak_mib=$($prove.peak_mib) base_mib=$($prove.base_mib) own_gb=$own vram_mib=$($card.mem_total) spare_gb=$(if ($prove.peak_mib -gt 0) { [math]::Round(($card.mem_total - $prove.peak_mib) / 1024, 1) } else { 'n/a' }) why=`"$($prove.why)`""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { "RESULT PROVE tier_gb=$tier verdict=not-a-16gb-card: the prove-beside read is for the 16 GB tier; an 8 GB card mines only (the 12 GB floor is 13.9 GB for an empty shard)" }
|
||||||
|
|
||||||
|
# ---- the row, the picker entry and the consequences ----
|
||||||
|
$rate = if ($stock) { $stock.mhs } elseif ($v4) { $v4.mhs } else { 0 }
|
||||||
|
$watts = if ($stockSmi -and $stockSmi.loaded -gt 0) { $stockSmi.w_mean } else { 0 }
|
||||||
|
$mhw = if ($watts -gt 0 -and $rate -gt 0) { [math]::Round($rate / $watts, 3) } else { 0 }
|
||||||
|
$blocksDay = if ($networkMhs -gt 0 -and $rate -gt 0) { [math]::Round($rate / $networkMhs * 86400, 2) } else { 0 }
|
||||||
|
$hoursBlock = if ($blocksDay -gt 0) { [math]::Round(24 / $blocksDay, 1) } else { 0 }
|
||||||
|
$ignDay = [math]::Round($blocksDay * $subsidyIgn, 1)
|
||||||
|
$shadowCost = if ($v4 -and $v3 -and $v3.mhs -gt 0) { [math]::Round((1 - $v4.mhs / $v3.mhs) * 100, 1) } else { 'owed' }
|
||||||
|
$of5090 = if ($rate -gt 0) { [math]::Round($rate / $ref5090Mhs * 100, 1) } else { 0 }
|
||||||
|
$ref5090Mhw = [math]::Round($ref5090Mhs / $ref5090W, 3)
|
||||||
|
$mhwVs5090 = if ($mhw -gt 0) { [math]::Round($mhw / $ref5090Mhw * 100) } else { 0 }
|
||||||
|
"RESULT ROW card=`"$($card.name)`" vram_gb=$tier driver=$($card.driver) pcie=gen$($card.gen)x$($card.width) g1_v4_mhs=$(if ($v4) { $v4.mhs } else { 'failed' }) g1_v3_mhs=$(if ($v3) { $v3.mhs } else { 'failed' }) stock_window_mhs=$(if ($stock) { $stock.mhs } else { 'failed' }) stock_window_s=$(if ($stock) { $stock.wall_s } else { 0 }) watts_mean=$watts watts_p50=$(if ($stockSmi) { $stockSmi.w_p50 } else { 0 }) limit_w=$($card.plimit) default_w=$($card.pdefault) mhw=$mhw sm_mhz=$(if ($stockSmi) { $stockSmi.sm_mhz } else { 0 }) mem_mhz=$(if ($stockSmi) { $stockSmi.mem_mhz } else { 0 }) temp_max=$(if ($stockSmi) { $stockSmi.temp_max } else { 0 }) shadow_cost_pct=$shadowCost efficient_point=app-tune-owed network_mhs=$networkMhs blocks_per_day=$blocksDay hours_per_block=$hoursBlock ign_per_day=$ignDay card_alone=$cardAlone"
|
||||||
|
"RESULT PICKER ['NVIDIA RTX 5060 Ti', $rate] (site/yourcard.js CARDS entry; measured on the card alone, so it may go on the site)"
|
||||||
|
"RESULT CONSEQUENCES a 5060 Ti owner ($tier GB, Windows, in a Thunderbolt enclosure at gen$($card.gen)x$($card.width)): $rate MH/s at $watts W ($mhw MH/W), $of5090 percent of the 5090's $ref5090Mhs MH/s at $ref5090W W ($ref5090Mhw MH/W: $mhwVs5090 percent of its hash per watt); $blocksDay blocks a day at today's $networkMhs MH/s network (one every $hoursBlock h, about $ignDay IGN a day at $subsidyIgn IGN a block, approximate: the network rate moves); prove-beside verdict $($prove.verdict); the efficient point is the app's tune to find and is owed (nothing set by this job)"
|
||||||
|
$status = if ($v4 -and $v4.match -eq 'yes' -and $v3 -and $v3.match -eq 'yes' -and $stock) { 'done' } elseif ($v4 -or $stock) { 'partial' } else { 'failed' }
|
||||||
|
"RESULT end $(Stamp)"
|
||||||
|
Summary $status @{ name = $card.name; tier_gb = $tier; driver = $card.driver; pcie = "gen$($card.gen)x$($card.width)"; rate_mhs = $rate; watts = $watts; mhw = $mhw; limit_w = $card.plimit; default_w = $card.pdefault; v4_fp = $(if ($v4) { $v4.fp } else { '' }); v4_match = $(if ($v4) { $v4.match } else { 'failed' }); v3_match = $(if ($v3) { $v3.match } else { 'failed' }); prove = $prove.verdict; prove_peak_mib = $prove.peak_mib; blocks_per_day = $blocksDay; card_alone = $cardAlone }
|
||||||
|
if ($status -eq 'failed') { exit 1 }
|
||||||
|
exit 0
|
||||||
Loading…
Reference in a new issue