Counter ASIC 3.0 PC 1 AMD: the scripts stop switching cards (jobs 1, 2, 4 and the watts job read the card's state only; the runner's --cards-off at publish), the watts job split into the read-only control row in the app and the candidate row alone, the README's record of the 17:52Z death (the 0.3.14 relaunch under the job) and the rule
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
8b9edccff8
commit
14c7087c42
6 changed files with 329 additions and 370 deletions
|
|
@ -2,15 +2,21 @@
|
|||
|
||||
Four signed `run` jobs for PC 1 (machine `ae432dc7`: RTX 5090, RTX 4070, RX 9070 XT `amd:gfx1201` on the eGPU, all on the installed app) that fill the OWED AMD rows of `docs/plans/counter-asic-3-status.md` sections 3, 5 and 7, plus the 4070 row of item 8. Prepared on branch `ca3-pc1-amd`; NOTHING here is published by this branch. The coordinator publishes on "go PC 1 AMD", one job at a time, in the order below, and reads each closing report before the next (`node tools/jobs.mjs <id>`, `node tools/jobs.mjs watch <id>`).
|
||||
|
||||
## The rule of 6 October 2026, 18:xx UTC: a script never switches the installed app's cards
|
||||
|
||||
The watts job (`run-ca3-pc1-amd-watts-20261006`, 17:49:46 to 17:52:54Z, failed exit 1) switched the 9070 XT off itself, and at 17:52:54Z the installed app went from 0.3.13 to 0.3.14 (the old miner run `win-ae432dc7-20261006-152452` ends 17:52:59Z with the gRPC connections closing, the new run `win-ae432dc7-20261006-175303` starts 17:53:03Z with `version=0.3.14`); the relaunch killed the job's process tree 71 s into the bench, powershell.exe left with code 1, the script's `finally` never ran, the card stayed off until a hand POST (the report is 10,776 bytes: not the 200 KB cap; not the runner's abort or time cap, which print their own lines and report `aborted`/`timeout`). Which path relaunched under an active job is open: `tick_update`'s `miner_busy` counts `jobs.active()`, and the runner's `tick` starts a queued `update-now` or `restart` only with no active job, so neither should have; main's to settle from the app's event log.
|
||||
|
||||
Main's rule: a POST of `enabled:false` to `/api/cards` from a script is the same class as `/api/pause` from a script. A job that needs a card alone asks the RUNNER: `packaging/ota/publish-jobs.sh add --kind run ... --cards-off <key,key>` (this branch, for the cut after 0.3.14): the runner matches the present live cards by key with or without the device index (`jobrun::cards_off_choices`), switches them off through the app's own card path (`apply_cards`) before the script starts, writes `cards-off: <keys> ... cards restored: <keys> by the runner on any exit` into the job's report, and puts the exact entries back (their own enabled flag, identities and cap) on ANY exit: done, failed, timeout, aborted, and the app quitting (`Jobs::abort` returns the restore and the engine applies it on its own thread before the exit). Tests on the box (`igneum-app` 114 passed): `cards_off_matches_keys_with_and_without_the_device_index_and_restores_exactly` and `a_card_hold_restores_once_on_any_exit` (the known-failed case: the list comes out once whichever path ends the job, never twice). `tools/ci/playbook-quit-check.sh` rule 2 (taken from master `630b537` and extended): any request to `api/cards` from a script fails CI; the pre-rule playbooks of 5 and 6 October are on a dated allow list (`PRE_RULE`) as the record of runs already made, never republished; `relay/playbooks/shard-test.ps1` fails rule 1 on master too (pre-existing, not touched here). Every script in this folder now reads the card's state only (the quiet confirmation by the process list stays); jobs 1, 1b, 2 and 4 are published with `--cards-off`.
|
||||
|
||||
## Rules every script keeps
|
||||
|
||||
| Rule | How |
|
||||
|---|---|
|
||||
| One job at a time on PC 1 | the publish order below; each job's closing report read before the next |
|
||||
| The card alone only where the fixture needs it | job 1 and job 4 switch ONLY the card under test off through `POST <app.url>api/cards` (plain `run` jobs, not `--stop-miners`: the other two cards keep mining); jobs 2 and 3 run beside the miners and print `card_state=loaded|quiet` on every row |
|
||||
| Card keys from settings.json, never `/api/state`, BOTH forms | every form is posted: the key as written (`amd:1:gfx1201`, `nvidia:1:NVIDIA GeForce RTX 4070` or whatever the file holds), `vendor:index`, `vendor:code`; `apply_cards` (engine.rs) skips a key that matches no live card, so the extra forms are harmless and nothing is written for them |
|
||||
| The card alone only where the fixture needs it | jobs 1, 1b, 2 (run e) and 4 are published with the RUNNER's `--cards-off <key>` (the card under test off through the app's own card path before the script, restored on any exit; the other two cards keep mining); job 1a and job 3 run beside the miners and print `card_state=` on every row; no script posts to `/api/cards` |
|
||||
| Card keys with or without the device index | the runner's `--cards-off` matches a live card by its key as written or with the index removed (`amd:1:gfx1201` reaches the live `amd:gfx1201`); a key that matches no present card switches nothing and the report says so |
|
||||
| Quiet confirmed by the process list | the worker process carrying the card's `--device <index>` must be gone (Win32_Process command lines; job 4 also reads `nvidia-smi -i <idx> --query-compute-apps`); an unconfirmed card prints `UNCONFIRMED` and the rows say `card_alone=False` |
|
||||
| Restored in `finally` | the card posted back enabled (only when settings.json had it enabled), the worker's return polled for 90 s and printed; the sampler ended by its own pid (never by name: the app runs its own copy of the AMD helper) |
|
||||
| Restored by the runner | the exact entries back on any exit, by the runner, not the script; the scripts' `finally` only ends their sampler by its own pid (never by name: the app runs its own copy of the AMD helper) and prints `RESULT finally_begin` as its first line |
|
||||
| The installed app untouched | no `/api/quit`, `/api/pause`, `/api/resume`, no manifest, no settings.json write; the only writes are POST `api/cards` on the card under test |
|
||||
| Copied sources re-stamped | nothing is built on the PC by these jobs (no cargo; the OpenCL kernels are compiled by the driver at run time); `tools/ci/copied-sources-check.sh` passes |
|
||||
| Prover socket | not applicable (no prover host in these jobs) |
|
||||
|
|
@ -36,14 +42,15 @@ The fetch lands at `<jobs>\fetch-ca3-pc1-amd-20261006\{bin,packs,src,SHA256SUMS}
|
|||
|
||||
| # | Script | Publish (after the fetch; `--id` fixed so the read-back is one command) | Length | Card | Rows it fills |
|
||||
|---|---|---|---|---|---|
|
||||
| 1 | `pc1-amd-g1-shadow.ps1` | `packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id run-ca3-pc1-amd-g1-shadow-20261006 --script tools/ca3-pc1-amd/pc1-amd-g1-shadow.ps1 --timeout-minutes 40 --title "CA3 PC 1: G1 + AMD ladder on the 9070 XT" --deploy` | about 15 min (7 benches of 60 to 90 dispatches of 2^24 at 0.9 to 2 s each, the card switch up to 150 s, the installed-worker cross-check 40 dispatches) | 9070 XT ALONE (its worker stopped; 5090 and 4070 keep mining) | G1 for `mx8+sh256x27` on the AMD vendor (section 5: `RESULT G1 pack=sh256x27 fingerprint=3d2e8245cc084d07 match=yes` turns "AMD vendor NOT RUN" green; the other five packs give the same for the ladder); the 9070 XT column of the item 8 tables (section 3: `RESULT LADDER pack=<p> ops=<N> mhs= watts= uj= gclk_mhz=`), where the card leaves the latency bound by the 5 percent rule; item 1's AMD per-joule row from the control (`RESULT LADDER pack=mx8-devnet-epoch0 ... uj=`: section 3 "Consequences per tier (item 1)", 16 GB AMD row); section 7 rows "item 8: the 9070 XT rows", "item 1: the 9070 XT rows" |
|
||||
| 1 | `pc1-amd-g1-shadow.ps1` | `packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id run-ca3-pc1-amd-g1-shadow-20261006-b --cards-off amd:gfx1201 --script tools/ca3-pc1-amd/pc1-amd-g1-shadow.ps1 --timeout-minutes 40 --title "CA3 PC 1: G1 + AMD ladder on the 9070 XT" --deploy` (the app that carries `--cards-off` must be installed on PC 1 first: the cut after 0.3.14) | about 15 min (7 benches of 60 to 90 dispatches of 2^24 at 0.9 to 2 s each, the card switch up to 150 s, the installed-worker cross-check 40 dispatches) | 9070 XT ALONE (its worker stopped; 5090 and 4070 keep mining) | G1 for `mx8+sh256x27` on the AMD vendor (section 5: `RESULT G1 pack=sh256x27 fingerprint=3d2e8245cc084d07 match=yes` turns "AMD vendor NOT RUN" green; the other five packs give the same for the ladder); the 9070 XT column of the item 8 tables (section 3: `RESULT LADDER pack=<p> ops=<N> mhs= watts= uj= gclk_mhz=`), where the card leaves the latency bound by the 5 percent rule; item 1's AMD per-joule row from the control (`RESULT LADDER pack=mx8-devnet-epoch0 ... uj=`: section 3 "Consequences per tier (item 1)", 16 GB AMD row); section 7 rows "item 8: the 9070 XT rows", "item 1: the 9070 XT rows" |
|
||||
| 0b (first in the queue after "Ember closed") | `pc1-amd-reset.ps1` | `packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id run-ca3-pc1-amd-reset-20261006 --script tools/ca3-pc1-amd/pc1-amd-reset.ps1 --timeout-minutes 5 --title "CA3 PC 1: 9070 XT ADLX back to factory, identities read" --deploy` | about 1 min | beside the miners, no card switch | the 9070 XT's ADLX tuning state after Ember run 6 (`gmax 0 plimit 0 factory 0`): `RESULT amd_state before gmax= plimit= factory=`, the factory reset as the Ember playbook did it (ember-tune ca990c2: `igneum-gpu-telemetry --card <ordinal> --reset`, the helper that answers `--tune`: the installed one first, else the newest amd-kit copy, which one printed), `RESULT amd_state after ...`, FAILED unless the after line reads factory 1; then READ ONLY the identities question of the 15:56Z restore: every gfx1201 settings.json entry (`RESULT settings_entry key= enabled= identities=`) and the app's live card list (`RESULT state_card key= identities= state= mhs_now= watts=`), `RESULT identities_answer live_key= identities=`; no POST of identities (the coordinator's call after the read) |
|
||||
| 1b (the watts re-run, LAST in the queue) | `pc1-amd-watts.ps1` | `packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id run-ca3-pc1-amd-watts-20261006 --script tools/ca3-pc1-amd/pc1-amd-watts.ps1 --timeout-minutes 20 --title "CA3 PC 1: watts on the 9070 XT (app control + sh256x27 alone)" --deploy` | about 5 min | the control row with the card ON in the app; the candidate row with the 9070 XT ALONE | the two `watts= uj=` cells job 1 left owed, taken the way main trusts after Ember run 6 (the app's path: engine telemetry 363 of 363 nonzero, /api/state 456 of 456; a direct helper call alone is not trusted): (1) the CONTROL row with the card ON in the app mining class v3, `/api/state` polled at 1 Hz for 90 s (`RESULT APPROW class=v3 mhs_wall= watts_mean= watts_min= watts_max= samples= uj=`; the app's watts come from its own helper at -l 5, so they move every 5 s: `distinct_watts=` says how many values), the helper sampler running beside over the same 90 s (`RESULT sampler_vs_app helper_mean= app_mean= ratio=`); (2) the CANDIDATE row (sh256x27, the card alone through the kit worker, which /api/state cannot see) with the sampler as its only source and the ratio as its calibration (`RESULT LADDER ... watts_source=adlx calibration_ratio=`); a ratio outside 0.95 to 1.05 prints the candidate's watts as owed with the reason. The sampler is proven on the 12 idle seconds first (`RESULT sampler_raw` x3, `RESULT sampler_idle lines= lines_9070_with_watts=`); without a 9070 XT watts line no row is taken (`RESULT watts error=<why>`, status failed). About 5 min: 12 s proof, 90 s app window, the switch, about 90 s of bench, the restore |
|
||||
| 1a (the control watts row, read only, any slot) | `pc1-amd-watts-app.ps1` | `packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id run-ca3-pc1-amd-watts-app-20261006 --script tools/ca3-pc1-amd/pc1-amd-watts-app.ps1 --timeout-minutes 10 --title "CA3 PC 1: watts of the 9070 XT in the app (class v3 control)" --deploy` | about 2 min | the card ON in the app, nothing switched | item 1's AMD per-joule row from the app's own path: `RESULT APPROW class=v3 mhs_wall= watts_mean= watts_min= watts_max= samples= distinct_watts= uj=` (printed right after the 90 s window, robust to a missing field) and the helper's calibration beside it: `RESULT sampler_vs_app helper_mean= app_mean= ratio=`, the ratio job 1b reads with |
|
||||
| 1b (the candidate watts row, after the cut that carries `--cards-off`) | `pc1-amd-watts.ps1` | `R=<the ratio from job 1a's sampler_vs_app line>; sed "s/__CALIBRATION_RATIO__/$R/" tools/ca3-pc1-amd/pc1-amd-watts.ps1 > "$TMPDIR/pc1-amd-watts.ps1"; packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id run-ca3-pc1-amd-watts-20261006-b --cards-off amd:gfx1201 --script "$TMPDIR/pc1-amd-watts.ps1" --timeout-minutes 15 --title "CA3 PC 1: watts of sh256x27 on the 9070 XT alone" --deploy` (without the sed the row prints `calibration_ratio=owed` and the raw helper watts) | about 3 min | the 9070 XT ALONE through the runner's `--cards-off` | the candidate's AMD cell of the item 8 table: `RESULT LADDER pack=sh256x27 ops=102100 mhs= watts= uj= ... watts_source=adlx calibration_ratio= raw_helper_mean=`; the sampler proven on 12 idle seconds first, no bench without it; a ratio outside 0.95 to 1.05 prints the watts as owed with the reason | the two `watts= uj=` cells job 1 left owed, taken the way main trusts after Ember run 6 (the app's path: engine telemetry 363 of 363 nonzero, /api/state 456 of 456; a direct helper call alone is not trusted): (1) the CONTROL row with the card ON in the app mining class v3, `/api/state` polled at 1 Hz for 90 s (`RESULT APPROW class=v3 mhs_wall= watts_mean= watts_min= watts_max= samples= uj=`; the app's watts come from its own helper at -l 5, so they move every 5 s: `distinct_watts=` says how many values), the helper sampler running beside over the same 90 s (`RESULT sampler_vs_app helper_mean= app_mean= ratio=`); (2) the CANDIDATE row (sh256x27, the card alone through the kit worker, which /api/state cannot see) with the sampler as its only source and the ratio as its calibration (`RESULT LADDER ... watts_source=adlx calibration_ratio=`); a ratio outside 0.95 to 1.05 prints the candidate's watts as owed with the reason. The sampler is proven on the 12 idle seconds first (`RESULT sampler_raw` x3, `RESULT sampler_idle lines= lines_9070_with_watts=`); without a 9070 XT watts line no row is taken (`RESULT watts error=<why>`, status failed). About 5 min: 12 s proof, 90 s app window, the switch, about 90 s of bench, the restore |
|
||||
| G2 (job 2's slot, after "Ember closed") | `pc1-amd-g2.ps1` | its own small kit first: `tools/ca3-pc1-amd/make-g2-kit.sh "$TMPDIR/igneum-ca3-pc1-amd-g2-kit.zip"` (rebuilt 6 October 16:4x UTC from the merged tree with the generator-4 packs and the merged packfile.h: sha256 `a16165483cbef9c9001897e2e965b95051c4e683940e0dbc9bded002ed0a0e7e`, 198,597 bytes, 35 files; worker exe `7dc3b1ee…`; the 16:2x build `f4c029c7…` carried the generator-3 copies and is superseded), then `packaging/ota/publish-jobs.sh add --kind fetch --target ae432dc7 --id fetch-ca3-pc1-amd-g2-20261006 --file "$TMPDIR/igneum-ca3-pc1-amd-g2-kit.zip" --dir jobs --extract --title "CA3 PC 1 G2 kit" --deploy`, then `packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id run-ca3-pc1-amd-g2-20261006 --script tools/ca3-pc1-amd/pc1-amd-g2.ps1 --timeout-minutes 10 --title "CA3 PC 1: gate G2 on the 9070 XT" --deploy` | about 2 min (two serve-mode jobs of 1,024 nonces; the Apple OpenCL dry run took 2.6 s a pack) | beside the miners (a correctness gate) | the "RX 9070 XT" cell of G2 in `docs/plans/counter-asic-3-gate/hash-gates.md` and the AMD line of G2 in status section 5: `RESULT g2 <pack> found <n> of 1024 distinct=<d> sha256 <digest> file <path> exit=<code>` (the hash lane's form) plus `RESULT G2 pack= found= distinct= digest= digest_match=yes|no`; the found lines go to `g2-<pack>.found` in the job folder, never to stdout; the Mac re-check is `tools/ca3-v4/g2-recheck.sh proto-cuda/packs-ca3-v4/<pack> --digest <hex>` (a digest match is 1,024 of 1,024), or `--file` after `publish-jobs.sh add --kind collect --target ae432dc7 --glob "app/jobs/run-ca3-pc1-amd-g2-20261006/g2-*.found"`. Packs: `packs-ca3-v4/mx8-devnet-epoch0` (control, class v3, id 73bcbfe8ccf988f1) and `packs-ca3-v4/v4-devnet-epoch0` (the candidate with the era at the devnet epoch-0 chain seeds; since the program-id fix 7c22d0d generator 4, class "v4", id c120d7963abdcd96, kernels and vectors byte-identical), NOT the first kit's `sh256x27`: that one is the genesis string-seed pack (`IGNEUM_SEED_BYTES_HEX` of 14 bytes) and the worker's job protocol takes only a 64-hex epoch seed, so it cannot be served (the script refuses such a pack with a RESULT line instead of a malformed job). The job line's class token is the pack's own `IGNEUM_PROGRAM_CLASS` (`class=v3` for the control, `class=v4` for the candidate), as `pc2-v4-gates.ps1` does since ca61dec: the worker refuses a token that disagrees with the pack (Mac check 16:41Z: `class=v3` on the v4 pack found 0, the known-bad case). Expected digests (the verifier through g2-recheck.sh on the Mac, equal to the kit worker's serve mode on Apple OpenCL at 16:21Z on the generator-3 copies and again at 16:41Z on the generator-4 packs through the merged host.c and packfile.h with `class=v4`, 1,024 of 1,024 every time; UNCHANGED by the re-export, as the byte-identical kernels require): mx8 `2a1824a2e0834287cf53b33f84cb7322014c85efa9788bece574e4e0730940bd`, v4 `435b976a4de57c5a26c8bada8b3b9e003504687c790d4b856bc56619d3aabd18`; the script carries them and prints `digest_match` itself |
|
||||
| 2 (run e, the 9070 XT alone) | `pc1-amd-family.ps1` | the family kit of run b is on the PC (probe exe unchanged); publish only `packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id run-ca3-pc1-amd-family-20261006-e --script tools/ca3-pc1-amd/pc1-amd-family.ps1 --timeout-minutes 20 --title "CA3 PC 1: family step costs on the 9070 XT (run e, the card alone)" --deploy` | about 5 min (the switch up to 150 s, 3 runs alone, the restore, then the gfx1036 and old-platform columns beside the miners) | the 9070 XT ALONE for its three runs (job 1's path: every entry's key forms, process-list confirmation, each entry restored with its own flag and identities in finally; 5090 and 4070 keep mining); the other AMD columns beside the miners | run d (17:17Z) showed the dependent-chain ratio does not survive the card's own miner (alu 226 then 195 G steps/s; ratios 5.9 to 11.8x then 0.17 to 1.3x), so the 9070 XT column is taken alone as the Mac's and the 5090's were; rows carry `card_state=alone` (or `loaded` with `UNCONFIRMED` printed when the worker did not go); mm8 stays exact=unverified (the gfx12 WMMA iu8 16x16x16 fragment layout is in no source at hand; a guessed CPU layout would turn a wrong guess into exact=no) |
|
||||
| 2 (run e, the 9070 XT alone) | `pc1-amd-family.ps1` | the family kit of run b is on the PC (probe exe unchanged); publish only `packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id run-ca3-pc1-amd-family-20261006-e --cards-off amd:gfx1201 --script tools/ca3-pc1-amd/pc1-amd-family.ps1 --timeout-minutes 20 --title "CA3 PC 1: family step costs on the 9070 XT (run e, the card alone)" --deploy` | about 5 min (the switch up to 150 s, 3 runs alone, the restore, then the gfx1036 and old-platform columns beside the miners) | the 9070 XT ALONE for its three runs (job 1's path: every entry's key forms, process-list confirmation, each entry restored with its own flag and identities in finally; 5090 and 4070 keep mining); the other AMD columns beside the miners | run d (17:17Z) showed the dependent-chain ratio does not survive the card's own miner (alu 226 then 195 G steps/s; ratios 5.9 to 11.8x then 0.17 to 1.3x), so the 9070 XT column is taken alone as the Mac's and the 5090's were; rows carry `card_state=alone` (or `loaded` with `UNCONFIRMED` printed when the worker did not go); mm8 stays exact=unverified (the gfx12 WMMA iu8 16x16x16 fragment layout is in no source at hand; a guessed CPU layout would turn a wrong guess into exact=no) |
|
||||
| 2 (run b, superseded by run e) | `pc1-amd-family.ps1` | its own kit first: `tools/ca3-pc1-amd/make-family-kit.sh "$TMPDIR/igneum-ca3-pc1-amd-family-kit.zip"` (built 6 October 18:0x UTC: sha256 `c8337e74af5f589d651603eddd3561700219bb5077f531a600a21719bb87ef58`; the probe exe `e7ad93a4d5b4d977…`), then `packaging/ota/publish-jobs.sh add --kind fetch --target ae432dc7 --id fetch-ca3-pc1-amd-family-20261006 --file "$TMPDIR/igneum-ca3-pc1-amd-family-kit.zip" --dir jobs --extract --title "CA3 PC 1 family kit" --deploy`, then `packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id run-ca3-pc1-amd-family-20261006-b --script tools/ca3-pc1-amd/pc1-amd-family.ps1 --timeout-minutes 15 --title "CA3 PC 1: family step costs on the 9070 XT (run b, by name)" --deploy` | about 3 min (3 runs on the 9070 XT by name, then 3 each on the older-platform duplicate and the gfx1036 by ordinal; builds dominate) | beside the miners (ratios; every row says `card_state=`) | the "RX 9070 XT" column of the item 6 table (section 3: `RESULT FAMILYBEST name=<f> ratio=<r> exact= path=` per family, `RESULT FAMILY ... variant=` for the form each took: `shfla_bperm` = `ds_bpermute_b32`, the one number that could move R3; `shflx_swz` / `shflx_bperm`; `perm_amd` = `v_perm_b32` through `amd_perm` or `perm_c` emulated; `bfe_amd`; `dot4_amd` = the 5 October sudot4 row re-measured; `mm8_gfx12` / `mm8_gfx11` = the WMMA builtins, exact=unverified); section 7 row "item 6: the 9070 XT step costs" |
|
||||
| 3 | `pc1-amd-derive.ps1` | `packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id run-ca3-pc1-amd-derive-20261006 --script tools/ca3-pc1-amd/pc1-amd-derive.ps1 --timeout-minutes 15 --title "CA3 PC 1: dr736 on the 9070 XT" --deploy` | about 4 min (3 packs x a 60-dispatch pass and a 3-dispatch pass) | beside the miners (build, compile and bit-exact rows; the rate as a ratio to mx8 in the same job) | the item 2 table's 9070 XT cells (section 3: `RESULT DERIVE pack=dr736-genesis build_ms= build_ms_cached= build_ms_over_mx8= dataset_ms= fingerprint= match= mhs= ratio_to_mx8=`): the OpenCL compile cost of the derivation program against mx8's (the NVRTC +1.1 s finding on AMD), the 1 GiB daily build against mx8's 72 to 77 ms (5 October), bit-exactness on the AMD vendor; section 7 row "item 2: the 9070 XT rows" |
|
||||
| 4 (optional, confirmed by main) | `pc1-4070-shadow.ps1` | `packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id run-ca3-pc1-4070-shadow-20261006 --script tools/ca3-pc1-amd/pc1-4070-shadow.ps1 --timeout-minutes 30 --title "CA3 PC 1: item 8 ladder on the RTX 4070" --deploy` | about 11 min (7 benches of 100 to 200 dispatches at an expected 0.4 to 1 s each, approximate: no 4070 row exists yet; NVRTC per pack; the card switch) | 4070 ALONE (its CUDA worker stopped; 5090 and 9070 XT keep mining) | the "small NVIDIA card binds near 100,000" row of item 8 (section 3, consequences per tier at N = 100,000; section 7 "the 4060-class rows"): `RESULT LADDER pack=<p> ops=<N> mhs= watts= uj= sm_mhz=` with nvidia-smi at 1 Hz on that index; G1 fingerprints on a second NVIDIA card |
|
||||
| 4 (optional, confirmed by main) | `pc1-4070-shadow.ps1` | `packaging/ota/publish-jobs.sh add --kind run --target ae432dc7 --id run-ca3-pc1-4070-shadow-20261006 --cards-off "nvidia:NVIDIA GeForce RTX 4070" --script tools/ca3-pc1-amd/pc1-4070-shadow.ps1 --timeout-minutes 30 --title "CA3 PC 1: item 8 ladder on the RTX 4070" --deploy` | about 11 min (7 benches of 100 to 200 dispatches at an expected 0.4 to 1 s each, approximate: no 4070 row exists yet; NVRTC per pack; the card switch) | 4070 ALONE (its CUDA worker stopped; 5090 and 9070 XT keep mining) | the "small NVIDIA card binds near 100,000" row of item 8 (section 3, consequences per tier at N = 100,000; section 7 "the 4060-class rows"): `RESULT LADDER pack=<p> ops=<N> mhs= watts= uj= sm_mhz=` with nvidia-smi at 1 Hz on that index; G1 fingerprints on a second NVIDIA card |
|
||||
|
||||
Read back: `node tools/jobs.mjs run-ca3-pc1-amd-g1-shadow-20261006` (and `--all` for the 5-minute progress uploads). The run_id on the intake is `job-<id>-ae432dc7`.
|
||||
|
||||
|
|
@ -74,8 +81,9 @@ What run a's build failures say, by their log lines (the gfx1036 ran, not the 90
|
|||
| `proto-opencl/host.c` with the build line, `--bench-pack` on all eight kit packs on Apple OpenCL | self-test PASS (96 of 96 lanes) and the 2^24 fingerprint at base 0 equal to the recorded one on every pack: mx8-devnet-epoch0 `90f794dd556f7a3b`, sh256x13 `59ac286fe2a5a9ef`, sh256x27 `3d2e8245cc084d07`, sh256x53 `4f824b15cf2b124a`, sh256x88 `0572522e39a94d8a`, sh64x52 `9dd010f79d8ca9f4`, dr736-genesis `50e3eaa779da4f1e`, dr736-devnet-epoch0 `9553f6d5c667205a`; `build` line: mx8 70 ms, shadow packs 70 to 88 ms, dr736 337 to 356 ms (+270 ms on Apple OpenCL: item 2's compile cost shows on this harness too) |
|
||||
| mingw cross-builds of both exes | clean (`-Wall -Wextra`), 116 KB and 35 KB |
|
||||
| The kit worker's serve mode on Apple OpenCL (G2 dry runs, 16:21 UTC at load 5.2 to 5.7 on the generator-3 copies; 16:41 UTC at load 27 to 57, a loaded box, on the generator-4 packs with the merged packfile.h and the `class=v4` token: a correctness run, no number taken) | `packs-ca3-v4/mx8-devnet-epoch0` and `v4-devnet-epoch0`: 1,024 of 1,024 found each (2.6 s), nonces 0 to 1,023, the sorted found file's sha256 equal to the verifier's through `tools/ca3-v4/g2-recheck.sh --file` (the coordinator's `igneum-pow` binary): the two digests above |
|
||||
| `tools/ci/bash-body-check.sh` on the seven scripts | 0 bash bodies, all parse |
|
||||
| `tools/ci/kit-path-check.sh` on the seven scripts | 6 of 6 kit paths checked before use (the reset job reads no kit) |
|
||||
| `tools/ci/bash-body-check.sh` on the eight scripts | 0 bash bodies, all parse |
|
||||
| `tools/ci/kit-path-check.sh` on the eight scripts | 6 of 6 kit paths checked before use (the reset and watts-app jobs read no kit) |
|
||||
| `tools/ci/playbook-quit-check.sh` (rule 2) on the tree | self-test fires on an `api/cards` POST and passes a read of `api/state`; every script here passes; the pre-rule playbooks on the dated allow list |
|
||||
| `tools/ci/prover-socket-check.sh`, `copied-sources-check.sh`, `no-conflict-markers.sh` | pass |
|
||||
| The Windows PowerShell 5.1 parse (`windows.yml` parse job) | the real parser runs only on the Windows runner and only over `relay/playbooks` and the other listed folders (not `tools/`); on the Mac the same rule's approximation (`tools/ci/check-workflow-shell.mjs`'s drive-reference regex, applied to these five files by hand) fired once on `"$dev: --stop-miners"` and the line was fixed to `${dev}:`; 0 findings after; brace, paren and bracket depth 0 by the bash-body-check tokenizer |
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@
|
|||
# RTX 4070 (machine ae432dc7) through the INSTALLED igneum-worker-cuda.exe (NVRTC, the pack's own kernel text, the
|
||||
# same harness as the PC 2 job tools/ca3-shadow/pc2-shadow-bench.ps1), nvidia-smi at 1 Hz on that card alone
|
||||
# (power.draw, clocks.sm, clocks.mem, utilization, temperature): the "small NVIDIA card binds near 100,000 ops" row of
|
||||
# the status file (section 3 "Item 8", section 7). Published as a plain signed `run` job (NOT --stop-miners): the
|
||||
# RTX 5090 and the RX 9070 XT keep mining; this script switches ONLY the 4070 off in the installed app through POST
|
||||
# <app.url>api/cards with its key read from settings.json (never api/state) in every form (as written, vendor:index,
|
||||
# vendor:name; the 6 October finding), confirms by the process list that the CUDA worker carrying the 4070's --device
|
||||
# index is gone and by nvidia-smi's compute-apps list on that index, benches, and restores the card in a finally block
|
||||
# whatever happens. It never quits, pauses, resumes or updates the installed app, never writes settings.json, sets no
|
||||
# the status file (section 3 "Item 8", section 7). Published as a signed `run` job with `--cards-off "nvidia:NVIDIA
|
||||
# GeForce RTX 4070"` (NOT --stop-miners): the RUNNER switches ONLY the 4070 off through the app's own card path before
|
||||
# this script starts and puts it back on ANY exit (rule of 6 October 2026, tools/ci/playbook-quit-check.sh rule 2: a
|
||||
# script never posts to /api/cards); the RTX 5090 and the RX 9070 XT keep mining. This script confirms, read-only, by
|
||||
# the process list that the CUDA worker carrying the 4070's --device index is gone and by nvidia-smi's compute-apps
|
||||
# list on that index, then benches. It never quits, pauses, resumes or updates the installed app, never writes settings.json, sets no
|
||||
# power cap and no clock (nothing elevated). The 4070's nvidia-smi index is read from nvidia-smi by name; the app
|
||||
# passes that index as the worker's --device (detect.rs), and the worker's own RESULT line names the device it ran on,
|
||||
# which the script checks for "4070" before it takes a row. Packs: mx8-devnet-epoch0 (the class v3 control, first and
|
||||
|
|
@ -55,40 +55,8 @@ foreach ($g in $gpus) { if ($g -match '^\s*(\d+),\s*([^,]*4070[^,]*),') { $idx =
|
|||
if ($null -eq $idx) { "RESULT error no RTX 4070 in nvidia-smi's list"; Summary 'failed' @{ error = 'no 4070' }; exit 2 }
|
||||
"RESULT device nvidia-smi index $idx name '$gpuName' limits $(Smi 'power.limit,power.min_limit,power.max_limit,clocks.max.sm' $idx)"
|
||||
|
||||
# ---- the card's keys from settings.json (READ ONLY), every form ----
|
||||
$entries = @()
|
||||
try {
|
||||
$sj = Get-Content -LiteralPath (Join-Path $appDir 'settings.json') -Raw | ConvertFrom-Json
|
||||
foreach ($prop in $sj.cards.PSObject.Properties) {
|
||||
if ($prop.Name -like 'nvidia:*' -and $prop.Name -match '4070') {
|
||||
$parts = $prop.Name -split ':'
|
||||
$forms = @()
|
||||
if ($parts.Count -ge 3) { $forms += ($parts[0] + ':' + $parts[1]); $forms += ($parts[0] + ':' + (($parts | Select-Object -Skip 2) -join ':')) }
|
||||
$ident = 1; if ($prop.Value.identities) { $ident = [int] $prop.Value.identities }
|
||||
$pp = 0; if ($prop.Value.power_pct) { $pp = [int] $prop.Value.power_pct }
|
||||
$entries += [pscustomobject]@{ key = $prop.Name; forms = $forms; enabled = [bool] $prop.Value.enabled; identities = $ident; power_pct = $pp }
|
||||
"RESULT card settings_key=$($prop.Name) enabled=$([bool] $prop.Value.enabled) identities=$ident power_pct=$pp derived_forms=[$($forms -join ' | ')]"
|
||||
}
|
||||
}
|
||||
} catch { "RESULT settings_read_failed $($_.Exception.Message)" }
|
||||
if ($entries.Count -eq 0) { "RESULT card none in settings.json matching nvidia:*4070*: no switch; the rows below carry whatever load the card has" }
|
||||
# off: every form of every entry; restore: the derived forms first, then each key as written with ITS OWN settings, so an
|
||||
# entry's own identities and cap win on the live key (the class found in job 1's first run: one entry's identities posted to every form)
|
||||
function PostCards([bool] $enabled) {
|
||||
if (-not $base) { "RESULT cards no app.url: nothing posted"; return }
|
||||
foreach ($pass in @('derived', 'written')) {
|
||||
foreach ($e in $entries) {
|
||||
$keys = if ($pass -eq 'derived') { $e.forms } else { @($e.key) }
|
||||
$on = if ($enabled) { $e.enabled } else { $false }
|
||||
foreach ($k in $keys) {
|
||||
$c = @{ key = $k; enabled = $on; identities = $e.identities }
|
||||
if ($e.power_pct -gt 0) { $c.power_pct = $e.power_pct }
|
||||
$body = @{ cards = @($c) } | ConvertTo-Json -Depth 5
|
||||
try { Invoke-RestMethod -Uri "$base/api/cards" -Method POST -Body $body -ContentType 'application/json' -TimeoutSec 10 | Out-Null; "RESULT cards key=$k enabled=$on identities=$($e.identities) posted (from entry $($e.key))" } catch { "RESULT cards key=$k enabled=$on error=$($_.Exception.Message)" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# ---- the card is switched off by the RUNNER (`--cards-off "nvidia:NVIDIA GeForce RTX 4070"` at publish, the key with or
|
||||
# without its device index), never by this script (rule of 6 October 2026); here only the read-only confirmation ----
|
||||
function Workers { @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe' OR Name = 'igneum-worker-cuda.exe'" -ErrorAction SilentlyContinue | ForEach-Object { "$($_.Name):$($_.ProcessId):[$($_.CommandLine -replace '\s+', ' ')]" }) }
|
||||
function CardWorkers { @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-cuda.exe'" -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -match "--device\s+$idx(\s|$)" } | ForEach-Object { $_.ProcessId }) }
|
||||
function CudaApps { @(& nvidia-smi -i $idx --query-compute-apps=pid,process_name,used_memory --format=csv,noheader 2>$null | ForEach-Object { "$_" } | Where-Object { $_ -match 'igneum|sp1|prove' }) }
|
||||
|
|
@ -99,21 +67,17 @@ $sampler = $null
|
|||
$samplerOk = $false
|
||||
$results = @()
|
||||
$cardAlone = $false
|
||||
$switched = $false
|
||||
try {
|
||||
if ($entries.Count -gt 0 -and $base) {
|
||||
$before = @(CardWorkers)
|
||||
if ($before.Count -eq 0 -and (@(CudaApps)).Count -eq 0) { "RESULT card already quiet before the switch (no cuda worker with --device $idx, no compute app on index $idx)"; $cardAlone = $true }
|
||||
else {
|
||||
PostCards $false; $switched = $true
|
||||
$t = 0
|
||||
while ($t -lt 150) { Start-Sleep -Seconds 5; $t += 5; if ((@(CardWorkers)).Count -eq 0 -and (@(CudaApps)).Count -eq 0) { break } }
|
||||
$left = @(CardWorkers); $apps = @(CudaApps)
|
||||
if ($left.Count -eq 0 -and $apps.Count -eq 0) { "RESULT card $(Stamp) quiet after $t s: no cuda worker with --device $idx and no igneum compute app on index $idx (the card to itself)"; $cardAlone = $true }
|
||||
else { "RESULT card $(Stamp) UNCONFIRMED after $t s: worker pids [$($left -join ' ')] compute apps [$($apps -join '; ')]; the rows below are LOADED-card figures" }
|
||||
Start-Sleep -Seconds 5
|
||||
}
|
||||
} else { "RESULT card not switched (no entry or no app.url): the rows below carry whatever load the card has" }
|
||||
$before = @(CardWorkers)
|
||||
if ($before.Count -eq 0 -and (@(CudaApps)).Count -eq 0) { "RESULT card already quiet: no cuda worker with --device $idx, no compute app on index $idx (the runner's --cards-off took)"; $cardAlone = $true }
|
||||
else {
|
||||
$t = 0
|
||||
while ($t -lt 150) { Start-Sleep -Seconds 5; $t += 5; if ((@(CardWorkers)).Count -eq 0 -and (@(CudaApps)).Count -eq 0) { break } }
|
||||
$left = @(CardWorkers); $apps = @(CudaApps)
|
||||
if ($left.Count -eq 0 -and $apps.Count -eq 0) { "RESULT card $(Stamp) quiet after $t s: no cuda worker with --device $idx and no igneum compute app on index $idx (the card to itself)"; $cardAlone = $true }
|
||||
else { "RESULT card $(Stamp) UNCONFIRMED after $t s: worker pids [$($left -join ' ')] compute apps [$($apps -join '; ')] (was the job published with --cards-off for the 4070?); the rows below are LOADED-card figures" }
|
||||
Start-Sleep -Seconds 5
|
||||
}
|
||||
"RESULT workers_during $(Stamp) $((Workers) -join ' ')"
|
||||
"RESULT gpu_before $(Stamp) $(Smi 'power.draw,power.limit,clocks.sm,clocks.mem,temperature.gpu,memory.used' $idx)"
|
||||
# the sampler: nvidia-smi on this index every second into a file (its own timestamps), ended in finally
|
||||
|
|
@ -171,14 +135,7 @@ try {
|
|||
}
|
||||
} finally {
|
||||
if ($sampler) { try { Stop-Process -Id $sampler.Id -Force -ErrorAction SilentlyContinue; "RESULT sampler ended pid=$($sampler.Id)" } catch { } }
|
||||
if ($switched) {
|
||||
PostCards $true
|
||||
"RESULT card restore posted: each settings.json entry with its own enabled flag, identities and cap"
|
||||
$t = 0; $back = @()
|
||||
$anyEnabled = ($entries | Where-Object { $_.enabled }).Count -gt 0
|
||||
while ($t -lt 90 -and $anyEnabled) { Start-Sleep -Seconds 5; $t += 5; $back = @(CardWorkers); if ($back.Count -gt 0) { break } }
|
||||
"RESULT card_workers_after $(Stamp) pids=[$($back -join ' ')] after $t s $(if ($back.Count -gt 0) { '(the card mines again)' } elseif ($anyEnabled) { '(NOT back yet: check the app)' } else { '(no entry was enabled before this job)' })"
|
||||
}
|
||||
# the card comes back through the RUNNER (its restore on any exit), not here
|
||||
"RESULT workers_after $(Stamp) $((Workers) -join ' ')"
|
||||
"RESULT gpu_after $(Stamp) $(Smi 'power.draw,power.limit,clocks.sm,clocks.mem,temperature.gpu,memory.used' $idx)"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@
|
|||
# columns. Run d (17:17Z, the card by name, every variant built) showed a dependent-chain ratio does NOT survive the
|
||||
# card's own miner: the alu chain read 226 then 195 G steps/s and the ratios swung from 5.9 to 11.8x to 0.17 to 1.3x
|
||||
# between runs (the loaded card's scheduler, not the ops), while the Mac and 5090 columns were taken with the card alone.
|
||||
# Run e: the 9070 XT is switched OFF in the installed app for its three runs (job 1's path: every key form of every
|
||||
# settings.json gfx1201 entry through POST api/cards, quiet confirmed by the process list, the opencl worker carrying
|
||||
# the card's --device index gone), restored in finally with each entry's own enabled flag and identities; the 5090 and
|
||||
# the 4070 keep mining (a plain signed `run` job, NOT --stop-miners). The gfx1036 and the old-platform columns run
|
||||
# Run e: the 9070 XT is switched OFF by the RUNNER (`--cards-off amd:gfx1201` at publish, the app's own card path, put
|
||||
# back on ANY exit with its own enabled flag, identities and cap: rule of 6 October 2026, a script never posts to
|
||||
# /api/cards) for its three runs; this script only confirms by the process list that the card's opencl worker is gone;
|
||||
# the 5090 and the 4070 keep mining (NOT --stop-miners). The gfx1036 and the old-platform columns run
|
||||
# AFTER the restore, beside the miners, as they are not the card under test. mm8 stays exact=unverified: the gfx12 WMMA
|
||||
# iu8 16x16x16 fragment layout is in no source at hand, and a guessed CPU layout would turn a wrong guess into exact=no. The probe is the family kit's
|
||||
# family-probe-cl.exe (proto-opencl/family-probe.c of branch ca3-pc1-amd, cross-compiled with mingw, OpenCL.dll at run
|
||||
|
|
@ -68,35 +68,8 @@ if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) {
|
|||
foreach ($l in $wl) { if ($l -match '^\s*\[(\d+)\].*gfx1201' -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; break } }
|
||||
}
|
||||
"RESULT app_device $(if ($null -ne $dev) { $dev } else { 'unknown (no installed worker list: the quiet check falls back to any opencl worker)' })"
|
||||
$entries = @()
|
||||
try {
|
||||
$sj = Get-Content -LiteralPath (Join-Path $appDir 'settings.json') -Raw | ConvertFrom-Json
|
||||
foreach ($prop in $sj.cards.PSObject.Properties) {
|
||||
if ($prop.Name -like 'amd:*' -and $prop.Name -match 'gfx1201') {
|
||||
$parts = $prop.Name -split ':'
|
||||
$forms = @()
|
||||
if ($parts.Count -ge 3) { $forms += ($parts[0] + ':' + $parts[1]); $forms += ($parts[0] + ':' + $parts[2]) }
|
||||
$ident = 1; if ($prop.Value.identities) { $ident = [int] $prop.Value.identities }
|
||||
$entries += [pscustomobject]@{ key = $prop.Name; forms = $forms; enabled = [bool] $prop.Value.enabled; identities = $ident }
|
||||
"RESULT card settings_key=$($prop.Name) enabled=$([bool] $prop.Value.enabled) identities=$ident derived_forms=[$($forms -join ' | ')]"
|
||||
}
|
||||
}
|
||||
} catch { "RESULT settings_read_failed $($_.Exception.Message)" }
|
||||
if ($entries.Count -eq 0) { "RESULT card none in settings.json matching amd:*gfx1201*: no switch possible; the 9070 XT rows will be loaded-card figures" }
|
||||
# off: every form of every entry; restore: the derived forms first, then each key as written with ITS OWN settings
|
||||
function PostCards([bool] $enabled) {
|
||||
if (-not $base) { "RESULT cards no app.url: nothing posted"; return }
|
||||
foreach ($pass in @('derived', 'written')) {
|
||||
foreach ($e in $entries) {
|
||||
$keys = if ($pass -eq 'derived') { $e.forms } else { @($e.key) }
|
||||
$on = if ($enabled) { $e.enabled } else { $false }
|
||||
foreach ($k in $keys) {
|
||||
$body = @{ cards = @(@{ key = $k; enabled = $on; identities = $e.identities }) } | ConvertTo-Json -Depth 5
|
||||
try { Invoke-RestMethod -Uri "$base/api/cards" -Method POST -Body $body -ContentType 'application/json' -TimeoutSec 10 | Out-Null; "RESULT cards key=$k enabled=$on identities=$($e.identities) posted (from entry $($e.key))" } catch { "RESULT cards key=$k enabled=$on error=$($_.Exception.Message)" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# the card is switched off by the RUNNER (`--cards-off amd:gfx1201` at publish), never by this script (rule of 6 October
|
||||
# 2026, tools/ci/playbook-quit-check.sh rule 2); below only the read-only confirmation that its worker is gone
|
||||
function CardWorkers {
|
||||
$procs = @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe'" -ErrorAction SilentlyContinue)
|
||||
if ($null -ne $dev) { $procs = @($procs | Where-Object { $_.CommandLine -match "--device\s+$dev(\s|$)" }) }
|
||||
|
|
@ -140,35 +113,24 @@ function RunProbe([string] $label, [string[]] $probeArgs, [int] $r) {
|
|||
$script:runs++
|
||||
}
|
||||
# ---- the 9070 XT alone: off in the app, confirmed quiet, three runs, restored whatever happens ----
|
||||
$switched = $false
|
||||
$cardAlone = $false
|
||||
try {
|
||||
if ($entries.Count -gt 0 -and $base) {
|
||||
$before = @(CardWorkers)
|
||||
"RESULT card_workers_before pids=[$($before -join ' ')]"
|
||||
if ($before.Count -eq 0) { "RESULT card already quiet before the switch (no opencl worker for the card)"; $cardAlone = $true }
|
||||
else {
|
||||
PostCards $false; $switched = $true
|
||||
$t = 0
|
||||
while ($t -lt 150) { Start-Sleep -Seconds 5; $t += 5; if ((@(CardWorkers)).Count -eq 0) { break } }
|
||||
$left = @(CardWorkers)
|
||||
if ($left.Count -eq 0) { "RESULT card $(Stamp) quiet after $t s: the card's igneum-worker-opencl process is gone (the card to itself)"; $cardAlone = $true }
|
||||
else { "RESULT card $(Stamp) UNCONFIRMED after $t s: opencl worker pids [$($left -join ' ')] still run; the 9070 XT rows below are LOADED-card figures (run d's class)" }
|
||||
Start-Sleep -Seconds 8
|
||||
}
|
||||
} else { "RESULT card not switched (no entry or no app.url): the 9070 XT rows below carry whatever load the card has" }
|
||||
$before = @(CardWorkers)
|
||||
"RESULT card_workers_before pids=[$($before -join ' ')]"
|
||||
if ($before.Count -eq 0) { "RESULT card already quiet: no opencl worker for the card (the runner's --cards-off took)"; $cardAlone = $true }
|
||||
else {
|
||||
$t = 0
|
||||
while ($t -lt 150) { Start-Sleep -Seconds 5; $t += 5; if ((@(CardWorkers)).Count -eq 0) { break } }
|
||||
$left = @(CardWorkers)
|
||||
if ($left.Count -eq 0) { "RESULT card $(Stamp) quiet after $t s: the card's igneum-worker-opencl process is gone (the card to itself)"; $cardAlone = $true }
|
||||
else { "RESULT card $(Stamp) UNCONFIRMED after $t s: opencl worker pids [$($left -join ' ')] still run (was the job published with --cards-off amd:gfx1201?); the 9070 XT rows below are LOADED-card figures (run d's class)" }
|
||||
Start-Sleep -Seconds 8
|
||||
}
|
||||
$cardState = if ($cardAlone) { 'alone' } else { 'loaded' }
|
||||
"RESULT workers_during $(Stamp) $((Workers) -join ' ')"
|
||||
for ($r = 1; $r -le 3; $r++) { RunProbe '9070xt' @('--device-name', 'gfx1201') $r; Start-Sleep -Seconds 2 }
|
||||
} finally {
|
||||
if ($switched) {
|
||||
PostCards $true
|
||||
"RESULT card restore posted: each settings.json entry with its own enabled flag and identities"
|
||||
$t = 0; $back = @()
|
||||
$anyEnabled = ($entries | Where-Object { $_.enabled }).Count -gt 0
|
||||
while ($t -lt 90 -and $anyEnabled) { Start-Sleep -Seconds 5; $t += 5; $back = @(CardWorkers); if ($back.Count -gt 0) { break } }
|
||||
"RESULT card_workers_after $(Stamp) pids=[$($back -join ' ')] after $t s $(if ($back.Count -gt 0) { '(the card mines again)' } elseif ($anyEnabled) { '(NOT back yet: check the app)' } else { '(no entry was enabled before this job)' })"
|
||||
}
|
||||
"RESULT runs_9070xt_done $(Stamp) (the card comes back through the RUNNER's restore on any exit, not here)"
|
||||
}
|
||||
# ---- the other AMD columns beside the miners (not the card under test) ----
|
||||
$cardState = if ((@(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe'" -ErrorAction SilentlyContinue)).Count -gt 0) { 'loaded' } else { 'quiet' }
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
# Counter ASIC 3.0, PC 1 AMD job 1 (6 October 2026): gate G1 for the class v4 candidate and item 8's AMD ladder on PC 1's
|
||||
# RX 9070 XT (machine ae432dc7, gfx1201 on the eGPU), docs/plans/counter-asic-3-status.md sections 3, 5 and 7.
|
||||
# Published as a plain signed `run` job (NOT --stop-miners): the RTX 5090 and the RTX 4070 keep mining; this script
|
||||
# switches ONLY the 9070 XT off in the installed app through POST <app.url>api/cards with the card key read from the
|
||||
# app's settings.json (never api/state) in EVERY form (the key as written, vendor:index, vendor:code; the 6 October
|
||||
# finding: the index-less form switched nothing on 5 October), confirms by the process list that the card's
|
||||
# igneum-worker-opencl process (the one with its --device index) is gone, runs the kit's igneum-worker-opencl.exe
|
||||
# (proto-opencl/host.c of branch ca3-pc1-amd: the installed worker's source plus one `build <ms> clBuildProgram` line,
|
||||
# cross-compiled with mingw as proto-cuda/nvrtc/build-windows.sh does) with --bench-pack on every pack of the kit while
|
||||
# the AMD helper igneum-gpu-telemetry.exe (ADLX board watts, the installed app's own helper, run as a second copy at
|
||||
# 1 Hz through a wrapper this script starts and ends by its own pid) samples the card, and restores the card in a
|
||||
# finally block whatever happens. It never quits, pauses, resumes or updates the installed app, never writes
|
||||
# settings.json and never touches the manifest. If --stop-miners IS used at publish time the card is already quiet
|
||||
# and the switch is a no-op: the script works either way and says which it saw.
|
||||
# Published as a signed `run` job with `--cards-off amd:gfx1201` (NOT --stop-miners): the RUNNER switches the 9070 XT off
|
||||
# through the app's own card path before this script starts and puts it back, with its own enabled flag, identities
|
||||
# and cap, on ANY exit (done, failed, timeout, aborted, the app quitting); the RTX 5090 and the RTX 4070 keep mining.
|
||||
# This script never touches /api/cards (rule of 6 October 2026: the watts job switched the card itself, an app update
|
||||
# killed it mid-run, its finally never ran and the card stayed off; tools/ci/playbook-quit-check.sh rule 2). It only
|
||||
# confirms, read-only, by the process list that the card's igneum-worker-opencl process (the one with its --device
|
||||
# index) is gone, then runs the kit's igneum-worker-opencl.exe (proto-opencl/host.c of branch ca3-pc1-amd: the
|
||||
# installed worker's source plus one `build <ms> clBuildProgram` line, cross-compiled with mingw as
|
||||
# proto-cuda/nvrtc/build-windows.sh does) with --bench-pack on every pack of the kit while the AMD helper
|
||||
# igneum-gpu-telemetry.exe (ADLX board watts, the installed app's own helper, run as a second copy at 1 Hz through an
|
||||
# environment-fed wrapper this script starts and ends by its own pid) samples the card. Never quits, pauses, resumes
|
||||
# or updates the installed app; never writes settings.json; never touches the manifest.
|
||||
# Per pack: the self-test (cache, dataset, 64 samples, 96 vector lanes, from the worker's own line), the 2^24
|
||||
# fingerprint at base nonce 0 against the Mac's and the 5090's (G1), and a 75 to 120 s rate with the card alone for
|
||||
# MH/s; the mean board watts over the bench window give microjoules per hash. The control row doubles as item 1's AMD
|
||||
|
|
@ -77,38 +77,8 @@ foreach ($l in $list) { if ($l -match '^\s*\[(\d+)\].*gfx1201' -and $l -notmatch
|
|||
if ($null -eq $dev) { "RESULT error no gfx1201 device in --list (the eGPU is off the bus: every row OWED)"; Summary 'failed' @{ error = 'no gfx1201' }; exit 2 }
|
||||
"RESULT device $dev gfx1201 (list from $listExe)"
|
||||
|
||||
# ---- the card's keys from settings.json (READ ONLY), every form ----
|
||||
$entries = @()
|
||||
try {
|
||||
$sj = Get-Content -LiteralPath (Join-Path $appDir 'settings.json') -Raw | ConvertFrom-Json
|
||||
foreach ($prop in $sj.cards.PSObject.Properties) {
|
||||
if ($prop.Name -like 'amd:*' -and $prop.Name -match 'gfx1201') {
|
||||
$parts = $prop.Name -split ':'
|
||||
$forms = @()
|
||||
if ($parts.Count -ge 3) { $forms += ($parts[0] + ':' + $parts[1]); $forms += ($parts[0] + ':' + $parts[2]) }
|
||||
$ident = 1; if ($prop.Value.identities) { $ident = [int] $prop.Value.identities }
|
||||
$entries += [pscustomobject]@{ key = $prop.Name; forms = $forms; enabled = [bool] $prop.Value.enabled; identities = $ident }
|
||||
"RESULT card settings_key=$($prop.Name) enabled=$([bool] $prop.Value.enabled) identities=$ident derived_forms=[$($forms -join ' | ')]"
|
||||
}
|
||||
}
|
||||
} catch { "RESULT settings_read_failed $($_.Exception.Message)" }
|
||||
if ($entries.Count -eq 0) { "RESULT card none in settings.json matching amd:*gfx1201*: no switch; the rows below carry whatever load the card has" }
|
||||
# off: every form of every entry; restore: the derived forms first, then each key as written with ITS OWN enabled flag and
|
||||
# identities, so an entry's own settings win on the live key (the first run of this job, 15:46Z, posted one entry's
|
||||
# identities to every form while settings.json held three gfx1201 entries: the class fixed here)
|
||||
function PostCards([bool] $enabled) {
|
||||
if (-not $base) { "RESULT cards no app.url: nothing posted"; return }
|
||||
foreach ($pass in @('derived', 'written')) {
|
||||
foreach ($e in $entries) {
|
||||
$keys = if ($pass -eq 'derived') { $e.forms } else { @($e.key) }
|
||||
$on = if ($enabled) { $e.enabled } else { $false }
|
||||
foreach ($k in $keys) {
|
||||
$body = @{ cards = @(@{ key = $k; enabled = $on; identities = $e.identities }) } | ConvertTo-Json -Depth 5
|
||||
try { Invoke-RestMethod -Uri "$base/api/cards" -Method POST -Body $body -ContentType 'application/json' -TimeoutSec 10 | Out-Null; "RESULT cards key=$k enabled=$on identities=$($e.identities) posted (from entry $($e.key))" } catch { "RESULT cards key=$k enabled=$on error=$($_.Exception.Message)" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# ---- the card is switched off by the RUNNER (`--cards-off amd:gfx1201` at publish), never by this script (rule of
|
||||
# 6 October 2026, tools/ci/playbook-quit-check.sh rule 2); here only the read-only confirmation that its worker is gone ----
|
||||
function Workers {
|
||||
@(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe' OR Name = 'igneum-worker-cuda.exe' OR Name = 'igneum-worker-metal.exe'" -ErrorAction SilentlyContinue | ForEach-Object { "$($_.Name):$($_.ProcessId):[$($_.CommandLine -replace '\s+', ' ')]" })
|
||||
}
|
||||
|
|
@ -133,23 +103,19 @@ $sampler = $null
|
|||
$windows = @()
|
||||
$results = @()
|
||||
$cardAlone = $false
|
||||
$switched = $false
|
||||
try {
|
||||
# ---- the card off, confirmed by the process list (never api/state) ----
|
||||
if ($entries.Count -gt 0 -and $base) {
|
||||
$before = @(CardWorkers)
|
||||
"RESULT card_workers_before pids=[$($before -join ' ')]"
|
||||
if ($before.Count -eq 0) { "RESULT card already quiet before the switch (no opencl worker with --device ${dev}: --stop-miners or the card is off in the app)"; $cardAlone = $true }
|
||||
else {
|
||||
PostCards $false; $switched = $true
|
||||
$t = 0
|
||||
while ($t -lt 150) { Start-Sleep -Seconds 5; $t += 5; if ((@(CardWorkers)).Count -eq 0) { break } }
|
||||
$left = @(CardWorkers)
|
||||
if ($left.Count -eq 0) { "RESULT card $(Stamp) quiet after $t s: no igneum-worker-opencl process with --device $dev (the card to itself)"; $cardAlone = $true }
|
||||
else { "RESULT card $(Stamp) UNCONFIRMED after $t s: opencl worker pids [$($left -join ' ')] still carry --device $dev; the rows below are LOADED-card figures" }
|
||||
Start-Sleep -Seconds 5
|
||||
}
|
||||
} else { "RESULT card not switched (no entry or no app.url): the rows below carry whatever load the card has" }
|
||||
# ---- the card quiet? read-only: the runner switched it off before this script started; its worker must be gone ----
|
||||
$before = @(CardWorkers)
|
||||
"RESULT card_workers_before pids=[$($before -join ' ')]"
|
||||
if ($before.Count -eq 0) { "RESULT card already quiet: no opencl worker with --device $dev (the runner's --cards-off took, or --stop-miners)"; $cardAlone = $true }
|
||||
else {
|
||||
$t = 0
|
||||
while ($t -lt 150) { Start-Sleep -Seconds 5; $t += 5; if ((@(CardWorkers)).Count -eq 0) { break } }
|
||||
$left = @(CardWorkers)
|
||||
if ($left.Count -eq 0) { "RESULT card $(Stamp) quiet after $t s: no igneum-worker-opencl process with --device $dev (the card to itself)"; $cardAlone = $true }
|
||||
else { "RESULT card $(Stamp) UNCONFIRMED after $t s: opencl worker pids [$($left -join ' ')] still carry --device $dev (was the job published with --cards-off amd:gfx1201?); the rows below are LOADED-card figures" }
|
||||
Start-Sleep -Seconds 5
|
||||
}
|
||||
"RESULT workers_during $(Stamp) $((Workers) -join ' ')"
|
||||
|
||||
# ---- the sampler: the helper at 1 Hz through a wrapper; its paths through the ENVIRONMENT (inherited by the child,
|
||||
|
|
@ -246,14 +212,7 @@ Add-Content -LiteralPath $out -Value ('wrapper end ' + (Get-Date).ToUniversalTim
|
|||
} finally {
|
||||
# ---- whatever happened: the sampler ended by its own pid (never by name: the app runs its own copy of the helper), the card back ----
|
||||
if ($sampler) { try { & taskkill /T /F /PID $sampler.Id 2>&1 | Out-Null; "RESULT sampler ended pid=$($sampler.Id)" } catch { "RESULT sampler end error=$($_.Exception.Message)" } }
|
||||
if ($switched) {
|
||||
PostCards $true
|
||||
"RESULT card restore posted: each settings.json entry with its own enabled flag and identities"
|
||||
$t = 0; $back = @()
|
||||
$anyEnabled = ($entries | Where-Object { $_.enabled }).Count -gt 0
|
||||
while ($t -lt 90 -and $anyEnabled) { Start-Sleep -Seconds 5; $t += 5; $back = @(CardWorkers); if ($back.Count -gt 0) { break } }
|
||||
"RESULT card_workers_after $(Stamp) pids=[$($back -join ' ')] after $t s $(if ($back.Count -gt 0) { '(the card mines again)' } elseif ($anyEnabled) { '(NOT back yet: check the app)' } else { '(no entry was enabled before this job)' })"
|
||||
}
|
||||
# the card comes back through the RUNNER (its restore on any exit), not here
|
||||
"RESULT workers_after $(Stamp) $((Workers) -join ' ')"
|
||||
}
|
||||
|
||||
|
|
|
|||
158
tools/ca3-pc1-amd/pc1-amd-watts-app.ps1
Normal file
158
tools/ca3-pc1-amd/pc1-amd-watts-app.ps1
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
# Counter ASIC 3.0, PC 1 AMD job 1a (6 October 2026): item 1's AMD per-joule row, the CONTROL (class v3) with the card
|
||||
# ON in the installed app, mining the live program as it does now, taken the way main trusts after Ember run 6 (the
|
||||
# app's path: engine telemetry 363 of 363 nonzero samples, /api/state 456 of 456). READ ONLY: no card is switched, no
|
||||
# POST is made, nothing of the installed app is touched (rule of 6 October 2026: a script never posts to /api/cards;
|
||||
# tools/ci/playbook-quit-check.sh rule 2). The candidate row (sh256x27, the card alone) is the separate job
|
||||
# pc1-amd-watts.ps1 under the runner's `--cards-off`, because the runner switches the card off BEFORE a script starts,
|
||||
# so a job cannot hold the card ON for this row and OFF for that one.
|
||||
# What it does: the helper sampler (a second copy of the installed app's igneum-gpu-telemetry.exe at -l 1 through an
|
||||
# environment-fed wrapper, ended by its own pid in finally) proves itself on 12 idle seconds (first three raw lines, a
|
||||
# 9070 XT line with watts) or the job fails loudly; then /api/state is polled at 1 Hz for 90 s, the gfx1201 card's
|
||||
# power_w (the app's own helper at -l 5: the value moves every 5 s) and hash_now (the app's last interval, hash_avg
|
||||
# beside it), the key read from settings.json: `RESULT APPROW class=v3 mhs_wall= watts_mean= watts_min= watts_max=
|
||||
# samples= uj=` (printed right after the window, robust to a missing field: what was read is printed); the sampler
|
||||
# beside it over the same 90 s: `RESULT sampler_vs_app helper_mean= app_mean= ratio=`, the calibration the candidate
|
||||
# job's row is read with. A `SUMMARY {json}` line ends the job. Read back with `node tools/jobs.mjs <job id>`.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$jobName = 'pc1-amd-watts-app'
|
||||
$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)
|
||||
}
|
||||
$appWindowS = 90
|
||||
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION"
|
||||
$job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-ca3-pc1-amd' }; 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-app.exe') } | Select-Object -First 1
|
||||
$appDir = if ($env:IGNEUM_APP_DIR) { $env:IGNEUM_APP_DIR } else { Join-Path $env:LOCALAPPDATA 'igneum\app' }
|
||||
$urlFile = Join-Path $appDir 'app.url'
|
||||
$base = $null
|
||||
if (Test-Path $urlFile) { $base = (Get-Content -LiteralPath $urlFile -Raw).Trim().TrimEnd('/') }
|
||||
"RESULT app install=$inst app_dir=$appDir url_present=$([bool]$base)"
|
||||
if (-not $base) { "RESULT error no app.url: the control row needs the app's /api/state"; Summary 'failed' @{ error = 'no app.url' }; exit 2 }
|
||||
# the card's settings.json entries, READ ONLY, for the record
|
||||
try {
|
||||
$sj = Get-Content -LiteralPath (Join-Path $appDir 'settings.json') -Raw | ConvertFrom-Json
|
||||
foreach ($prop in $sj.cards.PSObject.Properties) { if ($prop.Name -like 'amd:*' -and $prop.Name -match 'gfx1201') { "RESULT card settings_key=$($prop.Name) enabled=$([bool] $prop.Value.enabled) identities=$($prop.Value.identities)" } }
|
||||
} catch { "RESULT settings_read_failed $($_.Exception.Message)" }
|
||||
function Workers { @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe' OR Name = 'igneum-worker-cuda.exe'" -ErrorAction SilentlyContinue | ForEach-Object { "$($_.Name):$($_.ProcessId):[$($_.CommandLine -replace '\s+', ' ')]" }) }
|
||||
function StateCard {
|
||||
try {
|
||||
$st = Invoke-RestMethod -Uri "$base/api/state" -Method GET -TimeoutSec 5
|
||||
$cards = $st.mining.cards; if (-not $cards) { $cards = $st.cards }
|
||||
return @($cards | Where-Object { $_.vendor -eq 'amd' -and ("$($_.key)" -match 'gfx1201') -and $_.state -ne 'off' } | Select-Object -First 1)
|
||||
} catch { return @() }
|
||||
}
|
||||
"RESULT workers_before $(Stamp) $((Workers) -join ' ')"
|
||||
|
||||
# ---- the AMD helper: one in-process sample first ----
|
||||
$tele = $null
|
||||
if ($inst -and (Test-Path (Join-Path $inst 'igneum-gpu-telemetry.exe'))) { $tele = Join-Path $inst 'igneum-gpu-telemetry.exe' }
|
||||
if (-not $tele) { $found = Get-ChildItem -Path (Join-Path $appDir 'jobs') -Recurse -Filter 'igneum-gpu-telemetry.exe' -ErrorAction SilentlyContinue | Where-Object { $_.FullName -match 'amd-kit' } | Sort-Object LastWriteTime -Descending | Select-Object -First 1; if ($found) { $tele = $found.FullName } }
|
||||
if (-not $tele) { "RESULT watts error=no igneum-gpu-telemetry.exe in the install folder or under an amd-kit job folder"; Summary 'failed' @{ error = 'no helper' }; exit 2 }
|
||||
$one = @(& $tele 2>&1 | ForEach-Object { "$_" })
|
||||
$one | ForEach-Object { "RESULT tele_once $_" }
|
||||
$line9070 = $one | Where-Object { $_ -match '^amd \d+ .*name "[^"]*9070[^"]*" watts (\S+) ' } | Select-Object -First 1
|
||||
if (-not $line9070 -or $line9070 -notmatch ' watts ([\d.]+) ') { "RESULT watts error=the helper's one-shot sample has no 9070 XT line with a watts number"; Summary 'failed' @{ error = 'no 9070 watts in the one-shot sample' }; exit 2 }
|
||||
"RESULT tele helper=$tele sha256=$((Get-FileHash -Algorithm SHA256 $tele).Hash.ToLower()) one_shot_watts=$($Matches[1]) (ADLX GPUPower, GPUTotalBoardPower as the fallback)"
|
||||
|
||||
$samples = Join-Path $job 'amd-samples.log'
|
||||
$sampler = $null
|
||||
$samplerOk = $false
|
||||
$appRows = @()
|
||||
$appT0 = $null; $appT1 = $null
|
||||
try {
|
||||
$wrapper = Join-Path $job 'sampler.ps1'
|
||||
$wrapperText = @'
|
||||
$exe = $env:IGNEUM_SAMPLER_EXE
|
||||
$out = $env:IGNEUM_SAMPLER_OUT
|
||||
Add-Content -LiteralPath $out -Value ('wrapper start ' + (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fff') + ' exe=' + $exe + ' exists=' + (Test-Path -LiteralPath $exe))
|
||||
& $exe -l 1 2>&1 | ForEach-Object { Add-Content -LiteralPath $out -Value ((Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fff') + ' ' + $_) }
|
||||
Add-Content -LiteralPath $out -Value ('wrapper end ' + (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fff') + ' exit=' + $LASTEXITCODE)
|
||||
'@
|
||||
[IO.File]::WriteAllText($wrapper, $wrapperText, (New-Object System.Text.UTF8Encoding $false))
|
||||
Remove-Item -LiteralPath $samples -Force -ErrorAction SilentlyContinue
|
||||
$env:IGNEUM_SAMPLER_EXE = $tele
|
||||
$env:IGNEUM_SAMPLER_OUT = $samples
|
||||
$sampler = Start-Process -FilePath 'powershell.exe' -ArgumentList @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', ('"' + $wrapper + '"')) -WindowStyle Hidden -PassThru
|
||||
"RESULT sampler pid=$($sampler.Id) wrapper=$wrapper file=$samples exe_via_env=$tele"
|
||||
Start-Sleep -Seconds 12
|
||||
$raw = @(); if (Test-Path -LiteralPath $samples) { $raw = @(Get-Content -LiteralPath $samples) }
|
||||
$raw | Select-Object -First 3 | ForEach-Object { "RESULT sampler_raw $_" }
|
||||
$n9070 = @($raw | Where-Object { $_ -match '^\S+ amd \d+ .*name "[^"]*9070[^"]*" watts [\d.]+ ' }).Count
|
||||
"RESULT sampler_idle lines=$($raw.Count) lines_9070_with_watts=$n9070 sampler_alive=$(-not $sampler.HasExited)"
|
||||
if ($n9070 -eq 0) {
|
||||
$why = if ($raw.Count -eq 0) { 'the sampler wrote nothing in 12 s (file absent or empty)' } else { 'the sampler wrote ' + $raw.Count + ' line(s) but none is a 9070 XT line with a watts number (first lines above)' }
|
||||
"RESULT watts error=$why; no row is taken"
|
||||
"RESULT APPROW class=v3 error=$why"
|
||||
} else {
|
||||
$samplerOk = $true
|
||||
"RESULT idle $(Stamp) sampler proven on the idle window"
|
||||
$sc = @(StateCard)
|
||||
if ($sc.Count -eq 0) { "RESULT APPROW class=v3 error=the app's /api/state lists no mining gfx1201 card (the card is off or the app is not running): this row needs the card ON in the app" }
|
||||
else {
|
||||
"RESULT app_card key=$($sc[0].key) identities=$($sc[0].identities) state=$($sc[0].state) pid=$($sc[0].pid) mhs_now=$($sc[0].hash_now) watts=$($sc[0].power_w) (before the window)"
|
||||
$appT0 = (Get-Date).ToUniversalTime()
|
||||
"RESULT approw start $(Stamp) window_s=$appWindowS"
|
||||
for ($i = 0; $i -lt $appWindowS; $i++) {
|
||||
$c = @(StateCard)
|
||||
if ($c.Count -gt 0) {
|
||||
$w = 0.0; $mh = 0.0; $av = 0.0
|
||||
try { $w = [double] $c[0].power_w } catch { }
|
||||
try { $mh = [double] $c[0].hash_now } catch { }
|
||||
try { $av = [double] $c[0].hash_avg } catch { }
|
||||
$appRows += [pscustomobject]@{ ts = (Get-Date).ToUniversalTime(); w = $w; mhs = $mh; avg = $av; state = "$($c[0].state)" }
|
||||
}
|
||||
Start-Sleep -Seconds 1
|
||||
}
|
||||
$appT1 = (Get-Date).ToUniversalTime()
|
||||
"RESULT approw end $(Stamp) polls=$($appRows.Count)"
|
||||
# the row, printed HERE (the first run of the watts job computed it after the try block and the job died before)
|
||||
$valid = @($appRows | Where-Object { $_.w -gt 0 })
|
||||
if ($valid.Count -eq 0) { "RESULT APPROW class=v3 error=no poll carried a watts value above 0 (polls=$($appRows.Count); states=$(($appRows | Select-Object -ExpandProperty state -Unique) -join ','))" }
|
||||
else {
|
||||
$m = $valid | Measure-Object -Property w -Average -Minimum -Maximum
|
||||
$mh = ($valid | Measure-Object -Property mhs -Average).Average
|
||||
$ma = ($valid | Measure-Object -Property avg -Average).Average
|
||||
$distinct = ($valid | Select-Object -ExpandProperty w -Unique | Measure-Object).Count
|
||||
$uj = if ($mh -gt 0) { [math]::Round($m.Average / $mh, 3) } else { 'owed' }
|
||||
"RESULT APPROW class=v3 mhs_wall=$([math]::Round($mh, 3)) mhs_avg_app=$([math]::Round($ma, 3)) watts_mean=$([math]::Round($m.Average, 1)) watts_min=$($m.Minimum) watts_max=$($m.Maximum) samples=$($valid.Count) polls=$($appRows.Count) distinct_watts=$distinct uj=$uj window_s=$appWindowS source=api/state (the app's own helper at -l 5: the watts value moves every 5 s) states=$(($appRows | Select-Object -ExpandProperty state -Unique) -join ',')"
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
"RESULT finally_begin $(Stamp)"
|
||||
if ($sampler) { try { & taskkill /T /F /PID $sampler.Id 2>&1 | Out-Null; "RESULT sampler ended pid=$($sampler.Id)" } catch { "RESULT sampler end error=$($_.Exception.Message)" } }
|
||||
"RESULT workers_after $(Stamp) $((Workers) -join ' ')"
|
||||
}
|
||||
# ---- the sampler against the app over the same window ----
|
||||
$rows = @()
|
||||
if (Test-Path -LiteralPath $samples) {
|
||||
foreach ($l in (Get-Content -LiteralPath $samples)) {
|
||||
if ($l -match '^(\S+) amd (\d+) bus (\S+) kind (\S+) name "([^"]*)" watts (\S+) ') {
|
||||
if ($Matches[5] -notmatch '9070' -or $Matches[6] -eq '-') { continue }
|
||||
try { $ts = [datetime]::ParseExact($Matches[1], 'yyyy-MM-ddTHH:mm:ss.fff', [Globalization.CultureInfo]::InvariantCulture) } catch { continue }
|
||||
$rows += [pscustomobject]@{ ts = $ts; w = [double] $Matches[6] }
|
||||
}
|
||||
}
|
||||
}
|
||||
"RESULT samples total $($rows.Count) file $samples (9070 XT lines with watts)"
|
||||
$ratio = $null
|
||||
$valid = @($appRows | Where-Object { $_.w -gt 0 })
|
||||
if ($valid.Count -gt 0 -and $appT0 -and $appT1) {
|
||||
$appMean = ($valid | Measure-Object -Property w -Average).Average
|
||||
$hin = @($rows | Where-Object { $_.ts -gt $appT0 -and $_.ts -lt $appT1 })
|
||||
if ($hin.Count -gt 0 -and $appMean -gt 0) {
|
||||
$helperMean = ($hin | Measure-Object -Property w -Average).Average
|
||||
$ratio = [math]::Round($helperMean / $appMean, 4)
|
||||
"RESULT sampler_vs_app helper_mean=$([math]::Round($helperMean, 1)) app_mean=$([math]::Round($appMean, 1)) ratio=$ratio helper_samples=$($hin.Count) app_samples=$($valid.Count) window_s=$appWindowS calibration=$(if ($ratio -ge 0.95 -and $ratio -le 1.05) { 'inside 0.95..1.05' } else { 'OUTSIDE 0.95..1.05' }) (the candidate job pc1-amd-watts reads its helper watts with this ratio beside them)"
|
||||
} else { "RESULT sampler_vs_app error=no helper samples inside the app window (helper $($hin.Count), app mean $appMean)" }
|
||||
}
|
||||
foreach ($r in $rows) { "RESULT sample $($r.ts.ToString('HH:mm:ss')) $($r.w)" }
|
||||
$status = if (-not $samplerOk) { 'failed' } elseif ($valid.Count -gt 0 -and $null -ne $ratio) { 'done' } elseif ($valid.Count -gt 0) { 'partial' } else { 'failed' }
|
||||
"RESULT end $(Stamp)"
|
||||
Summary $status @{ approw = ($valid.Count -gt 0); calibration_ratio = "$ratio"; sampler_proven = $samplerOk }
|
||||
if ($status -eq 'failed') { exit 1 }
|
||||
exit 0
|
||||
|
|
@ -1,24 +1,25 @@
|
|||
# Counter ASIC 3.0, PC 1 AMD job 1b (6 October 2026, revised after Ember run 6): the two watts cells job 1 left owed,
|
||||
# taken the way main trusts. Main's facts (Ember run 6, PC 1 through the installed app): the 9070 XT's watts are fine
|
||||
# through the APP's path (the engine's telemetry 363 of 363 nonzero samples, /api/state 456 of 456); a direct helper
|
||||
# call outside the app is not trusted by itself. So:
|
||||
# 1. the CONTROL row (item 1's AMD per-joule row, class v3) is taken with the card ON in the app, mining the live class
|
||||
# v3 program as it does now: /api/state polled at 1 Hz for 90 s, the gfx1201 card's watts (power_w, from the app's
|
||||
# own helper at -l 5, so the value moves every 5 s) and its MH/s wall (hash_now, the app's last interval; hash_avg
|
||||
# printed beside it), the key read from settings.json: `RESULT APPROW class=v3 mhs_wall= watts_mean= watts_min=
|
||||
# watts_max= samples= uj=`. The helper sampler (a second copy of igneum-gpu-telemetry.exe at -l 1 through the
|
||||
# environment-fed wrapper) runs BESIDE it over the same 90 s: `RESULT sampler_vs_app helper_mean= app_mean= ratio=`.
|
||||
# 2. the CANDIDATE row (sh256x27, the card ALONE through the kit worker, which /api/state cannot see because the card is
|
||||
# off in the app): the helper sampler is its only source, printed with the ratio as the calibration (`RESULT LADDER
|
||||
# ... watts_source=adlx calibration_ratio=`); a ratio outside 0.95 to 1.05 prints the candidate's watts as owed with
|
||||
# the reason. The sampler proves itself in the first 12 s (first three raw lines, a 9070 XT line with watts) before
|
||||
# anything else, else `RESULT watts error=<why>` and no rows.
|
||||
# The card switch: every key form of every settings.json gfx1201 entry posted through POST api/cards, quiet confirmed by
|
||||
# the process list (the opencl worker carrying the card's --device index gone), each entry restored with ITS OWN enabled
|
||||
# flag and identities in finally (derived forms first, the key as written last). Plain signed `run` job (NOT
|
||||
# --stop-miners): the 5090 and the 4070 keep mining. Never quits, pauses, resumes or updates the app; never writes
|
||||
# settings.json. Lines: `RESULT APPROW ...`, `RESULT sampler_vs_app ...`, `RESULT LADDER ...`, `RESULT G1 ...` for the
|
||||
# candidate, a `SUMMARY {json}` line. Read back with `node tools/jobs.mjs <job id>`.
|
||||
# Counter ASIC 3.0, PC 1 AMD job 1b (6 October 2026, run b): the CANDIDATE row's watts (sh256x27, class v4, the 9070 XT
|
||||
# ALONE through the kit worker) for the item 8 table, the cell job 1 left owed. The control row (class v3, the card ON
|
||||
# in the app) is the separate read-only job pc1-amd-watts-app.ps1, because the runner switches the card off BEFORE a
|
||||
# script starts, so one job cannot hold the card ON for that row and OFF for this one.
|
||||
# Published as a signed `run` job with `--cards-off amd:gfx1201` (NOT --stop-miners): the RUNNER switches the 9070 XT
|
||||
# off through the app's own card path before this script and puts it back, with its own enabled flag, identities and
|
||||
# cap, on ANY exit (done, failed, timeout, aborted, the app quitting). This script never touches /api/cards (rule of
|
||||
# 6 October 2026: the first run of this job, run-ca3-pc1-amd-watts-20261006, switched the card itself, the 0.3.14 app
|
||||
# update relaunched the app at 17:52:54Z and killed the script mid-bench, its finally never ran and the card stayed off
|
||||
# until a hand POST; tools/ci/playbook-quit-check.sh rule 2). It confirms, read-only, by the process list that the
|
||||
# card's igneum-worker-opencl process (the one with its --device index) is gone; the sampler (a second copy of the
|
||||
# installed app's igneum-gpu-telemetry.exe at -l 1 through an environment-fed wrapper, ended by its own pid in
|
||||
# finally) proves itself on 12 idle seconds (first three raw lines, a 9070 XT line with watts) or the job fails loudly
|
||||
# with no bench; then 100 dispatches of 2^24 on sh256x27 (about 87 s at the 19.3 MH/s job 1 measured), the mean board
|
||||
# watts over the bench window after its first 12 s and before its last 2 s, microjoules per hash = watts / MH/s. The
|
||||
# helper's watts are read with the calibration the control job measured beside the app (`RESULT sampler_vs_app ratio=`
|
||||
# of pc1-amd-watts-app): the publisher substitutes it for __CALIBRATION_RATIO__ below (sed at publish time; the README
|
||||
# has the line); unsubstituted, the row prints `calibration_ratio=owed` and the watts stand as the helper's raw reading.
|
||||
# A ratio outside 0.95 to 1.05 prints the watts as owed with the reason. The 5090 and the 4070 keep mining. Never quits,
|
||||
# pauses, resumes or updates the app; never writes settings.json. Lines: `RESULT G1 ...`, `RESULT LADDER pack=sh256x27
|
||||
# ops=102100 mhs= watts= uj= ... watts_source=adlx calibration_ratio=`, `RESULT finally_begin` as the finally's first
|
||||
# line, a `SUMMARY {json}` line. Read back with `node tools/jobs.mjs <job id>`.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$jobName = 'pc1-amd-watts'
|
||||
$kitId = 'fetch-ca3-pc1-amd-20261006'
|
||||
|
|
@ -30,9 +31,11 @@ function Summary([string] $status, [hashtable] $extra) {
|
|||
'SUMMARY ' + ($o | ConvertTo-Json -Compress -Depth 4)
|
||||
}
|
||||
$expectedFp = '3d2e8245cc084d07' # sh256x27 at base nonce 0, 2^24 (the Mac, the 5090, PC 1 job 1)
|
||||
$appWindowS = 90
|
||||
$candBatches = 100 # 2^24 dispatches at the 19.3 MH/s job 1 measured: about 87 s
|
||||
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION"
|
||||
$candBatches = 100
|
||||
$calibrationText = '__CALIBRATION_RATIO__'
|
||||
$ratio = $null
|
||||
if ($calibrationText -match '^\d+(\.\d+)?$') { $ratio = [double] $calibrationText }
|
||||
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION calibration_ratio=$(if ($null -ne $ratio) { $ratio } else { 'owed (not substituted at publish)' })"
|
||||
$jobs = Split-Path $env:IGNEUM_JOB_DIR
|
||||
$kit = Join-Path $jobs $kitId
|
||||
if (-not (Test-Path $kit)) { "RESULT error kit missing at $kit (the fetch job $kitId runs first; republish it after any app update)"; Summary 'failed' @{ error = 'kit missing' }; exit 2 }
|
||||
|
|
@ -44,57 +47,22 @@ if (-not (Test-Path $packDir)) { "RESULT error pack missing at $packDir"; Summar
|
|||
$job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-ca3-pc1-amd' }; 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-app.exe') } | Select-Object -First 1
|
||||
$appDir = if ($env:IGNEUM_APP_DIR) { $env:IGNEUM_APP_DIR } else { Join-Path $env:LOCALAPPDATA 'igneum\app' }
|
||||
$urlFile = Join-Path $appDir 'app.url'
|
||||
$base = $null
|
||||
if (Test-Path $urlFile) { $base = (Get-Content -LiteralPath $urlFile -Raw).Trim().TrimEnd('/') }
|
||||
"RESULT app install=$inst app_dir=$appDir url_present=$([bool]$base)"
|
||||
if (-not $base) { "RESULT error no app.url: the control row needs the app's /api/state"; Summary 'failed' @{ error = 'no app.url' }; exit 2 }
|
||||
"RESULT app install=$inst app_dir=$appDir"
|
||||
$listExe = $exe
|
||||
if ($inst -and (Test-Path (Join-Path $inst 'igneum-worker-opencl.exe'))) { $listExe = Join-Path $inst 'igneum-worker-opencl.exe' }
|
||||
$list = @(& $listExe --list 2>&1 | ForEach-Object { "$_" })
|
||||
$list | ForEach-Object { "RESULT list $_" }
|
||||
$list | Where-Object { $_ -match 'gfx1201|gfx1036' } | ForEach-Object { "RESULT list $_" }
|
||||
$dev = $null
|
||||
foreach ($l in $list) { if ($l -match '^\s*\[(\d+)\].*gfx1201' -and $l -notmatch 'dup') { $dev = [int]$Matches[1]; break } }
|
||||
if ($null -eq $dev) { "RESULT error no gfx1201 device in --list (the eGPU is off the bus: every row OWED)"; Summary 'failed' @{ error = 'no gfx1201' }; exit 2 }
|
||||
if ($null -eq $dev) { "RESULT error no gfx1201 device in --list (the eGPU is off the bus: the row stays OWED)"; Summary 'failed' @{ error = 'no gfx1201' }; exit 2 }
|
||||
"RESULT device $dev gfx1201 (list from $listExe)"
|
||||
|
||||
# ---- every settings.json entry for the card (READ ONLY), each with its own settings and its key forms ----
|
||||
$entries = @()
|
||||
# READ ONLY: the card's settings.json entries, for the record (the runner holds and restores them, not this script)
|
||||
try {
|
||||
$sj = Get-Content -LiteralPath (Join-Path $appDir 'settings.json') -Raw | ConvertFrom-Json
|
||||
foreach ($prop in $sj.cards.PSObject.Properties) {
|
||||
if ($prop.Name -like 'amd:*' -and $prop.Name -match 'gfx1201') {
|
||||
$parts = $prop.Name -split ':'
|
||||
$forms = @()
|
||||
if ($parts.Count -ge 3) { $forms += ($parts[0] + ':' + $parts[1]); $forms += ($parts[0] + ':' + $parts[2]) }
|
||||
$ident = 1; if ($prop.Value.identities) { $ident = [int] $prop.Value.identities }
|
||||
$entries += [pscustomobject]@{ key = $prop.Name; forms = $forms; enabled = [bool] $prop.Value.enabled; identities = $ident }
|
||||
"RESULT card settings_key=$($prop.Name) enabled=$([bool] $prop.Value.enabled) identities=$ident derived_forms=[$($forms -join ' | ')]"
|
||||
}
|
||||
}
|
||||
foreach ($prop in $sj.cards.PSObject.Properties) { if ($prop.Name -like 'amd:*' -and $prop.Name -match 'gfx1201') { "RESULT card settings_key=$($prop.Name) enabled=$([bool] $prop.Value.enabled) identities=$($prop.Value.identities)" } }
|
||||
} catch { "RESULT settings_read_failed $($_.Exception.Message)" }
|
||||
if ($entries.Count -eq 0) { "RESULT card none in settings.json matching amd:*gfx1201*: no switch possible" }
|
||||
function PostCards([bool] $enabled) {
|
||||
foreach ($pass in @('derived', 'written')) {
|
||||
foreach ($e in $entries) {
|
||||
$keys = if ($pass -eq 'derived') { $e.forms } else { @($e.key) }
|
||||
$on = if ($enabled) { $e.enabled } else { $false }
|
||||
foreach ($k in $keys) {
|
||||
$body = @{ cards = @(@{ key = $k; enabled = $on; identities = $e.identities }) } | ConvertTo-Json -Depth 5
|
||||
try { Invoke-RestMethod -Uri "$base/api/cards" -Method POST -Body $body -ContentType 'application/json' -TimeoutSec 10 | Out-Null; "RESULT cards key=$k enabled=$on identities=$($e.identities) posted (from entry $($e.key))" } catch { "RESULT cards key=$k enabled=$on error=$($_.Exception.Message)" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function Workers { @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe' OR Name = 'igneum-worker-cuda.exe'" -ErrorAction SilentlyContinue | ForEach-Object { "$($_.Name):$($_.ProcessId):[$($_.CommandLine -replace '\s+', ' ')]" }) }
|
||||
function CardWorkers { @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe'" -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -match "--device\s+$dev(\s|$)" } | ForEach-Object { $_.ProcessId }) }
|
||||
function StateCard {
|
||||
try {
|
||||
$st = Invoke-RestMethod -Uri "$base/api/state" -Method GET -TimeoutSec 5
|
||||
$cards = $st.mining.cards; if (-not $cards) { $cards = $st.cards }
|
||||
return @($cards | Where-Object { $_.vendor -eq 'amd' -and ("$($_.key)" -match 'gfx1201') -and $_.state -ne 'off' } | Select-Object -First 1)
|
||||
} catch { return @() }
|
||||
}
|
||||
"RESULT workers_before $(Stamp) $((Workers) -join ' ')"
|
||||
|
||||
# ---- the AMD helper: one in-process sample first ----
|
||||
|
|
@ -103,21 +71,31 @@ if ($inst -and (Test-Path (Join-Path $inst 'igneum-gpu-telemetry.exe'))) { $tele
|
|||
if (-not $tele) { $found = Get-ChildItem -Path (Join-Path $appDir 'jobs') -Recurse -Filter 'igneum-gpu-telemetry.exe' -ErrorAction SilentlyContinue | Where-Object { $_.FullName -match 'amd-kit' } | Sort-Object LastWriteTime -Descending | Select-Object -First 1; if ($found) { $tele = $found.FullName } }
|
||||
if (-not $tele) { "RESULT watts error=no igneum-gpu-telemetry.exe in the install folder or under an amd-kit job folder"; Summary 'failed' @{ error = 'no helper' }; exit 2 }
|
||||
$one = @(& $tele 2>&1 | ForEach-Object { "$_" })
|
||||
$one | ForEach-Object { "RESULT tele_once $_" }
|
||||
$one | Where-Object { $_ -match '9070|^end' } | ForEach-Object { "RESULT tele_once $_" }
|
||||
$line9070 = $one | Where-Object { $_ -match '^amd \d+ .*name "[^"]*9070[^"]*" watts (\S+) ' } | Select-Object -First 1
|
||||
if (-not $line9070 -or $line9070 -notmatch ' watts ([\d.]+) ') { "RESULT watts error=the helper's one-shot sample has no 9070 XT line with a watts number"; Summary 'failed' @{ error = 'no 9070 watts in the one-shot sample' }; exit 2 }
|
||||
"RESULT tele helper=$tele sha256=$((Get-FileHash -Algorithm SHA256 $tele).Hash.ToLower()) one_shot_watts=$($Matches[1]) (ADLX GPUPower, GPUTotalBoardPower as the fallback)"
|
||||
"RESULT tele helper=$tele sha256=$((Get-FileHash -Algorithm SHA256 $tele).Hash.ToLower()) one_shot_watts=$($Matches[1])"
|
||||
|
||||
$samples = Join-Path $job 'amd-samples.log'
|
||||
$sampler = $null
|
||||
$switched = $false
|
||||
$samplerOk = $false
|
||||
$cardAlone = $false
|
||||
$appRows = @()
|
||||
$appT0 = $null; $appT1 = $null
|
||||
$cand = $null
|
||||
try {
|
||||
# ---- the sampler through the environment-fed wrapper; proven on 12 idle seconds before anything else ----
|
||||
# ---- the card quiet? read-only: the runner switched it off before this script started ----
|
||||
$before = @(CardWorkers)
|
||||
"RESULT card_workers_before pids=[$($before -join ' ')]"
|
||||
if ($before.Count -eq 0) { "RESULT card already quiet: no opencl worker with --device $dev (the runner's --cards-off took)"; $cardAlone = $true }
|
||||
else {
|
||||
$t = 0
|
||||
while ($t -lt 150) { Start-Sleep -Seconds 5; $t += 5; if ((@(CardWorkers)).Count -eq 0) { break } }
|
||||
$left = @(CardWorkers)
|
||||
if ($left.Count -eq 0) { "RESULT card $(Stamp) quiet after $t s: no igneum-worker-opencl process with --device $dev (the card to itself)"; $cardAlone = $true }
|
||||
else { "RESULT card $(Stamp) UNCONFIRMED after $t s: opencl worker pids [$($left -join ' ')] still carry --device $dev (was the job published with --cards-off amd:gfx1201?); the row below is a LOADED-card figure" }
|
||||
Start-Sleep -Seconds 5
|
||||
}
|
||||
"RESULT workers_during $(Stamp) $((Workers) -join ' ')"
|
||||
# ---- the sampler through the environment-fed wrapper; proven on 12 idle seconds ----
|
||||
$wrapper = Join-Path $job 'sampler.ps1'
|
||||
$wrapperText = @'
|
||||
$exe = $env:IGNEUM_SAMPLER_EXE
|
||||
|
|
@ -139,135 +117,72 @@ Add-Content -LiteralPath $out -Value ('wrapper end ' + (Get-Date).ToUniversalTim
|
|||
"RESULT sampler_idle lines=$($raw.Count) lines_9070_with_watts=$n9070 sampler_alive=$(-not $sampler.HasExited)"
|
||||
if ($n9070 -eq 0) {
|
||||
$why = if ($raw.Count -eq 0) { 'the sampler wrote nothing in 12 s (file absent or empty)' } else { 'the sampler wrote ' + $raw.Count + ' line(s) but none is a 9070 XT line with a watts number (first lines above)' }
|
||||
"RESULT watts error=$why; no rows are taken"
|
||||
"RESULT APPROW class=v3 error=$why"
|
||||
"RESULT watts error=$why; the bench is NOT run blind"
|
||||
"RESULT LADDER pack=sh256x27 error=$why"
|
||||
} else {
|
||||
$samplerOk = $true
|
||||
"RESULT idle $(Stamp) sampler proven on the idle window"
|
||||
|
||||
# ---- 1. the control row: the card ON in the app, mining class v3; /api/state at 1 Hz for 90 s, the sampler beside ----
|
||||
$sc = @(StateCard)
|
||||
if ($sc.Count -eq 0) { "RESULT APPROW class=v3 error=the app's /api/state lists no mining gfx1201 card (the card is off or the app is not running): the control row needs the card ON in the app" }
|
||||
else {
|
||||
"RESULT app_card key=$($sc[0].key) identities=$($sc[0].identities) state=$($sc[0].state) pid=$($sc[0].pid) mhs_now=$($sc[0].hash_now) watts=$($sc[0].power_w) (before the window)"
|
||||
$appT0 = (Get-Date).ToUniversalTime()
|
||||
"RESULT approw start $(Stamp) window_s=$appWindowS"
|
||||
for ($i = 0; $i -lt $appWindowS; $i++) {
|
||||
$c = @(StateCard)
|
||||
if ($c.Count -gt 0) { $appRows += [pscustomobject]@{ ts = (Get-Date).ToUniversalTime(); w = [double] $c[0].power_w; mhs = [double] $c[0].hash_now; avg = [double] $c[0].hash_avg; state = "$($c[0].state)" } }
|
||||
Start-Sleep -Seconds 1
|
||||
}
|
||||
$appT1 = (Get-Date).ToUniversalTime()
|
||||
"RESULT approw end $(Stamp) polls=$($appRows.Count)"
|
||||
$instrs = 0
|
||||
try { $pj = Get-Content -LiteralPath (Join-Path $packDir 'program.json') -Raw | ConvertFrom-Json; if ($pj.shadow) { $instrs = [int] $pj.shadow.instrs_per_hash } } catch { }
|
||||
$t0 = (Get-Date).ToUniversalTime()
|
||||
"RESULT bench sh256x27 start $(Stamp) batches=$candBatches shadow_instrs_per_hash=$instrs"
|
||||
$lines = @(& $exe --bench-pack --pack $packDir --batches $candBatches --batch-log2 24 --device $dev 2>&1 | ForEach-Object { "$_" })
|
||||
$rc = $LASTEXITCODE
|
||||
$t1 = (Get-Date).ToUniversalTime()
|
||||
$lines | Where-Object { $_ -match '^RESULT|^pack |^warm-up|^build |^kernel:|FAIL|error|MISMATCH' } | ForEach-Object { "RESULT bench sh256x27 $_" }
|
||||
"RESULT bench sh256x27 end $(Stamp) exit=$rc wall_s=$([int]($t1 - $t0).TotalSeconds)"
|
||||
$res = $lines | Where-Object { $_ -match '^RESULT pack=' } | Select-Object -First 1
|
||||
$fp = ''; $mhs = ''; $check = ''
|
||||
if ($res -and $res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }
|
||||
if ($res -and $res -match 'mhs=([\d.]+)') { $mhs = $Matches[1] }
|
||||
if ($res -and $res -match 'check=(\S+)') { $check = $Matches[1] }
|
||||
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" }
|
||||
"RESULT G1 pack=sh256x27 error=$($err -replace '\s+', ' ')"; "RESULT LADDER pack=sh256x27 error=$($err -replace '\s+', ' ')"
|
||||
} else {
|
||||
"RESULT G1 pack=sh256x27 fingerprint=$fp match=$(if ($fp -eq $expectedFp) { 'yes' } else { 'no' }) expected=$expectedFp check=$check card=gfx1201 harness=opencl-kit"
|
||||
$cand = [pscustomobject]@{ instrs = $instrs; mhs = $mhs; fp = $fp; t0 = $t0; t1 = $t1 }
|
||||
}
|
||||
|
||||
# ---- 2. the candidate row: the card off in the app, the kit worker alone on sh256x27, the sampler as the only source ----
|
||||
if ($entries.Count -gt 0) {
|
||||
$before = @(CardWorkers)
|
||||
"RESULT card_workers_before pids=[$($before -join ' ')]"
|
||||
if ($before.Count -eq 0) { "RESULT card already quiet before the switch (no opencl worker with --device ${dev})"; $cardAlone = $true }
|
||||
else {
|
||||
PostCards $false; $switched = $true
|
||||
$t = 0
|
||||
while ($t -lt 150) { Start-Sleep -Seconds 5; $t += 5; if ((@(CardWorkers)).Count -eq 0) { break } }
|
||||
$left = @(CardWorkers)
|
||||
if ($left.Count -eq 0) { "RESULT card $(Stamp) quiet after $t s: no igneum-worker-opencl process with --device $dev (the card to itself)"; $cardAlone = $true }
|
||||
else { "RESULT card $(Stamp) UNCONFIRMED after $t s: opencl worker pids [$($left -join ' ')] still carry --device $dev; the candidate row is a LOADED-card figure" }
|
||||
Start-Sleep -Seconds 8
|
||||
}
|
||||
"RESULT workers_during $(Stamp) $((Workers) -join ' ')"
|
||||
$instrs = 0
|
||||
try { $pj = Get-Content -LiteralPath (Join-Path $packDir 'program.json') -Raw | ConvertFrom-Json; if ($pj.shadow) { $instrs = [int] $pj.shadow.instrs_per_hash } } catch { }
|
||||
$t0 = (Get-Date).ToUniversalTime()
|
||||
"RESULT bench sh256x27 start $(Stamp) batches=$candBatches shadow_instrs_per_hash=$instrs"
|
||||
$lines = @(& $exe --bench-pack --pack $packDir --batches $candBatches --batch-log2 24 --device $dev 2>&1 | ForEach-Object { "$_" })
|
||||
$rc = $LASTEXITCODE
|
||||
$t1 = (Get-Date).ToUniversalTime()
|
||||
$lines | ForEach-Object { "RESULT bench sh256x27 $_" }
|
||||
"RESULT bench sh256x27 end $(Stamp) exit=$rc wall_s=$([int]($t1 - $t0).TotalSeconds)"
|
||||
$res = $lines | Where-Object { $_ -match '^RESULT pack=' } | Select-Object -First 1
|
||||
$fp = ''; $mhs = ''; $check = ''
|
||||
if ($res -and $res -match 'fingerprint=([0-9a-f]{16})') { $fp = $Matches[1] }
|
||||
if ($res -and $res -match 'mhs=([\d.]+)') { $mhs = $Matches[1] }
|
||||
if ($res -and $res -match 'check=(\S+)') { $check = $Matches[1] }
|
||||
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" }
|
||||
"RESULT G1 pack=sh256x27 error=$($err -replace '\s+', ' ')"; "RESULT LADDER pack=sh256x27 error=$($err -replace '\s+', ' ')"
|
||||
} else {
|
||||
"RESULT G1 pack=sh256x27 fingerprint=$fp match=$(if ($fp -eq $expectedFp) { 'yes' } else { 'no' }) expected=$expectedFp check=$check card=gfx1201 harness=opencl-kit"
|
||||
$cand = [pscustomobject]@{ instrs = $instrs; mhs = $mhs; fp = $fp; t0 = $t0; t1 = $t1 }
|
||||
}
|
||||
} else { "RESULT LADDER pack=sh256x27 error=no settings.json entry for the card: it cannot be switched off for the card-alone row" }
|
||||
}
|
||||
} finally {
|
||||
"RESULT finally_begin $(Stamp) (the card comes back through the RUNNER's restore on any exit, not here)"
|
||||
if ($sampler) { try { & taskkill /T /F /PID $sampler.Id 2>&1 | Out-Null; "RESULT sampler ended pid=$($sampler.Id)" } catch { "RESULT sampler end error=$($_.Exception.Message)" } }
|
||||
if ($switched) {
|
||||
PostCards $true
|
||||
"RESULT card restore posted: each settings.json entry with its own enabled flag and identities"
|
||||
$t = 0; $back = @()
|
||||
$anyEnabled = ($entries | Where-Object { $_.enabled }).Count -gt 0
|
||||
while ($t -lt 90 -and $anyEnabled) { Start-Sleep -Seconds 5; $t += 5; $back = @(CardWorkers); if ($back.Count -gt 0) { break } }
|
||||
"RESULT card_workers_after $(Stamp) pids=[$($back -join ' ')] after $t s $(if ($back.Count -gt 0) { '(the card mines again)' } elseif ($anyEnabled) { '(NOT back yet: check the app)' } else { '(no entry was enabled before this job)' })"
|
||||
}
|
||||
"RESULT workers_after $(Stamp) $((Workers) -join ' ')"
|
||||
}
|
||||
|
||||
# ---- the helper samples (9070 XT lines) ----
|
||||
# ---- the helper samples inside the bench window (after its first 12 s, before its last 2 s) ----
|
||||
$rows = @()
|
||||
if (Test-Path -LiteralPath $samples) {
|
||||
foreach ($l in (Get-Content -LiteralPath $samples)) {
|
||||
if ($l -match '^(\S+) amd (\d+) bus (\S+) kind (\S+) name "([^"]*)" watts (\S+) temp_c (\S+) fan_rpm (\S+) fan_pct (\S+) mclk_mhz (\S+) gclk_mhz (\S+) util_pct (\S+) source (\S+)') {
|
||||
if ($Matches[5] -notmatch '9070') { continue }
|
||||
if ($Matches[5] -notmatch '9070' -or $Matches[6] -eq '-') { continue }
|
||||
try { $ts = [datetime]::ParseExact($Matches[1], 'yyyy-MM-ddTHH:mm:ss.fff', [Globalization.CultureInfo]::InvariantCulture) } catch { continue }
|
||||
$w = if ($Matches[6] -eq '-') { $null } else { [double] $Matches[6] }
|
||||
$rows += [pscustomobject]@{ ts = $ts; w = $w; temp = $Matches[7]; fan = $Matches[8]; mclk = $Matches[10]; gclk = $Matches[11]; util = $Matches[12]; src = $Matches[13] }
|
||||
$rows += [pscustomobject]@{ ts = $ts; w = [double] $Matches[6]; temp = $Matches[7]; mclk = $Matches[10]; gclk = $Matches[11]; util = $Matches[12] }
|
||||
}
|
||||
}
|
||||
}
|
||||
"RESULT samples total $($rows.Count) file $samples (9070 XT lines)"
|
||||
function Window([datetime] $t0, [datetime] $t1, [int] $head, [int] $tail) { @($rows | Where-Object { $_.ts -gt $t0.AddSeconds($head) -and $_.ts -lt $t1.AddSeconds(-$tail) -and $null -ne $_.w }) }
|
||||
|
||||
# the control row from the app, and the sampler against it over the same window
|
||||
$ratio = $null; $appMean = $null; $helperMean = $null
|
||||
if ($appRows.Count -gt 0) {
|
||||
$valid = @($appRows | Where-Object { $_.w -gt 0 })
|
||||
$m = $valid | Measure-Object -Property w -Average -Minimum -Maximum
|
||||
$mh = ($valid | Measure-Object -Property mhs -Average).Average
|
||||
$ma = ($valid | Measure-Object -Property avg -Average).Average
|
||||
$distinct = ($valid | Select-Object -ExpandProperty w -Unique | Measure-Object).Count
|
||||
$appMean = $m.Average
|
||||
$uj = if ($mh -gt 0) { [math]::Round($m.Average / $mh, 3) } else { 'owed' }
|
||||
"RESULT APPROW class=v3 mhs_wall=$([math]::Round($mh, 3)) mhs_avg_app=$([math]::Round($ma, 3)) watts_mean=$([math]::Round($m.Average, 1)) watts_min=$($m.Minimum) watts_max=$($m.Maximum) samples=$($valid.Count) polls=$($appRows.Count) distinct_watts=$distinct uj=$uj window_s=$appWindowS source=api/state (the app's own helper at -l 5: the watts value moves every 5 s) states=$(($appRows | Select-Object -ExpandProperty state -Unique) -join ',')"
|
||||
$hin = @(Window $appT0 $appT1 0 0)
|
||||
if ($hin.Count -gt 0 -and $appMean -gt 0) {
|
||||
$helperMean = ($hin | Measure-Object -Property w -Average).Average
|
||||
$ratio = [math]::Round($helperMean / $appMean, 4)
|
||||
"RESULT sampler_vs_app helper_mean=$([math]::Round($helperMean, 1)) app_mean=$([math]::Round($appMean, 1)) ratio=$ratio helper_samples=$($hin.Count) app_samples=$($valid.Count) window_s=$appWindowS calibration=$(if ($ratio -ge 0.95 -and $ratio -le 1.05) { 'inside 0.95..1.05' } else { 'OUTSIDE 0.95..1.05' })"
|
||||
} else { "RESULT sampler_vs_app error=no helper samples inside the app window (helper $($hin.Count), app mean $appMean)" }
|
||||
}
|
||||
# the candidate row from the sampler, calibrated
|
||||
"RESULT samples total $($rows.Count) file $samples (9070 XT lines with watts)"
|
||||
$taken = $false
|
||||
if ($cand) {
|
||||
$in = @(Window $cand.t0 $cand.t1 12 2)
|
||||
$watts = 'owed'; $uj = 'owed'; $gclk = 'owed'; $mclk = 'owed'; $tmax = 'owed'; $wmin = ''; $wmax = ''; $why = ''
|
||||
if ($in.Count -eq 0) { $why = 'no helper samples inside the bench window' }
|
||||
elseif ($null -eq $ratio) { $why = 'no calibration ratio (the app window gave no pair of means)' }
|
||||
elseif ($ratio -lt 0.95 -or $ratio -gt 1.05) { $why = "calibration ratio $ratio outside 0.95..1.05: the helper's reading beside the app's disagrees; raw helper mean $([math]::Round(($in | Measure-Object -Property w -Average).Average, 1)) W kept for the record only" }
|
||||
else {
|
||||
$in = @($rows | Where-Object { $_.ts -gt $cand.t0.AddSeconds(12) -and $_.ts -lt $cand.t1.AddSeconds(-2) })
|
||||
$watts = 'owed'; $uj = 'owed'; $gclk = 'owed'; $mclk = 'owed'; $tmax = 'owed'; $wmin = ''; $wmax = ''; $why = ''; $rawMean = ''
|
||||
if ($in.Count -gt 0) {
|
||||
$m = $in | Measure-Object -Property w -Average -Minimum -Maximum
|
||||
$watts = [math]::Round($m.Average, 1); $wmin = $m.Minimum; $wmax = $m.Maximum
|
||||
$rawMean = [math]::Round($m.Average, 1)
|
||||
$gclk = [math]::Round((($in | ForEach-Object { [double] $_.gclk }) | Measure-Object -Average).Average)
|
||||
$mclk = [math]::Round((($in | ForEach-Object { [double] $_.mclk }) | Measure-Object -Average).Average)
|
||||
$tmax = (($in | ForEach-Object { [double] $_.temp }) | Measure-Object -Maximum).Maximum
|
||||
if ($cand.mhs -and [double] $cand.mhs -gt 0) { $uj = [math]::Round($m.Average / [double] $cand.mhs, 3) }
|
||||
}
|
||||
"RESULT LADDER pack=sh256x27 ops=102100 shadow_instrs=$($cand.instrs) mhs=$($cand.mhs) watts=$watts uj=$uj watts_min=$wmin watts_max=$wmax gclk_mhz=$gclk mclk_mhz=$mclk temp_max=$tmax samples=$($in.Count) window_s=$([int]($cand.t1 - $cand.t0).TotalSeconds) watts_source=adlx calibration_ratio=$ratio fingerprint=$($cand.fp) card_alone=$cardAlone$(if ($why) { " owed_reason=$why" })"
|
||||
if ($null -ne $ratio -and ($ratio -lt 0.95 -or $ratio -gt 1.05)) { $why = "calibration ratio $ratio outside 0.95..1.05: the helper disagreed with the app in the control job; raw helper mean $rawMean W kept for the record only" }
|
||||
else {
|
||||
$watts = $rawMean; $wmin = $m.Minimum; $wmax = $m.Maximum
|
||||
if ($cand.mhs -and [double] $cand.mhs -gt 0) { $uj = [math]::Round($m.Average / [double] $cand.mhs, 3) }
|
||||
$taken = $true
|
||||
}
|
||||
} else { $why = 'no helper samples inside the bench window' }
|
||||
"RESULT LADDER pack=sh256x27 ops=102100 shadow_instrs=$($cand.instrs) mhs=$($cand.mhs) watts=$watts uj=$uj watts_min=$wmin watts_max=$wmax gclk_mhz=$gclk mclk_mhz=$mclk temp_max=$tmax samples=$($in.Count) window_s=$([int]($cand.t1 - $cand.t0).TotalSeconds) watts_source=adlx calibration_ratio=$(if ($null -ne $ratio) { $ratio } else { 'owed' }) raw_helper_mean=$rawMean fingerprint=$($cand.fp) card_alone=$cardAlone$(if ($why) { " owed_reason=$why" })"
|
||||
}
|
||||
if ($rows.Count -gt 0) { foreach ($r in $rows) { "RESULT sample $($r.ts.ToString('HH:mm:ss')) $($r.w) $($r.temp) $($r.fan) $($r.mclk) $($r.gclk) $($r.util) $($r.src)" } }
|
||||
$haveApp = $appRows.Count -gt 0
|
||||
$haveCand = $null -ne $cand -and $null -ne $ratio -and $ratio -ge 0.95 -and $ratio -le 1.05
|
||||
$status = if (-not $samplerOk) { 'failed' } elseif ($haveApp -and $haveCand) { 'done' } elseif ($haveApp -or $cand) { 'partial' } else { 'failed' }
|
||||
foreach ($r in $rows) { "RESULT sample $($r.ts.ToString('HH:mm:ss')) $($r.w) $($r.temp) $($r.mclk) $($r.gclk) $($r.util)" }
|
||||
$status = if (-not $samplerOk) { 'failed' } elseif ($taken) { 'done' } elseif ($cand) { 'partial' } else { 'failed' }
|
||||
"RESULT end $(Stamp)"
|
||||
Summary $status @{ approw = $haveApp; candidate_row = [bool]$cand; calibration_ratio = "$ratio"; candidate_watts_taken = $haveCand; sampler_proven = $samplerOk; card_alone = $cardAlone; device = $dev }
|
||||
Summary $status @{ candidate_row = [bool]$cand; watts_taken = $taken; calibration_ratio = "$ratio"; sampler_proven = $samplerOk; card_alone = $cardAlone; device = $dev }
|
||||
if ($status -eq 'failed') { exit 1 }
|
||||
exit 0
|
||||
|
|
|
|||
Loading…
Reference in a new issue