172 lines
15 KiB
PowerShell
172 lines
15 KiB
PowerShell
# Counter ASIC 3.0, PC 1 job 4 (6 October 2026, optional, confirmed by main at 16:xx UTC): item 8's ladder on PC 1's
|
|
# 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 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
|
|
# last), sh256x13, sh256x27, sh256x53, sh256x88, sh64x52. Lines: `RESULT G1 ...` (the fingerprint against the Mac's
|
|
# and the 5090's), `RESULT LADDER pack=<name> ops=<N> mhs=<x> watts=<y> uj=<z> sm_mhz=<m> ...`, a `SUMMARY {json}` line.
|
|
# Read back with `node tools/jobs.mjs <job id>`. The kit: fetch job fetch-ca3-pc1-amd-20261006 (the packs).
|
|
$ErrorActionPreference = 'Continue'
|
|
$jobName = 'pc1-4070-shadow'
|
|
$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)
|
|
}
|
|
$expected = @{ 'mx8-devnet-epoch0' = '90f794dd556f7a3b'; 'sh256x13' = '59ac286fe2a5a9ef'; 'sh256x27' = '3d2e8245cc084d07'; 'sh256x53' = '4f824b15cf2b124a'; 'sh256x88' = '0572522e39a94d8a'; 'sh64x52' = '9dd010f79d8ca9f4' }
|
|
$opsPerHash = @{ 'mx8-devnet-epoch0' = 930; 'sh256x13' = 49700; 'sh256x27' = 102100; 'sh256x53' = 199600; 'sh256x88' = 330700; 'sh64x52' = 49700 }
|
|
# timed dispatches of 2^24 nonces (wall time, as the PC 2 job): a 4070-class card is expected near 40 to 50 MH/s on
|
|
# the control (approximate, no 4070 row exists yet), so 200 dispatches are about 70 to 85 s; fewer where the ALU budget binds
|
|
$batches = @{ 'mx8-devnet-epoch0' = 200; 'sh256x13' = 200; 'sh256x27' = 180; 'sh256x53' = 150; 'sh256x88' = 100; 'sh64x52' = 200 }
|
|
$order = @('mx8-devnet-epoch0', 'sh256x13', 'sh256x27', 'sh256x53', 'sh256x88', 'sh64x52', 'mx8-devnet-epoch0')
|
|
"RESULT start $(Stamp) job=$jobName machine=$env:COMPUTERNAME app_version=$env:IGNEUM_APP_VERSION"
|
|
$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 }
|
|
$packs = Join-Path $kit 'packs'
|
|
if (-not (Test-Path $packs)) { "RESULT error packs missing at $packs"; Summary 'failed' @{ error = 'packs missing' }; exit 2 }
|
|
$job = $env:IGNEUM_JOB_DIR; if (-not $job) { $job = Join-Path $env:TEMP 'igneum-ca3-pc1-4070' }; New-Item -ItemType Directory -Force -Path $job | Out-Null
|
|
$inst = @("$env:LOCALAPPDATA\Programs\Igneum Miner", "$env:ProgramFiles\Igneum Miner") | Where-Object { Test-Path (Join-Path $_ 'igneum-worker-cuda.exe') } | Select-Object -First 1
|
|
if (-not $inst) { "RESULT error no installed igneum-worker-cuda.exe"; Summary 'failed' @{ error = 'no cuda worker' }; exit 2 }
|
|
$exe = Join-Path $inst 'igneum-worker-cuda.exe'
|
|
"RESULT worker $exe sha256 $((Get-FileHash -Algorithm SHA256 $exe).Hash.ToLower()) bytes $((Get-Item $exe).Length) nvrtc_dlls $((Get-ChildItem $inst -Filter 'nvrtc*.dll').Count)"
|
|
$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('/') }
|
|
|
|
# ---- the 4070 by name: nvidia-smi index (= the app's --device for the CUDA worker, detect.rs) ----
|
|
function Smi([string] $q, $idx) { try { $a = @('--query-gpu=' + $q, '--format=csv,noheader,nounits'); if ($null -ne $idx) { $a += @('-i', "$idx") }; (& nvidia-smi @a 2>$null | ForEach-Object { "$_" }) -join ' | ' } catch { 'nvidia-smi failed' } }
|
|
$gpus = @(& nvidia-smi --query-gpu=index,name,pci.bus_id,memory.total --format=csv,noheader 2>$null | ForEach-Object { "$_" })
|
|
$gpus | ForEach-Object { "RESULT gpu $_" }
|
|
$idx = $null; $gpuName = ''
|
|
foreach ($g in $gpus) { if ($g -match '^\s*(\d+),\s*([^,]*4070[^,]*),') { $idx = [int]$Matches[1]; $gpuName = $Matches[2].Trim(); break } }
|
|
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 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' }) }
|
|
"RESULT workers_before $(Stamp) $((Workers) -join ' ') cuda_apps_on_$idx=[$((CudaApps) -join '; ')]"
|
|
|
|
$samples = Join-Path $job 'smi-samples.csv'
|
|
$sampler = $null
|
|
$samplerOk = $false
|
|
$results = @()
|
|
$cardAlone = $false
|
|
try {
|
|
$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
|
|
$smiExe = (Get-Command nvidia-smi -ErrorAction SilentlyContinue).Source
|
|
if (-not $smiExe) { $smiExe = Join-Path $env:SystemRoot 'System32\nvidia-smi.exe' }
|
|
Remove-Item -LiteralPath $samples -Force -ErrorAction SilentlyContinue
|
|
$sampler = Start-Process -FilePath $smiExe -ArgumentList @('-i', "$idx", '--query-gpu=timestamp,power.draw,utilization.gpu,clocks.sm,clocks.mem,temperature.gpu,memory.used', '--format=csv,noheader,nounits', '-l', '1') -RedirectStandardOutput $samples -NoNewWindow -PassThru
|
|
Start-Sleep -Seconds 12
|
|
# the sampler PROVES itself in the idle window: the first three raw nvidia-smi lines, and a line with a power number, or no benches
|
|
$raw = @(); if (Test-Path -LiteralPath $samples) { $raw = @(Get-Content -LiteralPath $samples) }
|
|
$raw | Select-Object -First 3 | ForEach-Object { "RESULT sampler_raw $_" }
|
|
$nPower = @($raw | Where-Object { $_ -match '^\d{4}/\d\d/\d\d \d\d:\d\d:\d\d\.\d+,\s*[\d.]+,' }).Count
|
|
"RESULT sampler_idle lines=$($raw.Count) lines_with_power=$nPower sampler_alive=$(-not $sampler.HasExited) now: $(Smi 'power.draw,utilization.gpu,clocks.sm' $idx)"
|
|
$samplerOk = $nPower -gt 0
|
|
if (-not $samplerOk) {
|
|
$why = if ($raw.Count -eq 0) { 'nvidia-smi -l 1 wrote nothing in 12 s' } else { 'nvidia-smi wrote ' + $raw.Count + ' line(s) but none carries a timestamp and a power number (first lines above)' }
|
|
"RESULT watts error=$why; the ladder is NOT run blind"
|
|
foreach ($pk in $order) { "RESULT LADDER pack=$pk error=$why" }
|
|
} else { "RESULT idle $(Stamp) sampler proven on the idle window" }
|
|
$i = 0
|
|
foreach ($pk in $order) {
|
|
if (-not $samplerOk) { break }
|
|
$i++
|
|
$d = Join-Path $packs $pk
|
|
if (-not (Test-Path $d)) { "RESULT G1 pack=$pk error=pack missing at $d"; "RESULT LADDER pack=$pk error=pack missing"; continue }
|
|
$instrs = 0; $cls = ''
|
|
try { $pj = Get-Content -LiteralPath (Join-Path $d 'program.json') -Raw | ConvertFrom-Json; $cls = [string] $pj.load_class; if ($pj.shadow) { $instrs = [int] $pj.shadow.instrs_per_hash } } catch { }
|
|
$n = $batches[$pk]
|
|
$tag = "$pk#$i"
|
|
$t0 = Get-Date
|
|
"RESULT bench $tag start $(Stamp) batches=$n class=$cls shadow_instrs_per_hash=$instrs"
|
|
$lines = @(& $exe --bench --pack $d --batches $n --batch-log2 24 --block-warps 1 --device $idx 2>&1 | ForEach-Object { "$_" })
|
|
$rc = $LASTEXITCODE
|
|
$t1 = Get-Date
|
|
$lines | ForEach-Object { "RESULT bench $tag $_" }
|
|
"RESULT bench $tag end $(Stamp) exit=$rc wall_s=$([int]($t1 - $t0).TotalSeconds)"
|
|
$res = $lines | Where-Object { $_ -match '^RESULT pack=' } | Select-Object -First 1
|
|
$fp = ''; $mhs = ''; $check = ''; $devName = ''; $nvrtc = ''; $ds = ''
|
|
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 ($res -and $res -match 'device=(\S+)') { $devName = $Matches[1] }
|
|
$tl = $lines | Where-Object { $_ -match 'nvrtc (\d+) cache \d+ dataset (\d+)' } | Select-Object -First 1
|
|
if ($tl -and $tl -match 'nvrtc (\d+) cache \d+ dataset (\d+)') { $nvrtc = $Matches[1]; $ds = $Matches[2] }
|
|
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=$pk error=$($err -replace '\s+', ' ')"; "RESULT LADDER pack=$pk error=$($err -replace '\s+', ' ')"; continue
|
|
}
|
|
if ($devName -notmatch '4070') { "RESULT G1 pack=$pk error=the worker ran on device '$devName', not the 4070 (CUDA ordinal $idx is another card: the row is not taken)"; "RESULT LADDER pack=$pk error=wrong device $devName"; continue }
|
|
$want = $expected[$pk]
|
|
$match = if ($fp -eq $want) { 'yes' } else { 'no' }
|
|
"RESULT G1 pack=$pk fingerprint=$fp match=$match expected=$want check=$check card=RTX4070 harness=cuda-installed"
|
|
$results += [pscustomobject]@{ tag = $tag; pack = $pk; instrs = $instrs; ops = $opsPerHash[$pk]; mhs = $mhs; fp = $fp; match = $match; check = $check; nvrtc_ms = $nvrtc; dataset_ms = $ds; dev = $devName; t0 = $t0; t1 = $t1 }
|
|
Start-Sleep -Seconds 3
|
|
}
|
|
} finally {
|
|
if ($sampler) { try { Stop-Process -Id $sampler.Id -Force -ErrorAction SilentlyContinue; "RESULT sampler ended pid=$($sampler.Id)" } catch { } }
|
|
# 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)"
|
|
}
|
|
# ---- the samples: the mean over each bench window after its first 12 s (NVRTC, fills, warm-up) and before its last 2 s ----
|
|
$rows = @()
|
|
if (Test-Path $samples) {
|
|
foreach ($l in (Get-Content -LiteralPath $samples)) {
|
|
$p = $l -split ',\s*'
|
|
if ($p.Count -lt 7) { continue }
|
|
try { $ts = [datetime]::ParseExact($p[0].Trim(), 'yyyy/MM/dd HH:mm:ss.fff', $null) } catch { continue }
|
|
try { $rows += [pscustomobject]@{ ts = $ts; w = [double]$p[1]; util = [double]$p[2]; sm = [double]$p[3]; mem = [double]$p[4]; temp = [double]$p[5]; used = [double]$p[6] } } catch { }
|
|
}
|
|
}
|
|
"RESULT samples total $($rows.Count) file $samples"
|
|
$g1Match = 0
|
|
foreach ($r in $results) {
|
|
$in = @($rows | Where-Object { $_.ts -gt $r.t0.AddSeconds(12) -and $_.ts -lt $r.t1.AddSeconds(-2) })
|
|
$watts = 'owed'; $uj = 'owed'; $sm = 'owed'; $mem = 'owed'; $tmax = 'owed'; $util = 'owed'; $wmin = ''; $wmax = ''
|
|
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
|
|
$sm = [math]::Round(($in | Measure-Object -Property sm -Average).Average); $mem = [math]::Round(($in | Measure-Object -Property mem -Average).Average)
|
|
$util = [math]::Round(($in | Measure-Object -Property util -Average).Average, 1); $tmax = ($in | Measure-Object -Property temp -Maximum).Maximum
|
|
if ($r.mhs -and [double] $r.mhs -gt 0) { $uj = [math]::Round($m.Average / [double] $r.mhs, 3) }
|
|
}
|
|
if ($r.match -eq 'yes') { $g1Match++ }
|
|
"RESULT LADDER pack=$($r.pack) ops=$($r.ops) shadow_instrs=$($r.instrs) mhs=$($r.mhs) watts=$watts uj=$uj watts_min=$wmin watts_max=$wmax sm_mhz=$sm mem_mhz=$mem util=$util temp_max=$tmax samples=$($in.Count) window_s=$([int]($r.t1 - $r.t0).TotalSeconds) nvrtc_ms=$($r.nvrtc_ms) dataset_ms=$($r.dataset_ms) fingerprint=$($r.fp) match=$($r.match) card_alone=$cardAlone device=$($r.dev) tag=$($r.tag)"
|
|
}
|
|
foreach ($r in $rows) { "RESULT sample $($r.ts.ToString('HH:mm:ss')) $($r.w) $($r.util) $($r.sm) $($r.mem) $($r.temp) $($r.used)" }
|
|
$status = if (-not $samplerOk) { 'failed' } elseif ($results.Count -eq $order.Count) { 'done' } elseif ($results.Count -gt 0) { 'partial' } else { 'failed' }
|
|
"RESULT end $(Stamp)"
|
|
Summary $status @{ packs_run = $results.Count; packs_wanted = $order.Count; g1_match = $g1Match; sampler_proven = $samplerOk; card_alone = $cardAlone; smi_index = $idx; device = $gpuName }
|
|
if ($status -eq 'failed') { exit 1 }
|
|
exit 0
|