Скрипт для усреднения результатов
This commit is contained in:
115
benchmark.py
Normal file
115
benchmark.py
Normal file
@@ -0,0 +1,115 @@
|
||||
"""
|
||||
Запускает make run <number_of_runs> раз и считает статистику по времени выполнения.
|
||||
Тупо парсит out.txt и берём значение из строки "Total execution time: <time> sec".
|
||||
|
||||
python benchmark.py <number_of_runs>
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import subprocess
|
||||
import statistics
|
||||
|
||||
N = int(sys.argv[1]) if len(sys.argv) > 1 else 10
|
||||
OUT = "out.txt"
|
||||
|
||||
TIME_RE = re.compile(r"Total execution time:\s*([0-9]*\.?[0-9]+)\s*sec")
|
||||
JOB_RE = re.compile(r"Submitted batch job\s+(\d+)")
|
||||
|
||||
APPEAR_TIMEOUT = 300.0 # ждать появления out.txt
|
||||
FINISH_TIMEOUT = 3600.0 # ждать появления Total execution time (сек)
|
||||
POLL = 0.2 # частота проверки файла
|
||||
|
||||
def wait_for_exists(path: str, timeout: float):
|
||||
t0 = time.time()
|
||||
while not os.path.exists(path):
|
||||
if time.time() - t0 > timeout:
|
||||
raise TimeoutError(f"{path} did not appear within {timeout} seconds")
|
||||
time.sleep(POLL)
|
||||
|
||||
def try_read(path: str) -> str:
|
||||
try:
|
||||
with open(path, "r", encoding="utf-8", errors="replace") as f:
|
||||
return f.read()
|
||||
except FileNotFoundError:
|
||||
return ""
|
||||
except OSError:
|
||||
# бывает, что файл на NFS в момент записи недоступен на чтение
|
||||
return ""
|
||||
|
||||
def wait_for_time_line(path: str, timeout: float) -> float:
|
||||
t0 = time.time()
|
||||
last_report = 0.0
|
||||
while True:
|
||||
txt = try_read(path)
|
||||
matches = TIME_RE.findall(txt)
|
||||
if matches:
|
||||
return float(matches[-1]) # последняя встреченная строка
|
||||
|
||||
now = time.time()
|
||||
if now - t0 > timeout:
|
||||
tail = txt[-800:] if txt else "<empty>"
|
||||
raise TimeoutError("Timed out waiting for 'Total execution time' line.\n"
|
||||
f"Last 800 chars of out.txt:\n{tail}")
|
||||
|
||||
# иногда полезно печатать прогресс раз в ~5 сек
|
||||
if now - last_report > 5.0:
|
||||
last_report = now
|
||||
if txt:
|
||||
# показать последнюю непустую строку
|
||||
lines = [l for l in txt.splitlines() if l.strip()]
|
||||
if lines:
|
||||
print(f" waiting... last line: {lines[-1][:120]}", flush=True)
|
||||
else:
|
||||
print(" waiting... (out.txt empty)", flush=True)
|
||||
else:
|
||||
print(" waiting... (out.txt not readable yet)", flush=True)
|
||||
|
||||
time.sleep(POLL)
|
||||
|
||||
times = []
|
||||
|
||||
for i in range(N):
|
||||
print(f"Run {i+1}/{N} ...", flush=True)
|
||||
|
||||
# удаляем out.txt перед запуском
|
||||
try:
|
||||
os.remove(OUT)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
# запускаем make run и забираем stdout (там будет Submitted batch job XXX)
|
||||
res = subprocess.run(["make", "run"], capture_output=True, text=True)
|
||||
out = (res.stdout or "") + "\n" + (res.stderr or "")
|
||||
|
||||
job_id = None
|
||||
m = JOB_RE.search(out)
|
||||
if m:
|
||||
job_id = m.group(1)
|
||||
print(f" submitted job {job_id}", flush=True)
|
||||
else:
|
||||
print(" (job id not detected; will only watch out.txt)", flush=True)
|
||||
|
||||
# ждём появления out.txt и появления строки с Total execution time
|
||||
wait_for_exists(OUT, APPEAR_TIMEOUT)
|
||||
t = wait_for_time_line(OUT, FINISH_TIMEOUT)
|
||||
|
||||
times.append(t)
|
||||
print(f" time = {t:.3f} sec", flush=True)
|
||||
|
||||
# опционально удалить out.txt после парсинга
|
||||
try:
|
||||
os.remove(OUT)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
print("\n=== RESULTS ===")
|
||||
print(f"Runs: {len(times)}")
|
||||
print(f"Mean: {statistics.mean(times):.3f} sec")
|
||||
print(f"Median: {statistics.median(times):.3f} sec")
|
||||
print(f"Min: {min(times):.3f} sec")
|
||||
print(f"Max: {max(times):.3f} sec")
|
||||
if len(times) > 1:
|
||||
print(f"Stddev: {statistics.stdev(times):.3f} sec")
|
||||
Reference in New Issue
Block a user