84 lines
3.5 KiB
Python
84 lines
3.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Лабораторная работа 2: многопоточный код (I/O-bound).
|
|
|
|
Заполните функции ниже. ИНТЕРФЕЙСЫ МЕНЯТЬ НЕЛЬЗЯ — по ним работают
|
|
автотесты (tests/test_lab02.py).
|
|
|
|
Запуск из корня курса:
|
|
python lab02_threads/solution.py
|
|
"""
|
|
import os
|
|
import sys
|
|
import threading
|
|
import time
|
|
|
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
|
|
|
from common.benchmark import make_table, save_speedup_plot, time_call
|
|
from common.tasks import get_variant, io_fetch
|
|
|
|
# TODO: впишите свой номер в журнале (1..20)
|
|
VARIANT_NUMBER = 0
|
|
|
|
|
|
def _split_range(items: int, parts: int):
|
|
"""Разбиение range(items) на parts почти равных кусков (списки item'ов)."""
|
|
# TODO: верните список из parts списков номеров запросов
|
|
raise NotImplementedError
|
|
|
|
|
|
def run_io_threads_manual(items: int, delay: float = 0.05, p: int = 4):
|
|
"""I/O-нагрузка на p ручных потоках.
|
|
|
|
Каждый поток обрабатывает СВОЙ кусок и кладёт результаты в СВОЙ список.
|
|
Вернуть (список_результатов, elapsed_seconds), упорядочено по item.
|
|
|
|
Подсказки:
|
|
- threading.Thread(target=worker, args=(...));
|
|
- start() всех потоков ДО первого join() — иначе выйдет последовательно.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
|
|
def run_io_threads_pool(items: int, delay: float = 0.05, p: int = 4):
|
|
"""I/O-нагрузка через ThreadPoolExecutor(max_workers=p) и pool.map.
|
|
|
|
Вернуть (список_результатов, elapsed_seconds), упорядочено по item.
|
|
pool.map сохраняет порядок входа независимо от порядка завершения.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
|
|
def main():
|
|
v = get_variant(VARIANT_NUMBER)
|
|
items, delay = v["io"]["items"], v["io"]["delay"]
|
|
print(f"Вариант {v['variant']}: I/O-нагрузка {items} запросов по {delay} c\n")
|
|
|
|
rows = {"ручные потоки": [], "пул потоков": []}
|
|
for p in v["p_list"]:
|
|
t1, (res, _) = time_call(run_io_threads_manual, (items, delay, p), repeats=1)
|
|
assert [r[0] for r in res] == list(range(items)), "порядок нарушен!"
|
|
rows["ручные потоки"].append((p, t1))
|
|
|
|
t2, (res, _) = time_call(run_io_threads_pool, (items, delay, p), repeats=1)
|
|
assert [r[0] for r in res] == list(range(items)), "порядок нарушен!"
|
|
rows["пул потоков"].append((p, t2))
|
|
|
|
ideal = items * delay / p
|
|
print(f"p={p}: вручную {t1:.3f} c | пул {t2:.3f} c | идеал ≈ {ideal:.3f} c")
|
|
|
|
print()
|
|
table_rows = [(f"пул, p={p}", t) for p, t in rows["пул потоков"]]
|
|
print(make_table(table_rows, title="Пул потоков"))
|
|
|
|
save_speedup_plot(
|
|
rows,
|
|
f"Ускорение I/O-нагрузки потоками ({items} запросов)",
|
|
os.path.join(os.path.dirname(__file__), "speedup.png"))
|
|
|
|
# TODO: в отчёт — почему ускорение близко к идеальному?
|
|
# TODO (по желанию, +1 балл): submit + as_completed, порядок завершения
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |