This commit is contained in:
lovinervy committed 2026-10-07 13:06:47 +05:00
commit c394677d07
59 files changed
+5149

No files matched your search

@@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
"""Демо: perf_counter против time.time и process_time.
Запуск из корня курса:
python lab01_sequential/examples/01_perf_counter_demo.py
Показывает:
- почему time.time() для замеров плох (не монотонные часы);
- как perf_counter и process_time диагностируют I/O-bound задачу.
"""
import os
import sys
import time
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
def demo_time_vs_perf():
print("--- time.time() против time.perf_counter() ---")
t1 = time.time()
p1 = time.perf_counter()
time.sleep(0.05)
t2 = time.time()
p2 = time.perf_counter()
print(f"time.time(): {(t2 - t1) * 1000:8.2f} мс (точность зависит от ОС)")
print(f"time.perf_counter():{(p2 - p1) * 1000:8.2f} мс (монотонные, наносекундное разрешение)")
def demo_cpu_vs_io():
print("\n--- CPU-bound против I/O-bound: perf_counter vs process_time ---")
# CPU-bound: считаем
t0 = time.perf_counter()
c0 = time.process_time()
s = 0.0
for i in range(3_000_000):
s += i ** 0.5
cpu_wall = time.perf_counter() - t0
cpu_proc = time.process_time() - c0
# I/O-bound: ждём (имитация сетевых запросов)
t0 = time.perf_counter()
c0 = time.process_time()
for _ in range(10):
time.sleep(0.05) # «ожидание ответа сервера»
io_wall = time.perf_counter() - t0
io_proc = time.process_time() - c0
print(f"{'':14}{'настенное, c':>14}{'процессорное, c':>18}{'доля сна':>10}")
print(f"{'CPU-bound':14}{cpu_wall:>14.3f}{cpu_proc:>18.3f}"
f"{100 * (1 - cpu_proc / cpu_wall):>9.0f}%")
print(f"{'I/O-bound':14}{io_wall:>14.3f}{io_proc:>18.3f}"
f"{100 * (1 - io_proc / io_wall):>9.0f}%")
print("\nВывод: у I/O-задачи процессор почти всё время спит —")
print("именно поэтому потоки/asyncio могут её ускорить (лабы 2 и 6).")
if __name__ == "__main__":
demo_time_vs_perf()
demo_cpu_vs_io()
@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
"""Демо: разбиение работы на куски не должно менять результат.
Запуск из корня курса:
python lab01_sequential/examples/02_split_check_demo.py
Ключевая идея курса: задача формулируется как split -> kernel -> combine ->
checksum. Разбиение — это подготовка к параллелизму: последовательный запуск
кусков обязан давать тот же checksum, что и один большой кусок.
"""
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
from common.tasks import TASKS, smoke_params
def run(task_name: str, parts: int):
"""Последовательный запуск задачи из `parts` кусков."""
task = TASKS[task_name]
params = smoke_params(task_name)
data = task["build"](params)
chunks = task["split"](data, parts)
partials = [task["kernel"](ch) for ch in chunks]
raw = task["combine"](partials)
return task["checksum"](raw)
def main():
print(f"{'задача':<12} {'parts=1':>18} {'parts=2':>18} {'parts=4':>18} совпадает")
for name in ("pi_mc", "integrate", "nqueens", "blur", "hashing"):
v1 = run(name, 1)
v2 = run(name, 2)
v4 = run(name, 4)
# float-результаты (π, интеграл) сравниваем с допуском: при разбиении
# меняется порядок суммирования, меняются последние биты мантиссы
def same(a, b):
if isinstance(a, float):
return abs(a - b) <= 1e-6 * max(1.0, abs(a))
return a == b
same12 = same(v1, v2)
same14 = same(v1, v4)
marker = "True" if (same12 and same14) else "False <-- ОШИБКА"
print(f"{name:<12} {v1:>18.6f} {v2:>18.6f} {v4:>18.6f} {marker}")
print("\nЕсли checksum зависит от разбиения — параллельная версия будет")
print("давать «случайный» результат. Это ошибка, а не особенность.")
print("(Float-задачи сравниваются с допуском — см. код демо.)")
if __name__ == "__main__":
main()
@@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
"""Демо: cProfile — где программа проводит время.
Запуск из корня курса:
python lab01_sequential/examples/03_cprofile_demo.py
На глаз две реализации выглядят одинаково. Профилировщик показывает,
что наивная transpose-версия медленнее — из-за плохой локальности памяти.
"""
import cProfile
import os
import pstats
import sys
import io
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
N = 250
def matmul_rows(A, B):
"""Доступ к B по строкам: b[k] читается целиком (хорошо для кэша)."""
C = []
for i in range(N):
Ai, row = A[i], []
for j in range(N):
s = 0.0
for k in range(N):
s += Ai[k] * B[k][j] # B[k] — колонка, каждый раз новая строка
row.append(s)
C.append(row)
return C
def main():
rng_a = [[float(i + j) for j in range(N)] for i in range(N)]
rng_b = [[float(i - j) for j in range(N)] for i in range(N)]
profiler = cProfile.Profile()
profiler.enable()
C = matmul_rows(rng_a, rng_b)
profiler.disable()
stream = io.StringIO()
stats = pstats.Stats(profiler, stream=stream)
stats.sort_stats("cumulative").print_stats(8)
print(stream.getvalue())
print(f"checksum = {sum(sum(r) for r in C):.3f}")
print("Смотрим tototime: почти всё время — во внутреннем цикле matmul_rows.")
print("Это и есть узкое место, которое будут распараллеливать лабы 3–4.")
if __name__ == "__main__":
main()