godosa-engine

git clone https://git.godosa.eu/godosa-engine

master

raw · 1448 bytes

#!/usr/bin/env python3
"""User-space instructions per thread name over an interval (perf stat --per-thread) —
a load-independent cost measure: CPU % swings with other work on the machine and
clock speed, retired instructions don't (smoke_run.sh step `instr:<s>`).

Usage: scripts/thread_instr.py PID SECONDS
"""
import re
import subprocess
import sys
from collections import defaultdict


def parse(text):
    """{thread name: instructions} from `perf stat --per-thread -x,` output."""
    out = defaultdict(int)
    for line in text.splitlines():
        parts = line.split(",")
        if len(parts) < 4 or "instructions" not in parts[3]:
            continue
        try:
            count = int(parts[1])
        except ValueError:
            continue  # <not counted>
        out[re.sub(r"-\d+$", "", parts[0].strip())] += count
    return out


def main():
    pid, seconds = sys.argv[1], float(sys.argv[2])
    res = subprocess.run(["perf", "stat", "--per-thread", "-x,", "-e", "instructions:u", "-p", pid,
                          "--", "sleep", str(seconds)], capture_output=True, text=True)
    counts = parse(res.stderr)
    total = sum(counts.values())
    print(f"instr: {total / seconds / 1e6:.0f} M/s total over {seconds:g}s")
    for name, n in sorted(counts.items(), key=lambda kv: -kv[1])[:8]:
        if n / seconds >= 1e6:
            print(f"  {n / seconds / 1e6:7.0f} M/s  {name}")


if __name__ == "__main__":
    main()