gen_scenarios.py раскладывает 115 прогонов по пяти долям (LPT, поправка на размер сетки по замерам dzn) — по ≈24 ч каждая; поле shard в scenarios.json. run_campaign.sh/.ps1 получили --shard. Новая точка входа образа entrypoint.sh: при KBC2D_SHARD сама выбирает путь к карте (dzn или NVIDIA), проверяет vulkaninfo и parity.py и запускает долю. Для чужих ПК под Windows/WSL2 — docker-compose.shards.yml и shard.bat. Образ notbigghost/kbc2d:1.3.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
119 lines
5.8 KiB
PowerShell
119 lines
5.8 KiB
PowerShell
# Драйвер валидационной кампании для Windows. Делает ровно то же, что run_campaign.sh, и
|
||
# читает тот же scenarios.json — целевая машина Linux, а этот вариант нужен для локальной
|
||
# отладки обвязки.
|
||
#
|
||
# .\run_campaign.ps1 -DryRun
|
||
# .\run_campaign.ps1 -Calibrate
|
||
# .\run_campaign.ps1 -Smoke
|
||
# .\run_campaign.ps1 -Resume -Group A,B
|
||
# .\run_campaign.ps1 -Only cyl_re150 -BudgetHours 6
|
||
# .\run_campaign.ps1 -Resume -Shard 3
|
||
|
||
[CmdletBinding()]
|
||
param(
|
||
[switch]$DryRun,
|
||
[switch]$Resume,
|
||
[switch]$Calibrate,
|
||
[switch]$Smoke,
|
||
[string[]]$Group,
|
||
[string]$Only,
|
||
[int]$Shard = 0,
|
||
[double]$BudgetHours = 0,
|
||
[string]$Bin = "..\target\release\kbc2d.exe",
|
||
[string]$Scenarios = "scenarios.json",
|
||
[string]$Out = "out",
|
||
[double]$GpuMlups = 1200,
|
||
[double]$CpuMlups = 22
|
||
)
|
||
|
||
Set-Location $PSScriptRoot
|
||
if (-not (Test-Path $Bin)) { throw "не найден бинарь решателя: $Bin (собери cargo build --release)" }
|
||
if (-not (Test-Path $Scenarios)) { throw "не найден список сценариев: $Scenarios" }
|
||
|
||
if ($Calibrate) {
|
||
"Калибровка на этой машине (три коротких прогона)…"
|
||
# 1920x960 добавлена не для красоты: на 95% стоимости кампании сетки крупнее
|
||
# 600 тыс. узлов, и оценивать по мелким - значит занижать пропускную способность.
|
||
foreach ($c in @(@(240,120,'gpu'), @(960,480,'gpu'), @(1920,960,'gpu'), @(480,240,'cpu'))) {
|
||
$o = & $Bin --nx $c[0] --ny $c[1] --size 16 --refine 1 --steps 3000 `
|
||
--report-every 3000 --verbose full --backend $c[2] 2>$null
|
||
$m = ($o | Select-String 'MLUPS' | Select-Object -Last 1)
|
||
" $($c[0])x$($c[1]) на $($c[2]): $(if ($m) { ($m -replace '.*?([0-9.]+) MLUPS.*','$1') } else { 'не измерено' })"
|
||
}
|
||
""
|
||
"Подставь замеренное: .\run_campaign.ps1 -DryRun -GpuMlups <число> -CpuMlups <число>"
|
||
return
|
||
}
|
||
|
||
$runs = (Get-Content $Scenarios -Raw -Encoding UTF8 | ConvertFrom-Json).runs
|
||
if ($Group) { $g = $Group | ForEach-Object { $_.ToUpper() }; $runs = $runs | Where-Object { $g -contains $_.group.ToUpper() } }
|
||
if ($Shard) { $runs = $runs | Where-Object { $_.shard -eq $Shard } }
|
||
if ($Only) { $runs = $runs | Where-Object { $_.id -like "*$Only*" } }
|
||
if ($Smoke) { $runs = $runs | Sort-Object cost | Select-Object -First 3 }
|
||
if (-not $runs) { "под выборку не попал ни один прогон"; return }
|
||
|
||
$totalCost = ($runs | Measure-Object -Property cost -Sum).Sum
|
||
$estH = $totalCost / ($GpuMlups * 1e6) / 3600
|
||
"прогонов: $($runs.Count) обновлений узлов: {0:e3} ≈ {1:F1} ч при $GpuMlups MLUPS" -f $totalCost, $estH
|
||
"результаты: $Out\<id>\"
|
||
""
|
||
|
||
if ($DryRun) {
|
||
"{0,-28} {1,-3} {2,-4} {3,12} {4,8} {5}" -f 'id','гр','бэк','шагов','часов','название'
|
||
foreach ($r in $runs) {
|
||
$m = if ($r.backend -eq 'cpu') { $CpuMlups } else { $GpuMlups }
|
||
"{0,-28} {1,-3} {2,-4} {3,12} {4,8:F2} {5}" -f $r.id, $r.group, $r.backend, $r.steps, ($r.cost/($m*1e6)/3600), $r.title
|
||
}
|
||
return
|
||
}
|
||
|
||
New-Item -ItemType Directory -Force $Out | Out-Null
|
||
$summary = Join-Path $Out 'summary.csv'
|
||
$log = Join-Path $Out 'campaign.log'
|
||
# у каждой доли своя сводка и свой журнал, как в run_campaign.sh
|
||
if ($Shard) {
|
||
$summary = Join-Path $Out "summary_shard$Shard.csv"
|
||
$log = Join-Path $Out "campaign_shard$Shard.log"
|
||
}
|
||
if (-not (Test-Path $summary)) { 'id,group,backend,status,seconds,steps,cost,title' | Out-File $summary -Encoding utf8 }
|
||
|
||
$started = Get-Date
|
||
foreach ($r in $runs) {
|
||
$dir = Join-Path $Out $r.id
|
||
if ($Resume -and (Test-Path (Join-Path $dir 'summary.json'))) { "· $($r.id) — уже посчитан, пропускаю"; continue }
|
||
if ($BudgetHours -gt 0 -and ((Get-Date) - $started).TotalHours -ge $BudgetHours) {
|
||
"бюджет $BudgetHours ч исчерпан, останавливаюсь"; break
|
||
}
|
||
New-Item -ItemType Directory -Force $dir | Out-Null
|
||
|
||
# относительные имена файлов из сценария кладём внутрь папки прогона
|
||
$argv = @()
|
||
for ($i = 0; $i -lt $r.args.Count; $i++) {
|
||
$v = $r.args[$i]
|
||
if ($i -gt 0 -and @('--gif','--xt','--case-csv') -contains $r.args[$i-1]) { $v = Join-Path $dir $v }
|
||
$argv += $v
|
||
}
|
||
$argv += @('--summary', (Join-Path $dir 'summary.json'),
|
||
'--csv', (Join-Path $dir 'series.csv'),
|
||
'--verbose','full','--report-every','5000')
|
||
|
||
"$Bin $($argv -join ' ')" | Out-File (Join-Path $dir 'cmd.txt') -Encoding utf8
|
||
"▶ $($r.id) ($($r.group), $($r.backend)) $($r.title)"
|
||
$t0 = Get-Date
|
||
& $Bin @argv 2>&1 | Out-File (Join-Path $dir 'log.txt') -Encoding utf8
|
||
$st = if ($LASTEXITCODE -eq 0) { 'ok' } else { 'fail' }
|
||
$dt = [int]((Get-Date) - $t0).TotalSeconds
|
||
if (Select-String -Path (Join-Path $dir 'log.txt') -Pattern 'РАЗВАЛИЛСЯ' -Quiet) { $st = 'blewup' }
|
||
$rep = Get-Content (Join-Path $dir 'log.txt') -Encoding utf8
|
||
$k = ($rep | Select-String 'ИТОГОВЫЙ ОТЧЁТ' | Select-Object -First 1).LineNumber
|
||
if ($k) { $rep[($k-1)..($rep.Count-1)] | Out-File (Join-Path $dir 'report.txt') -Encoding utf8 }
|
||
|
||
'{0},{1},{2},{3},{4},{5},{6},"{7}"' -f $r.id,$r.group,$r.backend,$st,$dt,$r.steps,$r.cost,$r.title |
|
||
Out-File $summary -Append -Encoding utf8
|
||
"$(Get-Date -Format o) $($r.id) $st ${dt}s" | Out-File $log -Append -Encoding utf8
|
||
" → $st за $dt с"
|
||
}
|
||
|
||
""
|
||
"готово. сводная таблица: $summary"
|