Download tools/sample_live_inference.py from Sariel00/Ling-3.0-tiny-RKNN: direct link, hf CLI and curl.
- Browser
- Download file 3 kB
-
https://huggingface.co/Sariel00/Ling-3.0-tiny-RKNN/resolve/main/tools/sample_live_inference.py
- Command line
-
hf download hf://Sariel00/Ling-3.0-tiny-RKNN/tools/sample_live_inference.py
-
curl -L -o sample_live_inference.py https://huggingface.co/Sariel00/Ling-3.0-tiny-RKNN/resolve/main/tools/sample_live_inference.py
3 kB
| #!/usr/bin/env python3 | |
| """Read-only low-rate CPU/NPU sampling of an already running inference process.""" | |
| import argparse | |
| import json | |
| import os | |
| from pathlib import Path | |
| import re | |
| import statistics | |
| import time | |
| p = argparse.ArgumentParser(description=__doc__) | |
| p.add_argument("--pid", type=int, required=True) | |
| p.add_argument("--duration", type=float, default=30) | |
| p.add_argument("--interval", type=float, default=0.1) | |
| p.add_argument("--output", type=Path, required=True) | |
| a = p.parse_args() | |
| if a.duration <= 0 or a.interval <= 0: | |
| p.error("duration and interval must be positive") | |
| hz = os.sysconf("SC_CLK_TCK") | |
| base = Path(f"/proc/{a.pid}") | |
| def ticks(path): | |
| text = path.read_text() | |
| fields = text[text.rfind(")") + 2:].split() | |
| return {"cpu": int(fields[11]) + int(fields[12]), | |
| "minflt": int(fields[7]), "majflt": int(fields[9])} | |
| start = time.monotonic() | |
| before = ticks(base / "stat") | |
| thread_before = {x.name: ticks(x / "stat")["cpu"] for x in (base / "task").iterdir()} | |
| samples = [] | |
| deadline = start | |
| while time.monotonic() - start < a.duration: | |
| sample = {"elapsed_s": time.monotonic() - start, "timestamp": time.time()} | |
| sample["npu_load_pct"] = [int(x) for x in re.findall( | |
| r"Core\d+:\s*(\d+)%", Path("/sys/kernel/debug/rknpu/load").read_text())] | |
| samples.append(sample) | |
| deadline += a.interval | |
| time.sleep(max(0, deadline - time.monotonic())) | |
| elapsed = time.monotonic() - start | |
| after = ticks(base / "stat") | |
| threads = [] | |
| for thread in (base / "task").iterdir(): | |
| if thread.name in thread_before: | |
| threads.append({"tid": int(thread.name), "name": (thread / "comm").read_text().strip(), | |
| "cpu_pct": 100 * (ticks(thread / "stat")["cpu"] - thread_before[thread.name]) / hz / elapsed}) | |
| valid = [x["npu_load_pct"] for x in samples if len(x["npu_load_pct"]) == 3] | |
| summary = {"pid": a.pid, "elapsed_s": elapsed, | |
| "process_cpu_pct": 100 * (after["cpu"] - before["cpu"]) / hz / elapsed, | |
| "minor_faults": after["minflt"] - before["minflt"], | |
| "major_faults": after["majflt"] - before["majflt"], | |
| "threads": sorted(threads, key=lambda x: -x["cpu_pct"]), | |
| "npu_mean_pct": [statistics.mean(x[i] for x in valid) for i in range(3)] if valid else [], | |
| "npu_peak_pct": [max(x[i] for x in valid) for i in range(3)] if valid else [], | |
| "npu_all_zero_fraction": sum(not any(x) for x in valid) / len(valid) if valid else None, | |
| "frequencies_khz": {f"cpu{i}": Path(f"/sys/devices/system/cpu/cpufreq/policy{i}/scaling_cur_freq").read_text().strip() for i in (4, 6)}, | |
| "npu_frequency_hz": Path("/sys/class/devfreq/fdab0000.npu/cur_freq").read_text().strip(), | |
| "cpu_temperature_millic": Path("/sys/class/thermal/thermal_zone0/temp").read_text().strip()} | |
| a.output.parent.mkdir(parents=True, exist_ok=True) | |
| a.output.write_text(json.dumps({"summary": summary, "samples": samples}, indent=2) + "\n") | |
| print(json.dumps(summary, indent=2), flush=True) | |