В WSL2 драйвера Vulkan для Linux у NVIDIA нет: карта отдаётся через /dev/dxg по протоколу WDDM, нативный libGLX_nvidia про него не знает и перечисляет ноль устройств, поэтому Container Toolkit нечего подкладывать внутрь и docker падает с `could not select device driver "nvidia"`. С /dev/dxg умеет говорить dzn (Dozen) — драйвер Mesa, транслирующий Vulkan в D3D12. Он положен в образ, и NVIDIA-runtime для этого пути не нужен вовсе: нужны проброс устройства и монтирование /usr/lib/wsl, где Microsoft держит libd3d12.so. Правка в решателе одна: флаги инстанса wgpu теперь читаются из окружения (InstanceFlags::from_build_config().with_env()). Без этого переменная WGPU_ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER не действует, а wgpu по умолчанию МОЛЧА прячет адаптеры, не прошедшие тесты соответствия Vulkan, — под это правило попадает dzn, и решатель сообщал, что GPU не найден. Поведение по умолчанию не изменилось: без переменной такие адаптеры по-прежнему скрыты. База образа сменена с debian:bookworm-slim на archlinux:base: в пакетах Mesa у Debian и Ubuntu dzn не собирают (проверено по спискам файлов), в Arch он лежит отдельным пакетом той же версии Mesa, что и на хосте WSL. Новое: * docker-compose.wsl.yml — путь через /dev/dxg, с профилем проверок и с build: на случай, когда доступа к реестру нет; * bench/parity.py — сверка GPU-пути с CPU в f64 на течениях, где расхождение f32 и f64 не нарастает. Соответствие dzn вендором не проверено, значит проверяем сами, а не верим на слово. Замерено на Intel Iris Xe (та же карта, нативный драйвер против dzn): * точность: cd 2.39486 против 2.39486, energy_end 5.74813e-05 против 5.74810e-05 — совпадение до 5-6 значащих цифр; * скорость: плата за трансляцию падает с ростом сетки, 4.2x на 61 тыс. узлов, 1.46x на 461 тыс., 1.30x на 1.84 млн. На 95.1% стоимости кампании сетки крупнее 600 тыс. узлов, поэтому ожидаемое удорожание — около трети, не в разы. В калибровку добавлена сетка 1920x960: оценивать кампанию по 240x120 значит занижать пропускную способность вчетверо. Образ опубликован как notbigghost/kbc2d:1.1.0 (он же latest), проверен вытягиванием из реестра. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
111 lines
5.4 KiB
PowerShell
111 lines
5.4 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
|
||
|
||
[CmdletBinding()]
|
||
param(
|
||
[switch]$DryRun,
|
||
[switch]$Resume,
|
||
[switch]$Calibrate,
|
||
[switch]$Smoke,
|
||
[string[]]$Group,
|
||
[string]$Only,
|
||
[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 ($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'
|
||
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"
|