Download mindxtrain/budget/resource.py from PYTHAI/mindXtrain: direct link, hf CLI and curl.
- Browser
- Download file 3.13 kB
-
https://huggingface.co/PYTHAI/mindXtrain/resolve/main/mindxtrain/budget/resource.py
- Command line
-
hf download hf://PYTHAI/mindXtrain/mindxtrain/budget/resource.py
-
curl -L -o resource.py https://huggingface.co/PYTHAI/mindXtrain/resolve/main/mindxtrain/budget/resource.py
3.13 kB
| """ResourceBudget — psutil + rocm-smi-derived training-job sizing helper. | |
| Reads host RAM, CPU, GPU memory; emits a recommended `micro_batch_size` | |
| that fits under the budget for a given `seq_len`. psutil + rocm-smi are | |
| both lazy / optional — falls back to conservative defaults if neither is | |
| available. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import shutil | |
| import subprocess | |
| from typing import cast | |
| from pydantic import BaseModel, ConfigDict, Field | |
| class ResourceBudget(BaseModel): | |
| model_config = ConfigDict(extra="forbid") | |
| host_ram_gb: float = Field(ge=0.0) | |
| cpu_cores: int = Field(ge=1) | |
| gpu_count: int = Field(ge=0) | |
| gpu_mem_gb_each: float = Field(ge=0.0) | |
| def _host_ram_gb() -> float: | |
| try: | |
| import psutil | |
| return float(psutil.virtual_memory().total) / (1024**3) | |
| except ImportError: | |
| return 0.0 | |
| def _cpu_cores() -> int: | |
| try: | |
| import psutil | |
| return int(psutil.cpu_count(logical=True) or 1) | |
| except ImportError: | |
| import os | |
| return os.cpu_count() or 1 | |
| def _gpu_mem_gb() -> tuple[int, float]: | |
| """Return (gpu_count, gpu_mem_gb_each).""" | |
| if shutil.which("rocm-smi") is None: | |
| return (0, 0.0) | |
| try: | |
| out = subprocess.run( | |
| ["rocm-smi", "--showmeminfo", "vram", "--json"], | |
| capture_output=True, | |
| text=True, | |
| timeout=10.0, | |
| check=False, | |
| ) | |
| except (subprocess.TimeoutExpired, OSError): | |
| return (0, 0.0) | |
| if out.returncode != 0: | |
| return (0, 0.0) | |
| try: | |
| data = json.loads(out.stdout) | |
| except json.JSONDecodeError: | |
| return (0, 0.0) | |
| cards = [k for k in data if k.startswith("card")] | |
| if not cards: | |
| return (0, 0.0) | |
| total_bytes = 0.0 | |
| for c in cards: | |
| for k, v in (data[c] or {}).items(): | |
| if "total" in k.lower(): | |
| try: | |
| total_bytes = max(total_bytes, float(v)) | |
| break | |
| except ValueError: | |
| continue | |
| if total_bytes == 0.0: | |
| return (len(cards), 0.0) | |
| return (len(cards), total_bytes / (1024**3)) | |
| def detect() -> ResourceBudget: | |
| """Return a `ResourceBudget` snapshot of the current host.""" | |
| gpu_count, gpu_mem = _gpu_mem_gb() | |
| return ResourceBudget( | |
| host_ram_gb=_host_ram_gb(), | |
| cpu_cores=_cpu_cores(), | |
| gpu_count=gpu_count, | |
| gpu_mem_gb_each=gpu_mem, | |
| ) | |
| def recommend_micro_batch(budget: ResourceBudget, seq_len: int, *, dtype_bytes: int = 2) -> int: | |
| """Return a conservative `micro_batch_size` that fits under the budget. | |
| Heuristic: each token consumes ~`dtype_bytes` bytes for activations + a | |
| factor for KV cache + gradients. We leave 30% headroom. | |
| """ | |
| if budget.gpu_mem_gb_each <= 0: | |
| return 1 | |
| bytes_per_token = dtype_bytes * 16 # rough activations + grads + KV factor | |
| available = budget.gpu_mem_gb_each * 0.7 * (1024**3) | |
| bs = int(available / (seq_len * bytes_per_token)) | |
| return cast(int, max(1, bs)) | |
| __all__ = ["ResourceBudget", "detect", "recommend_micro_batch"] | |