Init
This commit is contained in:
commit
c394677d07
59 files changed
+5149
No files matched your search
@@ -0,0 +1,115 @@
|
||||
# Лабораторная работа 1. Последовательное вычисление и профилирование
|
||||
|
||||
**Время на работу:** 2 пары.
|
||||
|
||||
## Цель работы
|
||||
|
||||
Создать базовую линию: последовательную реализацию своей задачи и её замеры.
|
||||
Без неё невозможно показать ускорение в следующих лабораторных — нечего будет
|
||||
ускорять.
|
||||
|
||||
## Теория
|
||||
|
||||
### Последовательное вычисление
|
||||
|
||||
Программа выполняет операцию за операцией, в одном потоке управления. Это
|
||||
простейшая и самая предсказуемая модель: результат не зависит от порядка
|
||||
выполнения, время выполнения = сумма времён всех операций. Все дальнейшие
|
||||
лабораторные сравниваются с этой базой.
|
||||
|
||||
### Два типа нагрузки
|
||||
|
||||
**CPU-bound** — программа ограничена скоростью процессора: считает, шифрует,
|
||||
сортирует. Время определяется числом элементарных операций.
|
||||
|
||||
**I/O-bound** — программа ограничена ожиданием: сеть, диск, база данных.
|
||||
Время определяется временем ожидания ответов, процессор при этом простаивает.
|
||||
|
||||
Отличить просто: если удалить «полезную работу», а программа всё равно
|
||||
медленная — это I/O-bound; если быстрая — CPU-bound. Тип нагрузки определяет,
|
||||
какая модель параллелизма поможет (потоки — для I/O, процессы — для CPU,
|
||||
async — для I/O в больших количествах).
|
||||
|
||||
### Время: настенное и процессорное
|
||||
|
||||
- `time.perf_counter()` — «настенные» часы: сколько времени прошло реально
|
||||
(включая сон и ожидание). Ими меряют всё в этом курсе.
|
||||
- `time.process_time()` — только время, потраченное процессором.
|
||||
Разница между ними для I/O-задачи огромна — это её диагноз.
|
||||
|
||||
Обе функции монотонные (не откатываются назад, как `time.time()` при
|
||||
переводе часов), имеют наносекундное разрешение — всегда используйте их, а не
|
||||
`time.time()`.
|
||||
|
||||
### Профилирование
|
||||
|
||||
Профилировщик отвечает на вопрос «где программа проводит время». `cProfile`
|
||||
собирает статистику по функциям: число вызовов, суммарное время, время без
|
||||
вложенных вызовов.
|
||||
|
||||
```python
|
||||
import cProfile
|
||||
cProfile.run("main()", sort="cumulative")
|
||||
```
|
||||
|
||||
Интерпретация: колонка `cumtime` — общее время функции (с вызовами),
|
||||
`totime` — собственное. Первые строки обычно сразу показывают узкое место.
|
||||
|
||||
## Задание
|
||||
|
||||
Ваша задача и параметры — по варианту (`common/варианты.md`), доступ:
|
||||
|
||||
```python
|
||||
from common.tasks import get_variant, TASKS
|
||||
v = get_variant(13) # ваш номер
|
||||
task = TASKS[v["task_name"]]
|
||||
data = task["build"](v["cpu_params"])
|
||||
```
|
||||
|
||||
Заполните `solution.py` (интерфейсы менять нельзя):
|
||||
|
||||
1. **`run_cpu_sequential(task_name, params, parts=4)`** — выполняет задачу
|
||||
последовательно: `build` → разбиение на `parts` кусков → `kernel` для
|
||||
каждого куска в цикле → `combine` → вернуть `checksum`. Куски
|
||||
обязательны: в следующих лабораторных те же куски уйдут воркерам.
|
||||
2. **`run_io_sequential(items, delay)`** — для каждого элемента вызывает
|
||||
`common.tasks.io_fetch(item, delay)` и собирает результаты в список
|
||||
(в порядке возрастания item).
|
||||
3. **`main()`** — запускает обе части на параметрах своего варианта, выводит
|
||||
таблицу времен (`common.benchmark.make_table`) и значения checksum.
|
||||
|
||||
### Измерения для отчёта
|
||||
|
||||
- CPU-часть: T(1) при разбиении на 1, 2, 4 куска (убедитесь, что checksum
|
||||
одинаков и время почти не меняется — разбиение не должно менять работу).
|
||||
- I/O-часть: суммарное время для items из варианта; сравните
|
||||
`perf_counter` и `process_time` — увидите, сколько времени машина спит.
|
||||
- Профилирование CPU-части: `cProfile.run(...)`, топ-5 функций по времени.
|
||||
Укажите в отчёте, какая функция — узкое место.
|
||||
|
||||
## Контрольные вопросы
|
||||
|
||||
1. Чем CPU-bound задача отличается от I/O-bound? Какая у вас?
|
||||
2. Почему для замеров нужен `perf_counter`, а не `time.time()`?
|
||||
3. Что показывает `cumtime` и `tototime` в выводе cProfile?
|
||||
4. Почему checksum не должен зависеть от числа кусков? Что это проверяет?
|
||||
|
||||
## Что сдаётся
|
||||
|
||||
`solution.py` + отчёт по шаблону: таблицы T(1/2/4 кусков), время I/O-части,
|
||||
сравнение perf_counter/process_time, топ-5 из cProfile, анализ.
|
||||
|
||||
## Критерии оценки
|
||||
|
||||
| Пункт | Баллы |
|
||||
|---|---|
|
||||
| `run_cpu_sequential` работает, checksum совпадает при разном разбиении | 2 |
|
||||
| `run_io_sequential` работает | 1 |
|
||||
| Замеры и сравнение perf_counter/process_time | 2 |
|
||||
| Профилирование и вывод об узком месте | 2 |
|
||||
| Отчёт: таблицы, анализ, ответы на вопросы | 3 |
|
||||
|
||||
## Типичные ошибки
|
||||
|
||||
См. `common/типичные_ошибки.md` (пп. 8, 9). Здесь их всего две: замеры в
|
||||
отладчике и замеры без повторов.
|
||||
Reference in new issue
Block a user