OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / tools / perf / nsys_windows.py

50 lines · python ref:
#!/usr/bin/env python3
"""Per time window: frame-time p50/p95 (present to present), GPU busy per frame (UNION of
workload intervals -- labels nest, so never a sum), and the top labelled passes.

  python3 -I tools/perf/nsys_windows.py CAPTURE.sqlite [SECONDS=10] [TOP=4]

GPU busy ~= frame time means GPU-bound; far below it means the CPU or a sync is the limit.
"""
import collections, re, sqlite3, statistics as st, sys

db = sqlite3.connect(sys.argv[1])
W = float(sys.argv[2] if len(sys.argv) > 2 else 10) * 1e9
top = int(sys.argv[3]) if len(sys.argv) > 3 else 4
pres = [r[0] for r in db.execute(
    "select v.start from VULKAN_API v join StringIds s on s.id=v.nameId "
    "where s.value='vkQueuePresentKHR' order by v.start")]
rows = db.execute(
    "select w.start, w.end, coalesce(s.value,'') from VULKAN_WORKLOAD w "
    "left join StringIds s on s.id=w.textId order by w.start").fetchall()
t0, i, ws = pres[0], 0, pres[0]
while ws < pres[-1]:
    we = ws + W
    fp = [p for p in pres if ws <= p < we]
    if len(fp) < 3:
        ws = we
        continue
    ft = [(b - a) / 1e6 for a, b in zip(fp, fp[1:])]
    nfr = len(ft)
    busy, cs, ce, agg = 0, None, None, collections.Counter()
    while i < len(rows) and rows[i][0] < fp[0]:
        i += 1
    j = i
    while j < len(rows) and rows[j][0] < fp[-1]:
        s, e, n = rows[j]
        if cs is None or s > ce:
            if cs is not None:
                busy += ce - cs
            cs, ce = s, e
        else:
            ce = max(ce, e)
        if n:
            agg[re.sub(r' \(L-?\d+\)', '', n)] += (e - s) / 1e6 / nfr
        j += 1
    if cs is not None:
        busy += ce - cs
    tops = ", ".join(f"{n[:26]} {v:.1f}" for n, v in agg.most_common(top))
    print(f"{(ws - t0) / 1e9:6.0f}s frames {nfr:4d} frame p50 {st.median(ft):6.1f} "
          f"p95 {sorted(ft)[int(.95 * nfr)]:6.1f} | GPU busy {busy / 1e6 / nfr:5.1f} ms/f | {tops}")
    ws = we