diff --git a/docs/bench-log.md b/docs/bench-log.md index 5d5220b34..0c328e6dc 100644 --- a/docs/bench-log.md +++ b/docs/bench-log.md @@ -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 | | 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 | + +## 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). diff --git a/site/miner-bench.json b/site/miner-bench.json index 4256e0100..1a1cfa40d 100644 --- a/site/miner-bench.json +++ b/site/miner-bench.json @@ -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", "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" + }, + { + "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" } ] } diff --git a/site/yourcard.js b/site/yourcard.js index fb2f80b7b..4ad14adb7 100644 --- a/site/yourcard.js +++ b/site/yourcard.js @@ -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 measured numbers (site/miners.html); nothing here is a promise. Reads /api/live every 15 s. */ (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 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'; } diff --git a/tools/bench-5060ti/README.md b/tools/bench-5060ti/README.md new file mode 100644 index 000000000..ec25c2cd6 --- /dev/null +++ b/tools/bench-5060ti/README.md @@ -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- \ + --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 `): `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', ]`, `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 | diff --git a/tools/bench-5060ti/make-kit.sh b/tools/bench-5060ti/make-kit.sh new file mode 100755 index 000000000..41e489fb6 --- /dev/null +++ b/tools/bench-5060ti/make-kit.sh @@ -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 ' ')" diff --git a/tools/bench-5060ti/pc2-5060ti-bench.ps1 b/tools/bench-5060ti/pc2-5060ti-bench.ps1 new file mode 100644 index 000000000..018800f54 --- /dev/null +++ b/tools/bench-5060ti/pc2-5060ti-bench.ps1 @@ -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 +# (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 `. +$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 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