Signed-off-by: redearly123 <65453204+redearly123@users.noreply.github.com> Co-authored-by: redearly123 <65453204+redearly123@users.noreply.github.com>
128 lines
5.0 KiB
Python
128 lines
5.0 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
|
|
"""Raw safetensors -> GPU micro-benchmark, without vLLM's loader.
|
|
|
|
Compares CPU and GPU dtype conversion after materializing each mmap-backed tensor.
|
|
Materialization includes page faults, CPU allocation and copying; it is not a disk
|
|
bandwidth measurement. CUDA setup is excluded; transfer includes GPU allocation.
|
|
|
|
--mode cpu_cast materialize -> cast to bf16 on the CPU -> copy to GPU
|
|
--mode gpu_cast materialize -> copy source dtype to GPU -> cast on the GPU
|
|
|
|
python benchmarks/mammoth_moda2/raw_load_bench.py --model /path/MammothModa2-Preview --shards 6,7,8
|
|
OMP_NUM_THREADS=4 python benchmarks/mammoth_moda2/raw_load_bench.py --model /path --shards 6,7,8 --mode cpu_cast
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import re
|
|
import time
|
|
|
|
import torch
|
|
from safetensors import safe_open
|
|
|
|
SHARD_NO = re.compile(r"-(\d+)-of-\d+\.safetensors$")
|
|
|
|
|
|
def shard_files(model: str, shards: str) -> list[str]:
|
|
"""Resolve ``--shards`` to files, refusing empty or partial matches so a typo cannot pass as a fast load."""
|
|
files = sorted(f for f in os.listdir(model) if f.endswith(".safetensors"))
|
|
if not files:
|
|
raise SystemExit(f"no .safetensors files under {model}")
|
|
if shards != "all":
|
|
want = {int(x) for x in shards.split(",")}
|
|
found = {int(m.group(1)): f for f in files if (m := SHARD_NO.search(f))}
|
|
missing = sorted(want - found.keys())
|
|
if missing:
|
|
raise SystemExit(f"shard(s) {missing} not found under {model}; available: {sorted(found)}")
|
|
files = [found[n] for n in sorted(want)]
|
|
return [os.path.join(model, f) for f in files]
|
|
|
|
|
|
def sync() -> None:
|
|
if torch.cuda.is_available():
|
|
torch.accelerator.synchronize()
|
|
|
|
|
|
def main() -> None:
|
|
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
ap.add_argument("--model", required=True)
|
|
ap.add_argument("--shards", default="all", help="comma list of 1-based shard numbers, or 'all'")
|
|
ap.add_argument("--mode", choices=("cpu_cast", "gpu_cast"), default="cpu_cast")
|
|
ap.add_argument("--label", default="raw")
|
|
ap.add_argument("--no-cuda", action="store_true", help="read + CPU cast only (mode must be cpu_cast)")
|
|
a = ap.parse_args()
|
|
if a.no_cuda and a.mode != "cpu_cast":
|
|
ap.error("--no-cuda only makes sense with --mode cpu_cast")
|
|
|
|
paths = shard_files(a.model, a.shards)
|
|
total_bytes = sum(os.path.getsize(p) for p in paths)
|
|
|
|
if not a.no_cuda:
|
|
# Initialize the context and exercise both operations outside the timer.
|
|
warmup = torch.zeros(1).to("cuda")
|
|
warmup = warmup.to(torch.bfloat16)
|
|
sync()
|
|
del warmup
|
|
|
|
t0 = time.perf_counter()
|
|
n_tensors, cpu_bytes = 0, 0
|
|
dtypes: dict[str, int] = {}
|
|
t_materialize = t_cast = t_h2d = 0.0
|
|
for p in paths:
|
|
with safe_open(p, framework="pt", device="cpu") as f:
|
|
for k in f.keys():
|
|
r0 = time.perf_counter()
|
|
t = f.get_tensor(k).clone() # touch every page and own the CPU storage
|
|
t_materialize += time.perf_counter() - r0
|
|
dtypes[str(t.dtype)] = dtypes.get(str(t.dtype), 0) + 1
|
|
cpu_bytes += t.numel() * t.element_size()
|
|
needs_cast = t.is_floating_point() and t.dtype != torch.bfloat16
|
|
if a.mode == "cpu_cast":
|
|
c0 = time.perf_counter()
|
|
if needs_cast:
|
|
t = t.to(torch.bfloat16)
|
|
t_cast += time.perf_counter() - c0
|
|
if not a.no_cuda:
|
|
h0 = time.perf_counter()
|
|
g = t.to("cuda")
|
|
sync()
|
|
t_h2d += time.perf_counter() - h0
|
|
del g
|
|
else:
|
|
h0 = time.perf_counter()
|
|
g = t.to("cuda")
|
|
sync()
|
|
t_h2d += time.perf_counter() - h0
|
|
c0 = time.perf_counter()
|
|
if needs_cast:
|
|
g = g.to(torch.bfloat16)
|
|
sync()
|
|
t_cast += time.perf_counter() - c0
|
|
del g
|
|
n_tensors += 1
|
|
del t
|
|
total = time.perf_counter() - t0
|
|
res = {
|
|
"label": a.label,
|
|
"mode": a.mode,
|
|
"files": [os.path.basename(p) for p in paths],
|
|
"file_bytes_gib": round(total_bytes / 2**30, 2),
|
|
"tensor_bytes_gib": round(cpu_bytes / 2**30, 2),
|
|
"n_tensors": n_tensors,
|
|
"dtypes": dtypes,
|
|
"torch_threads": torch.get_num_threads(),
|
|
"cpu_affinity": len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else None,
|
|
"materialize_s": round(t_materialize, 3),
|
|
"cast_s": round(t_cast, 3),
|
|
"h2d_s": round(t_h2d, 3),
|
|
"total_s": round(total, 3),
|
|
"cuda_setup_included": False,
|
|
}
|
|
print("RAWLOAD_JSON " + json.dumps(res))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|