Init
This commit is contained in:
commit
c394677d07
59 files changed
+5149
No files matched your search
@@ -0,0 +1,166 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
common.benchmark — утилиты для замеров времени и построения таблиц/графиков.
|
||||
|
||||
Используется во всех лабораторных (1–7), чтобы отчёты были единообразными.
|
||||
|
||||
Пример:
|
||||
|
||||
from common.benchmark import time_call, make_table, save_speedup_plot
|
||||
|
||||
elapsed, result = time_call(lambda: task["kernel"](chunk), repeats=1)
|
||||
rows = [("последовательно", T1), ("потоки, p=4", T2)]
|
||||
print(make_table(rows))
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import statistics
|
||||
import time
|
||||
from typing import Callable, Dict, List, Sequence, Tuple
|
||||
|
||||
|
||||
def time_call(fn: Callable, args: tuple = (), repeats: int = 3) -> Tuple[float, any]:
|
||||
"""Замер времени выполнения fn(*args) с повторами.
|
||||
|
||||
Возвращает (лучшее_время_сек, результат). Лучшее (минимальное) время —
|
||||
стандартная практика: случайные помехи только удлиняют замер.
|
||||
"""
|
||||
best = float("inf")
|
||||
result = None
|
||||
for _ in range(repeats):
|
||||
t0 = time.perf_counter()
|
||||
result = fn(*args)
|
||||
best = min(best, time.perf_counter() - t0)
|
||||
return best, result
|
||||
|
||||
|
||||
def speedup(T1: float, Tp: float) -> float:
|
||||
"""Ускорение S(p) = T(1) / T(p)."""
|
||||
if Tp <= 0:
|
||||
return 0.0
|
||||
return T1 / Tp
|
||||
|
||||
|
||||
def efficiency(speedup_val: float, p: int) -> float:
|
||||
"""Эффективность E(p) = S(p) / p (доля, а не проценты)."""
|
||||
if p <= 0:
|
||||
return 0.0
|
||||
return speedup_val / p
|
||||
|
||||
|
||||
def amdahl(speedup_bound: float, p: int) -> float:
|
||||
"""Теоретическое ускорение по закону Амдала.
|
||||
|
||||
speedup_bound — доля последовательной части (0..1), т.е. доля кода,
|
||||
которая не распараллеливается.
|
||||
"""
|
||||
s = max(1e-12, speedup_bound)
|
||||
return 1.0 / (s + (1.0 - s) / p)
|
||||
|
||||
|
||||
def make_table(rows: Sequence[Tuple[str, float]], T1: float = None,
|
||||
title: str = "Замеры") -> str:
|
||||
"""Текстовая таблица: подход | T, c | S(p) | E(p).
|
||||
|
||||
rows — пары (название, время в секундах). T1 — время последовательного
|
||||
запуска (первая строка, если не задано отдельно).
|
||||
"""
|
||||
if T1 is None:
|
||||
T1 = rows[0][1]
|
||||
lines = [
|
||||
f"{title}",
|
||||
f"{'подход':<32} {'T, c':>10} {'S(p)':>8} {'E(p)':>8}",
|
||||
"-" * 62,
|
||||
]
|
||||
for name, t in rows:
|
||||
s = speedup(T1, t)
|
||||
p = _p_from_name(name)
|
||||
e = efficiency(s, p)
|
||||
lines.append(f"{name:<32} {t:>10.3f} {s:>8.2f} {e:>8.2f}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _p_from_name(name: str) -> int:
|
||||
"""Пытается вытащить число воркеров из названия ('потоки, p=4' -> 4)."""
|
||||
if "p=" in name:
|
||||
try:
|
||||
return int(name.split("p=")[1].split()[0].strip("),"))
|
||||
except ValueError:
|
||||
pass
|
||||
return 1
|
||||
|
||||
|
||||
def save_speedup_plot(results: Dict[str, List[Tuple[int, float]]], title: str,
|
||||
out_path: str, show: bool = False):
|
||||
"""График ускорения S(p) для нескольких подходов.
|
||||
|
||||
results = {"потоки": [(1, 1.0), (2, 1.9), (4, 3.8)],
|
||||
"процессы": [(1, 1.0), (2, 1.95), (4, 3.7)]}
|
||||
|
||||
Для каждого подхода S(p) считается относительно его же T(1) — так
|
||||
кривые корректно сравниваются между собой.
|
||||
"""
|
||||
import matplotlib
|
||||
if not show:
|
||||
matplotlib.use("Agg")
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
plt.figure(figsize=(8, 5))
|
||||
for name, pts in results.items():
|
||||
pts = sorted(pts)
|
||||
base = next((t for p, t in pts if p == 1), None)
|
||||
if base is None:
|
||||
continue
|
||||
xs = [p for p, _ in pts]
|
||||
ys = [base / t for _, t in pts]
|
||||
plt.plot(xs, ys, marker="o", label=name)
|
||||
plt.plot(xs, xs, "--", color="gray", label="идеал S(p)=p")
|
||||
plt.xlabel("число воркеров p")
|
||||
plt.ylabel("ускорение S(p) = T(1)/T(p)")
|
||||
plt.title(title)
|
||||
plt.legend()
|
||||
plt.grid(True, alpha=0.3)
|
||||
plt.tight_layout()
|
||||
os.makedirs(os.path.dirname(out_path) or ".", exist_ok=True)
|
||||
plt.savefig(out_path, dpi=150)
|
||||
if show:
|
||||
plt.show()
|
||||
plt.close()
|
||||
print(f"график сохранён: {out_path}")
|
||||
|
||||
|
||||
def save_bar_plot(results: Dict[str, float], title: str, out_path: str,
|
||||
ylabel: str = "время, c", show: bool = False):
|
||||
"""Столбчатая диаграмма: подход -> значение (время или ускорение)."""
|
||||
import matplotlib
|
||||
if not show:
|
||||
matplotlib.use("Agg")
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
names = list(results.keys())
|
||||
vals = [results[k] for k in names]
|
||||
plt.figure(figsize=(9, 5))
|
||||
plt.bar(names, vals, color="steelblue")
|
||||
plt.ylabel(ylabel)
|
||||
plt.title(title)
|
||||
plt.xticks(rotation=20, ha="right")
|
||||
plt.grid(True, axis="y", alpha=0.3)
|
||||
plt.tight_layout()
|
||||
os.makedirs(os.path.dirname(out_path) or ".", exist_ok=True)
|
||||
plt.savefig(out_path, dpi=150)
|
||||
if show:
|
||||
plt.show()
|
||||
plt.close()
|
||||
print(f"график сохранён: {out_path}")
|
||||
|
||||
|
||||
def median_time(fn: Callable, args: tuple = (), repeats: int = 5) -> Tuple[float, any]:
|
||||
"""Аналог time_call, но по медиане — устойчив к выбросам."""
|
||||
times = []
|
||||
result = None
|
||||
for _ in range(repeats):
|
||||
t0 = time.perf_counter()
|
||||
result = fn(*args)
|
||||
times.append(time.perf_counter() - t0)
|
||||
return statistics.median(times), result
|
||||
Reference in new issue
Block a user