Files

49 lines
2.8 KiB
Python
Raw Permalink 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.
"""显存预算(2026-10-18 本机实测后加;同日两次修正)。
口径:整卡留 1 GB 给系统(VRAM_RESERVE_GIB),本进程上限 = CUDA 可用 − 余量。
为什么不能按"整卡 − nvidia-smi 已用"扣:WDDM 下 nvidia-smi 的"已用"含**驱动可以换出**的量,
把它当硬占用会把自己卡死——实测预训练第一次 evaluate 峰值要 ~15 GiB,那样算出的上限只有
14.45 GiB,torch 就报 OOM,而同一时刻卡上其实还有 9.98 GiB 空闲(train.py step 2000 的评估里挂掉)。
CUDA 的 mem_get_info 才是"我实际能拿到多少":它已经把可换出的部分算进去了。
limit() 上限 = min(CUDA 可用, 整卡) − 余量;显式给 VRAM_FRAC 时再取较小值(跟旧口径 0.78 对拍用)。
别的应用占多少照样打印,但只作参考、不从上限里扣。
peak_gib() 记进日志的 vram 字段:网页指标卡与 ml/selftest.sh 都按它核对,不用再去猜。
实测参考(d=192 同结构):预训练 batch=1024 的训练步峰值只有 2.76 GiB,但第一次 evaluate 就要 ~15 GiB
(kin.gaps 一次开 2.35 GiB);APG batch=48/plans=150 → 13.08 GiB。"""
import os
import subprocess
import torch
def _smi():
"""(整卡已用 GiB, 整卡总量 GiB);拿不到就 (None, None)。"""
try:
q = 'memory.used,memory.total'
r = subprocess.run(['nvidia-smi', f'--query-gpu={q}', '--format=csv,noheader,nounits'],
capture_output=True, text=True, timeout=3)
u, t = [float(x) for x in r.stdout.strip().splitlines()[0].split(',')[:2]]
return u / 1024, t / 1024
except Exception:
return None, None
def limit():
if not torch.cuda.is_available(): return False
tot = torch.cuda.get_device_properties(0).total_memory / 2**30
torch.empty(1, device='cuda') # 让本进程的 CUDA 上下文先落地,再读可用(否则会把自家上下文算进可用)
free = torch.cuda.mem_get_info()[0] / 2**30
res = float(os.environ.get('VRAM_RESERVE_GIB', '1'))
cap = min(free, tot) - res
if os.environ.get('VRAM_FRAC'): # 显式硬帽(对拍旧口径用)
cap = min(cap, tot * float(os.environ['VRAM_FRAC']))
cap = max(1.0, min(cap, tot - res))
torch.cuda.set_per_process_memory_fraction(cap / tot)
used, _ = _smi()
note = '' if used is None else ';别的应用当前占 %.1f GiB,需要时驱动自行换出,不从上限里扣' % used
print('显存上限:本进程 %.1f GiB(CUDA 可用 %.1f − 系统余量 %.1f,= 整卡 %.1f 的 %.0f%%)%s'
% (cap, free, res, tot, 100 * cap / tot, note), flush=True)
return True
def peak_gib():
return round(torch.cuda.max_memory_allocated() / 2**30, 2) if torch.cuda.is_available() else None