188 lines
16 KiB
PowerShell
188 lines
16 KiB
PowerShell
# 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'
|
|
$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)
|
|
}
|
|
$expectedFp = '3d2e8245cc084d07' # sh256x27 at base nonce 0, 2^24 (the Mac, the 5090, PC 1 job 1)
|
|
$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 }
|
|
$exe = Join-Path $kit 'bin\igneum-worker-opencl.exe'
|
|
$packDir = Join-Path $kit 'packs\sh256x27'
|
|
if (-not (Test-Path $exe)) { "RESULT error worker missing at $exe"; Summary 'failed' @{ error = 'worker missing' }; exit 2 }
|
|
if (-not (Test-Path $packDir)) { "RESULT error pack missing at $packDir"; Summary 'failed' @{ error = 'pack missing' }; exit 2 }
|
|
"RESULT worker kit $exe sha256 $((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()) bytes $((Get-Item $exe).Length)"
|
|
$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' }
|
|
"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 | 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: the row stays OWED)"; Summary 'failed' @{ error = 'no gfx1201' }; exit 2 }
|
|
"RESULT device $dev gfx1201 (list from $listExe)"
|
|
# 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') { "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 CardWorkers { @(Get-CimInstance Win32_Process -Filter "Name = 'igneum-worker-opencl.exe'" -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -match "--device\s+$dev(\s|$)" } | ForEach-Object { $_.ProcessId }) }
|
|
"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 | 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])"
|
|
|
|
$samples = Join-Path $job 'amd-samples.log'
|
|
$sampler = $null
|
|
$samplerOk = $false
|
|
$cardAlone = $false
|
|
$cand = $null
|
|
try {
|
|
# ---- 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
|
|
$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; the bench is NOT run blind"
|
|
"RESULT LADDER pack=sh256x27 error=$why"
|
|
} else {
|
|
$samplerOk = $true
|
|
"RESULT idle $(Stamp) sampler proven on the idle window"
|
|
$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 }
|
|
}
|
|
}
|
|
} 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)" } }
|
|
"RESULT workers_after $(Stamp) $((Workers) -join ' ')"
|
|
}
|
|
# ---- 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' -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]; temp = $Matches[7]; mclk = $Matches[10]; gclk = $Matches[11]; util = $Matches[12] }
|
|
}
|
|
}
|
|
}
|
|
"RESULT samples total $($rows.Count) file $samples (9070 XT lines with watts)"
|
|
$taken = $false
|
|
if ($cand) {
|
|
$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
|
|
$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 ($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" })"
|
|
}
|
|
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 @{ 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
|