# Лабораторная работа 7. Сравнительный анализ: одна задача — четыре подхода **Время на работу:** 2 пары. **Зависимости:** лабы 1–6. Итоговая работа. ## Цель работы Свести весь курс в одну таблицу: ваша CPU-задача и ваша I/O-нагрузка, выполненные последовательно, потоками, процессами и асинхронно. Объяснить каждую цифру. Это работа для защиты: по ней проверяется понимание всего курса. ## Теория ### Ускорение и эффективность - Ускорение: **S(p) = T(1) / T(p)** — во сколько раз быстрее на p воркерах. - Эффективность: **E(p) = S(p) / p** — насколько эффективно используется каждый воркер. E = 1 — идеал, E = 0.5 — половина мощности простаивает. ### Закон Амдала Если доля последовательного кода задачи равна s (0 ≤ s ≤ 1), то ускорение ограничено: ``` S(p) ≤ 1 / (s + (1 − s) / p) ``` При s = 0.1 и p → ∞: S ≤ 10. Даже бесконечное число воркеров не даст больше 10×. Отсюда главный вывод курса: **сначала сокращайте последовательную часть и накладные расходы, потом наращивайте p.** В `common.benchmark` есть `amdahl(s, p)` — сравните свои S(p) с теорией: зная измеренное S(p), можно оценить эффективную долю последовательной части вашего эксперимента (создание пулов, pickle, GIL-переключения — всё это «последовательная» часть). ### Матрица выбора модели | Задача | Потоки | Процессы | asyncio | |---|---|---|---| | CPU-bound (чистый Python) | ✗ (GIL) | ✓ | ✗ (блокирует loop) | | CPU-bound в C-коде (hashlib, numpy) | ✓ | ✓ | ✗ | | I/O-bound, десятки задач | ✓ | возможно (дорого) | ✓ | | I/O-bound, тысячи задач | дорого | ✗ | ✓✓ | ## Задание Заполните `solution.py` — оба сценария из ваших лабораторных: **CPU-сценарий** (задача из варианта): 1. `cpu_sequential(task_name, params)` — как в лабе 1; 2. `cpu_threads(task_name, params, p)` — как в лабе 3 (S ≈ 1!); 3. `cpu_processes(task_name, params, p)` — как в лабе 4 (S ≈ ядра). **I/O-сценарий** (items × delay из варианта): 4. `io_sequential(items, delay)` — как в лабе 1; 5. `io_threads(items, delay, p)` — как в лабе 2; 6. `io_async(items, delay)` — как в лабе 6; 7. `io_async_limited(items, delay, max_concurrent)` — с семафором 8. **`main()`** — таблицы для обоих сценариев с T(p), S(p), E(p); два графика ускорения; оценка доли s по закону Амдала для процессов. ### Ожидаемые результаты (сверьтесь) CPU: потоки S ≈ 1; процессы S(p) ≈ до числа физических ядер. I/O: последовательный — база; потоки и async — S ≈ p; async без лимита — минимальное время (нет расходов на потоки); processes для I/O — работает, но с заметными накладными расходами. ### Что дополнительно оценить - Разница T(1) у процессов против последовательного — цена старта процессов и pickle. - По S(p_max) процессов найдите s через `amdahl` (подберите s так, чтобы теория совпала с практикой) — укажите его в отчёте. ## Контрольные вопросы 1. Почему потоки не ускорили CPU-задачу, но ускорили I/O? 2. Почему asyncio на I/O обгоняет потоки? Откуда выигрыш? 3. Какая доля последовательного кода получилась у процессов по Амдалу? Что в неё входит? 4. Какую модель вы выбрали бы для веб-краулера на 10 000 страниц? Для рендера видео? Обоснуйте. 5. Что в ваших замерах не совпало с теорией и почему? ## Что сдаётся `solution.py` + отчёт: две таблицы (CPU и I/O) с T/S/E, два графика, оценка s по Амдалу, развёрнутые выводы (это итоговая работа — выводы должны покрывать весь курс), ответы на вопросы. ## Критерии оценки | Пункт | Баллы | |---|---| | Все 7 функций работают, checksum корректен | 3 | | Таблицы и графики для обоих сценариев | 3 | | Оценка s по закону Амдала с объяснением | 2 | | Выводы и ответы на вопросы (защита) | 4 | ## Типичные ошибки - Сравнение T(p) разных сценариев при разном объёме работы — объём фиксирован внутри сценария. - Забыт smoke-режим `LAB_SMOKE` (автотесты запускают solution.py подпроцессом — не удаляйте блок в main()). - Замеры в один запуск без повторов: `time_call(..., repeats=3)`.