SPB Git

spb/wp33_uqo

Public MIT
3.0 KB · 65 lines python
1#!/usr/bin/env python32"""Measure local SSD read bandwidth at expert-granularity (random reads) and sequential reads,3bypassing the page cache with F_NOCACHE (macOS). Writes a JSON record to benchmarks/raw/<out>.4Usage: ssd_bandwidth.py <file> --chunk-mb 3.4 --n 200 --out benchmarks/raw/gateA/ssd/<host>.json5"""6import argparse, fcntl, json, os, platform, random, socket, subprocess, time, statistics, sys78F_NOCACHE = 48  # from <sys/fcntl.h> on macOS910def read_chunks(path, offsets, size, nocache=True):11    fd = os.open(path, os.O_RDONLY)12    if nocache:13        fcntl.fcntl(fd, F_NOCACHE, 1)14    lat = []15    nbytes = 016    t0 = time.perf_counter()17    for off in offsets:18        t = time.perf_counter()19        b = os.pread(fd, size, off)20        lat.append(time.perf_counter() - t)21        nbytes += len(b)22    tot = time.perf_counter() - t023    os.close(fd)24    return nbytes, tot, lat2526def main():27    ap = argparse.ArgumentParser()28    ap.add_argument("file")29    ap.add_argument("--chunk-mb", type=float, default=3.4)30    ap.add_argument("--n", type=int, default=200)31    ap.add_argument("--seq-mb", type=int, default=2048)32    ap.add_argument("--seed", type=int, default=0)33    ap.add_argument("--out", required=True)34    a = ap.parse_args()35    size = int(a.chunk_mb * 1024 * 1024)36    fsize = os.path.getsize(a.file)37    rng = random.Random(a.seed)38    offsets = [rng.randrange(0, fsize - size) // 4096 * 4096 for _ in range(a.n)]39    rec = {"host": socket.gethostname(), "file": a.file, "file_bytes": fsize, "chunk_bytes": size, "n": a.n,40           "seed": a.seed, "machine": platform.machine(), "macos": platform.mac_ver()[0],41           "chip": subprocess.run(["sysctl", "-n", "machdep.cpu.brand_string"], capture_output=True, text=True).stdout.strip(),42           "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S"), "cmd": " ".join(sys.argv)}43    # random expert-sized reads, uncached44    nb, tot, lat = read_chunks(a.file, offsets, size, nocache=True)45    lat_ms = sorted(x * 1e3 for x in lat)46    rec["random_nocache"] = {"GBps": nb / tot / 1e9, "lat_ms_p50": statistics.median(lat_ms),47                              "lat_ms_p95": lat_ms[int(0.95 * len(lat_ms)) - 1], "lat_ms_p99": lat_ms[int(0.99 * len(lat_ms)) - 1],48                              "lat_ms_mean": statistics.mean(lat_ms)}49    # same offsets, page-cache allowed (second pass is warm)50    read_chunks(a.file, offsets, size, nocache=False)51    nb, tot, lat = read_chunks(a.file, offsets, size, nocache=False)52    rec["random_cached"] = {"GBps": nb / tot / 1e9, "lat_ms_p50": statistics.median(x * 1e3 for x in lat)}53    # sequential uncached54    seq = min(a.seq_mb * 1024 * 1024, fsize)55    offs = list(range(0, seq, 8 * 1024 * 1024))56    nb, tot, _ = read_chunks(a.file, offs, 8 * 1024 * 1024, nocache=True)57    rec["sequential_nocache_8MB"] = {"GBps": nb / tot / 1e9, "bytes": nb}58    os.makedirs(os.path.dirname(a.out), exist_ok=True)59    with open(a.out, "w") as f:60        json.dump(rec, f, indent=2)61    print(json.dumps(rec, indent=2))6263if __name__ == "__main__":64    main()65
Files · main
Menu

Theme