Параллелизм
Одновременное выполнение нескольких вычислений на разных ядрах, процессорах или серверах, чтобы закончить работу быстрее.
Параллелизм — одновременное выполнение нескольких частей работы на разных исполнителях: ядрах процессора, ядрах видеокарты, серверах кластера. Отчет по миллиону договоров можно считать на одном ядре час, а можно разделить договоры на 16 частей и посчитать на 16 ядрах — в идеале за четыре минуты.
Параллельность и конкурентность
Эти понятия часто путают:
- Параллельность — задачи действительно выполняются в один момент на разных ядрах.
- Конкурентность — программа ведет несколько задач, переключаясь между ними; одновременно они могут и не выполняться. Веб-сервер на одном ядре обслуживает тысячи соединений, пока каждое ждет ответа от сети или базы.
Конкурентность — про организацию программы, параллельность — про одновременное исполнение. Подробнее об исполнителях внутри программы — в термине «Потоки и процессы».
Уровни параллелизма
- Внутри процессора — ядро выполняет несколько команд за такт, векторные инструкции обрабатывают сразу несколько чисел.
- Потоки и процессы — программа делит работу между ядрами одного сервера.
- GPU — тысячи ядер выполняют одну операцию над огромным массивом данных.
- Распределенные системы — работа делится между множеством серверов, как при шардинге или в системах обработки больших данных.
Предел ускорения: закон Амдала
Ускорение ограничено частью работы, которую нельзя распараллелить. Если 10% программы обязательно выполняются последовательно, то даже на бесконечном числе ядер она ускорится не больше чем в 10 раз. Поэтому, прежде чем добавлять ядра и серверы, ищут и сокращают последовательные участки: общие блокировки, единую очередь, запись в одну таблицу.
Чем приходится платить
- Гонки и блокировки. Параллельные части, работающие с общими данными, могут мешать друг другу. Отсюда гонки состояний и взаимные блокировки. Помогают неизменяемые данные — подробнее в термине «Функциональное программирование» — и обмен сообщениями вместо общих переменных.
- Накладные расходы. Разделить работу, передать данные и собрать результат тоже стоит времени. Мелкие задачи на многих ядрах иногда выполняются медленнее, чем на одном.
- Сложность отладки. Ошибки проявляются случайно и редко, их трудно воспроизвести.
На практике
Обычно не создают потоки вручную, а используют готовые механизмы: пулы потоков, асинхронное программирование, очереди задач с несколькими обработчиками, параллельные возможности баз данных и фреймворков. Хороший признак параллельной задачи — данные легко делятся на независимые части: клиенты, документы, дни, регионы. Тогда обработку можно масштабировать простым добавлением исполнителей.