Метрики: ускорение, эффективность, масштабируемость
Урок вводит численные меры того, насколько хорошо алгоритм использует много процессоров.
Ускорение (speedup) S(p) = T(1) / T(p) — во сколько раз p процессоров справились быстрее одного.
Ускорение
Базовая метрика. Если на одном ядре задача шла 100 секунд, а на четырёх — 25, ускорение равно 4. Идеальное (линейное) ускорение S(p) = p достигается редко: накладные расходы на координацию и неустранимая последовательная часть мешают. Иногда наблюдают сверхлинейное ускорение (S(p) > p) — обычно из-за того, что на нескольких ядрах данные целиком влезают в кэши, чего не было на одном.
Эффективность
Эффективность E(p) = S(p) / p показывает, какую долю мощности процессоров вы реально используете. Если 4 ядра дали ускорение 3, эффективность 0.75 — каждое ядро занято на 75%. Эффективность ближе к 1 означает, что параллелизм почти не теряется впустую. Падение эффективности с ростом p — нормальное явление: чем больше ядер, тем больше тратится на коммуникацию.
# измеряем ускорение и эффективность по замерам времени
T1 = 100.0 # время на 1 ядре
times = {1: 100.0, 2: 52.0, 4: 28.0, 8: 16.0}
print(f"{'p':>3} {'T(p)':>7} {'speedup':>8} {'eff':>6}")
for p, tp in times.items():
speedup = T1 / tp
eff = speedup / p
print(f"{p:>3} {tp:>7.1f} {speedup:>8.2f} {eff:>6.2f}")
Вывод:
p T(p) speedup eff 1 100.0 1.00 1.00 2 52.0 1.92 0.96 4 28.0 3.57 0.89 8 16.0 6.25 0.78
Видно типичную картину: ускорение растёт, но эффективность медленно падает — на 8 ядрах мы используем уже только 78% потенциала.
Почему вообще важно различать ускорение и эффективность? Потому что они отвечают на разные вопросы бизнеса. Ускорение говорит «насколько быстрее стало» — это интересно пользователю, который ждёт результат. Эффективность говорит «насколько окупились ядра» — это интересно тому, кто платит за них. Можно получить ускорение 50× на ста ядрах, но эффективность 0.5 означает, что половину дорогих ядер вы топите впустую; иногда выгоднее взять меньше ядер и эффективность повыше. Поэтому при выборе конфигурации смотрят на обе метрики сразу, а не на одну.
Масштабируемость: сильная и слабая
Сильная масштабируемость (strong scaling): фиксируем размер задачи и добавляем процессоры. Хорошо масштабируется — значит, время продолжает падать. Здесь правит закон Амдала: последовательная часть рано или поздно становится потолком.
Слабая масштабируемость (weak scaling): увеличиваем размер задачи пропорционально числу процессоров (на каждое ядро — фиксированная порция работы). Хорошо масштабируется — значит, время держится почти постоянным, сколько бы ядер вы ни добавили. Здесь правит закон Густафсона. Для «больших данных» слабое масштабирование часто важнее: мы не ускоряем фиксированную задачу, а решаем всё большие задачи за то же время.
Как работает под капотом
Метрики — это не свойство алгоритма в вакууме, а результат измерения на конкретной машине. Один и тот же алгоритм покажет разное ускорение на CPU с быстрой общей памятью и на кластере с медленной сетью. Поэтому всегда указывают, что измерялось: какой размер задачи, какое железо, какая базовая последовательная версия. Особенно коварен выбор T(1): если сравнивать с нарочно плохой однопоточной реализацией, ускорение будет завышенным.
Частые ошибки
- Сравнивать параллельную версию с медленной, неоптимизированной последовательной — это раздувает мнимое ускорение.
- Ожидать роста ускорения без предела: на фиксированной задаче оно упирается в потолок Амдала.
- Путать сильное и слабое масштабирование — это разные вопросы и разные законы.
Итоги
- Speedup = T(1)/T(p); эффективность = speedup/p (доля используемой мощности).
- Сильное масштабирование — фиксированная задача, больше ядер; слабое — задача растёт вместе с ядрами.
- Метрики зависят от железа и от честности выбора базовой версии.