Как правило, функциональное программирование не способствует более быстрым программам. Это облегчает параллельное и параллельное программирование. Для этого есть два основных ключа:
- Избегание изменчивого состояния имеет тенденцию уменьшать количество вещей, которые могут пойти не так в программе, и тем более в параллельной программе.
- Избегание примитивов синхронизации с общей памятью и на основе блокировок в пользу понятий более высокого уровня имеет тенденцию упрощать синхронизацию между потоками кода.
Один прекрасный пример пункта №2 состоит в том, что в Хаскеле мы имеем четкое различие между детерминированным параллелизмом и недетерминированным параллелизмом . Нет лучшего объяснения, чем цитирование превосходной книги Саймона Марлоу « Параллельное и параллельное программирование на Хаскеле» (цитаты из главы 1 ):
Параллельная программа является один , который использует множество вычислительных аппаратных средств (например, несколько процессорных ядер) для выполнения вычислений более быстро. Цель состоит в том, чтобы прийти к ответу раньше, делегируя разные части вычислений разным процессорам, которые выполняются одновременно.
В отличие от этого, параллелизм - это метод структурирования программы, в котором существует несколько потоков управления. Концептуально потоки управления выполняются «одновременно»; пользователь видит, что их эффекты чередуются. Независимо от того, выполняются ли они одновременно или нет, это деталь реализации; Параллельная программа может выполняться на одном процессоре с чередованием или на нескольких физических процессорах.
В дополнение к этому Марлоу упоминает также поднимает измерение детерминизма :
С этим связано различие между моделями детерминированного и недетерминированного программирования. Модель детерминированного программирования - это модель, в которой каждая программа может дать только один результат, тогда как недетерминированная модель программирования допускает программы, которые могут иметь разные результаты, в зависимости от некоторого аспекта выполнения. Модели параллельного программирования обязательно являются недетерминированными, поскольку они должны взаимодействовать с внешними агентами, которые вызывают события в непредсказуемое время. Однако недетерминизм имеет некоторые существенные недостатки: программы становятся значительно сложнее для проверки и рассуждений.
Для параллельного программирования мы хотели бы использовать детерминированные модели программирования, если это вообще возможно. Поскольку цель состоит в том, чтобы просто быстрее найти ответ, мы бы не стали усложнять отладку нашей программы в процессе. Детерминированное параллельное программирование - лучшее из двух миров: тестирование, отладка и анализ могут выполняться в последовательной программе, но программа работает быстрее с добавлением большего количества процессоров.
В Haskell функции параллелизма и параллелизма разработаны вокруг этих концепций. В частности, то, что другие языки объединяются в один набор функций, Haskell разделяется на два:
- Детерминированные особенности и библиотеки для параллелизма .
- Недетерминированные функции и библиотеки для параллелизма .
Если вы просто пытаетесь ускорить чистые, детерминированные вычисления, наличие детерминированного параллелизма часто делает вещи намного проще. Часто вы просто делаете что-то вроде этого:
- Напишите функцию, которая выдает список ответов, каждый из которых является дорогостоящим для вычисления, но не очень сильно зависит друг от друга. Это Haskell, поэтому списки ленивы - значения их элементов фактически не вычисляются, пока потребитель не потребует их.
- Используйте библиотеку стратегий, чтобы использовать элементы списков результатов вашей функции параллельно между несколькими ядрами.
Я фактически сделал это с одной из моих игрушечных программ проекта несколько недель назад . Распараллелить программу было тривиально - главное, что я должен был сделать - добавить код, который говорит «вычислять элементы этого списка параллельно» (строка 90), и я получил почти линейное увеличение пропускной способности в некоторые из моих более дорогих тестовых случаев.
Является ли моя программа быстрее, чем если бы я использовал обычные многопоточные утилиты на основе блокировок? Я очень сильно сомневаюсь в этом. В моем случае была полезна такая большая отдача от такого небольшого бакса - мой код, вероятно, очень неоптимальный, но из-за того, что распараллелить так легко, я получил от него гораздо большее ускорение с гораздо меньшими усилиями, чем должное профилирование и оптимизация, и нет риска гоночных условий. И это, я бы сказал, является основным способом, которым функциональное программирование позволяет вам писать «более быстрые» программы.