Files
CFDManager/docs/theory/2d_solver/bench/run_campaign.sh
T
NotBigGhostandClaude Opus 5 81985b169e Запуск в WSL2 через docker compose без NVIDIA-runtime
В 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>
2026-08-31 17:15:53 +03:00

153 lines
7.4 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Драйвер валидационной кампании. Читает scenarios.json, гоняет прогоны по одному, кладёт
# логи, ряды, сводки и гифки каждого в собственную папку и ведёт общую таблицу.
#
# ./run_campaign.sh --dry-run смета: что и сколько будет считаться
# ./run_campaign.sh --calibrate замерить фактические MLUPS этой машины
# ./run_campaign.sh --smoke три коротких прогона: проверить обвязку
# ./run_campaign.sh --resume считать, пропуская уже готовое
# ./run_campaign.sh --group A,B только выбранные группы
# ./run_campaign.sh --only cyl_re150 по подстроке идентификатора
# ./run_campaign.sh --budget-hours 24 остановиться, когда время выйдет
#
# Прогон, который упал или развалился, помечается в сводке и НЕ останавливает кампанию:
# группы E и G специально ищут предел устойчивости.
set -u
cd "$(dirname "$0")"
BIN="${KBC2D_BIN:-../target/release/kbc2d}"
SCEN="${KBC2D_SCENARIOS:-scenarios.json}"
OUT="${KBC2D_OUT:-out}"
# ВНИМАНИЕ: имя GROUPS занято самим bash (список групп пользователя), присваивание в неё
# молча игнорируется, а "$GROUPS" под root разворачивается в 0 — и выборка съедает всю
# кампанию, не сказав ни слова. Отсюда GRP_SEL.
DRY=0; RESUME=0; CALIB=0; SMOKE=0; GRP_SEL=""; ONLY=""; BUDGET=""
GPU_MLUPS="${KBC2D_GPU_MLUPS:-1200}"
CPU_MLUPS="${KBC2D_CPU_MLUPS:-22}"
while [ $# -gt 0 ]; do
case "$1" in
--dry-run) DRY=1 ;;
--resume) RESUME=1 ;;
--calibrate) CALIB=1 ;;
--smoke) SMOKE=1 ;;
--group) GRP_SEL="$2"; shift ;;
--only) ONLY="$2"; shift ;;
--budget-hours) BUDGET="$2"; shift ;;
-h|--help) sed -n '2,20p' "$0"; exit 0 ;;
*) echo "неизвестный ключ: $1" >&2; exit 2 ;;
esac
shift
done
command -v python3 >/dev/null 2>&1 && PY=python3 || PY=python
[ -x "$BIN" ] || { echo "не найден бинарь решателя: $BIN (собери cargo build --release)" >&2; exit 1; }
[ -f "$SCEN" ] || { echo "не найден список сценариев: $SCEN" >&2; exit 1; }
# ── калибровка: три коротких прогона на месте, чтобы оценки в часах были не гаданием ──
if [ "$CALIB" = 1 ]; then
echo "Калибровка на этой машине (три коротких прогона)…"
# 1920x960 добавлена не для красоты: на 95% стоимости кампании сетки крупнее
# 600 тыс. узлов, и оценивать по мелким — значит занижать пропускную способность.
for spec in "240 120 gpu" "960 480 gpu" "1920 960 gpu" "480 240 cpu"; do
set -- $spec
m=$("$BIN" --nx "$1" --ny "$2" --size 16 --refine 1 --steps 3000 --report-every 3000 \
--verbose full --backend "$3" 2>/dev/null | grep -o '[0-9.]* MLUPS' | tail -1)
echo " $1x$2 на $3: ${m:-не измерено}"
done
echo
echo "Подставь замеренное в переменные окружения и запусти смету:"
echo " KBC2D_GPU_MLUPS=<число> KBC2D_CPU_MLUPS=<число> ./run_campaign.sh --dry-run"
exit 0
fi
mkdir -p "$OUT"
SUMMARY="$OUT/summary.csv"
LOG="$OUT/campaign.log"
[ -f "$SUMMARY" ] || echo "id,group,backend,status,seconds,steps,cost,title" > "$SUMMARY"
# Выборку и порядок считает python: разбирать JSON башем — верный способ ошибиться.
PLAN=$("$PY" - "$SCEN" "$GRP_SEL" "$ONLY" "$SMOKE" <<'PYEOF'
import json, sys
scen, groups, only, smoke = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4] == "1"
runs = json.load(open(scen, encoding="utf-8"))["runs"]
if groups:
keep = {g.strip().upper() for g in groups.split(",")}
runs = [r for r in runs if r["group"].upper() in keep]
if only:
runs = [r for r in runs if only in r["id"]]
if smoke:
# три самых дешёвых прогона: обвязку проверяем, а не физику
runs = sorted(runs, key=lambda r: r["cost"])[:3]
for r in runs:
print("\t".join([r["id"], r["group"], r["backend"], str(r["cost"]),
str(r["steps"]), r["title"], "\x1f".join(r["args"])]))
PYEOF
)
[ -n "$PLAN" ] || { echo "под выборку не попал ни один прогон"; exit 0; }
total_cost=0; n=0
while IFS=$'\t' read -r id grp bk cost steps title args; do
n=$((n + 1)); total_cost=$((total_cost + cost))
done <<< "$PLAN"
est_h=$("$PY" -c "
import sys
print('%.1f' % (float(sys.argv[1]) / (float(sys.argv[2]) * 1e6) / 3600))" "$total_cost" "$GPU_MLUPS")
echo "прогонов: $n обновлений узлов: $total_cost ≈ $est_h ч при $GPU_MLUPS MLUPS"
echo "результаты: $OUT/<id>/"
echo
if [ "$DRY" = 1 ]; then
printf '%-28s %-3s %-4s %12s %10s %s\n' id гр бэк шагов часов название
while IFS=$'\t' read -r id grp bk cost steps title args; do
m=$GPU_MLUPS; [ "$bk" = cpu ] && m=$CPU_MLUPS
h=$("$PY" -c "import sys;print('%.2f'%(float(sys.argv[1])/(float(sys.argv[2])*1e6)/3600))" "$cost" "$m")
printf '%-28s %-3s %-4s %12s %10s %s\n' "$id" "$grp" "$bk" "$steps" "$h" "$title"
done <<< "$PLAN"
exit 0
fi
started=$(date +%s)
while IFS=$'\t' read -r id grp bk cost steps title args; do
dir="$OUT/$id"
if [ "$RESUME" = 1 ] && [ -f "$dir/summary.json" ]; then
echo "· $id — уже посчитан, пропускаю"
continue
fi
if [ -n "$BUDGET" ]; then
spent=$(( ($(date +%s) - started) ))
limit=$("$PY" -c "import sys;print(int(float(sys.argv[1])*3600))" "$BUDGET")
[ "$spent" -ge "$limit" ] && { echo "бюджет $BUDGET ч исчерпан, останавливаюсь"; break; }
fi
mkdir -p "$dir"
# аргументы разделены символом 0x1f: в них есть пробелы (список тел) и запятые
IFS=$'\x1f' read -r -a ARGV <<< "$args"
ARGV+=(--summary "$dir/summary.json" --csv "$dir/series.csv" --verbose full --report-every 5000)
# относительные имена файлов из сценария кладём внутрь папки прогона
for i in "${!ARGV[@]}"; do
case "${ARGV[$((i-1))]:-}" in
--gif|--xt|--case-csv) ARGV[$i]="$dir/${ARGV[$i]}" ;;
esac
done
printf '%s\n' "$BIN ${ARGV[*]}" > "$dir/cmd.txt"
echo "▶ $id ($grp, $bk) $title"
t0=$(date +%s)
if "$BIN" "${ARGV[@]}" > "$dir/log.txt" 2>&1; then st=ok; else st=fail; fi
t1=$(date +%s); dt=$((t1 - t0))
grep -q "РАЗВАЛИЛСЯ" "$dir/log.txt" 2>/dev/null && st=blewup
sed -n '/ИТОГОВЫЙ ОТЧЁТ/,$p' "$dir/log.txt" > "$dir/report.txt" 2>/dev/null
printf '%s,%s,%s,%s,%s,%s,%s,"%s"\n' "$id" "$grp" "$bk" "$st" "$dt" "$steps" "$cost" "$title" >> "$SUMMARY"
echo "$(date -Is) $id $st ${dt}s" >> "$LOG"
echo " → $st за ${dt} с"
done <<< "$PLAN"
echo
echo "готово. сводная таблица: $SUMMARY"