GHC поддерживает конкурентный Haskell по умолчанию, без необходимости специальных опций или библиотек, скомпилированных определённым образом. Для доступа к библиотекам поддержки конкурентного Haskell просто импортируйте Control.Concurrent. Более подробная информация о конкурентном Haskell представлена в документации к этому модулю.
Программа может быть связана с опцией -threaded (см. Параметры, влияющие на компоновку). Это предоставляет две преимущества:
- Это позволяет использовать
-N ⟨x⟩, что позволяет потокам выполняться параллельно на многопроцессорной или многоядерной машине. См. Использование SMP-параллелизма. - Если поток выполняет вызов внешней функции (и вызов не помечен как
unsafe), другие Haskell-потоки в программе продолжат выполнение, пока вызов внешней функции выполняется. Кроме того,foreign exportHaskell-функции могут вызываться из нескольких операционных потоков одновременно. См. Многопоточность и FFI.
Следующие опции RTS влияют на поведение программ с конкурентным Haskell:
-
-C ⟨s⟩ -
- Значение по умолчанию:
-
20 миллисекунд
Устанавливает интервал переключения контекста на ⟨s⟩ секунд. Переключение контекста произойдёт в следующий момент выделения блока памяти после истечения таймера (выделение блока памяти происходит каждые 4к выделения). С
-C0или-C, переключения контекста будут происходить как можно чаще (при каждом выделении блока памяти).
5.5. Использование SMP-параллелизма
GHC поддерживает параллельное выполнение Haskell-программ на SMP (симметрично-многопроцессорной) системе.
Существует тонкая разница между конкурентностью и параллелизмом: параллелизм связан с тем, чтобы программа выполнялась быстрее, используя несколько процессоров одновременно. Конкурентность, с другой стороны, — это средство абстрагирования: это удобный способ структурировать программу, которая должна реагировать на несколько асинхронных событий.
Однако эти два термина тесно связаны. Использование нескольких процессоров позволяет параллельно запускать конкурентные потоки, и именно это делает GHC с его поддержкой SMP-параллелизма. Но также возможно получить улучшение производительности с параллелизмом в программах, не использующих конкурентность. Этот раздел описывает, как использовать GHC для компиляции и запуска параллельных программ, в Параллельные и конкурентные мы описываем языковые особенности, влияющие на параллелизм.
5.5.1. Параметры компиляции для SMP-параллелизма
Для использования нескольких процессоров программа должна быть связана с опцией -threaded (см. Параметры, влияющие на компоновку). Кроме того, следующие параметры компилятора влияют на параллелизм:
- -feager-blackholing
-
Blackholing — это процесс маркировки сущности вычисления (ленивого вычисления) как находящейся в стадии оценки. Это полезно по трём причинам: во-первых, это позволяет обнаруживать определённые виды бесконечных циклов (исключение
NonTermination), во-вторых, это предотвращает определённые виды утечек памяти, и, в-третьих, это предотвращает повторение вычислений в параллельной программе, потому что мы можем определить, когда вычисление уже выполняется.Опция
-feager-blackholingзаставляет каждую сущность вычисления быть помеченной как blackholed как только начинается оценка. По умолчанию используется «ленивое blackholing», где сущности вычисления помечаются как находящиеся в стадии оценки только тогда, когда поток приостанавливается по какой-либо причине. Ленивое blackholing обычно более эффективно (на 1-2% или около того), потому что большинству сущностей вычисления не нужно быть blackholed. Однако eager blackholing может избежать более повторяющихся вычислений в параллельной программе, и это часто оказывается важным для параллелизма.Мы рекомендуем компилировать любой код, предназначенный для выполнения в параллельном режиме, с флагом
-feager-blackholing.
5.5.2. Параметры RTS для SMP-параллелизма
Существует два способа запуска программы на нескольких процессорах: вызовите Control.Concurrent.setNumCapabilities из вашей программы или используйте опции RTS -N ⟨x⟩.
-
-N ⟨x⟩ -
-N -
-maxN ⟨x⟩ -
Используйте ⟨x⟩ одновременных потоков при выполнении программы.
Система времени выполнения управляет набором виртуальных процессоров, которые мы называем возможностями, количество которых определяется параметром
-N. Каждая возможность может выполнять один Haskell-поток за раз, поэтому количество возможностей равно количеству Haskell-потоков, которые могут выполняться физически параллельно. Возможность оживляется одним или несколькими операционными потоками; система времени выполнения управляет пулом операционных потоков для каждой возможности, так что если Haskell-поток выполняет вызов внешней функции (см. Многопоточность и FFI), другой операционный поток может взять на себя эту возможность.Обычно ⟨x⟩ следует выбирать, чтобы соответствовать количеству процессорных ядер на машине [1]. Например, на двухъядерной машине мы, вероятно, будем использовать
+RTS -N2 -RTS.Пропуск ⟨x⟩, т.е.
+RTS -N -RTS, позволяет системе времени выполнения самостоятельно выбирать значение ⟨x⟩ на основе количества процессоров в вашей машине.Полное отсутствие
-N⟨x⟩означает-N1.С
-maxN⟨x⟩, т.е.+RTS -maxN3 -RTS, система времени выполнения выберет не более (x), также ограниченное количеством процессоров в системе. Пропуск (x) — это ошибка; если вам нужен по умолчанию, используйте опцию-N.Будьте осторожны при использовании всех процессоров вашей машины: если некоторые процессоры заняты другими программами, это может на самом деле навредить производительности, а не улучшить её. Запрос GHC на создание больше возможностей, чем у вас физических потоков, почти всегда плохая идея.
Установка
-Nтакже приводит к включению параллельного сборщика мусора (см. Параметры RTS для управления сборщиком мусора).Текущее значение опции
-Nдоступно Haskell-программе черезControl.Concurrent.getNumCapabilities, и его можно изменить во время выполнения программы, вызвавControl.Concurrent.setNumCapabilities.
Следующие опции влияют на способ планирования потоков системой времени выполнения на процессорах:
-
-qa -
Используйте средства родственной принадлежности ОС для привязки операционных потоков к ядрам процессоров.
При включенной этой опции операционные потоки для возможности \(i\) привязаны к ядру процессора \(i\) с использованием API, предоставляемого ОС для установки родственной принадлежности потоков. Например, в Linux GHC использует
sched_setaffinity().В зависимости от вашей рабочей нагрузки и другой активности в системе, это может или не может привести к улучшению производительности. Мы рекомендуем попробовать и измерить разницу.
-
-qm -
Отключить автоматическую миграцию для балансировки нагрузки. Обычно система времени выполнения будет автоматически пытаться распределить потоки по доступным процессорам, чтобы использовать неиспользуемые процессоры; эта опция отключает это поведение. Обратите внимание, что миграция применима только к потокам; искры, создаваемые
par, балансируются по нагрузке отдельно с помощью кражи работ.Эта опция, вероятно, полезна только для конкурентных программ, которые явно планируют потоки на процессоры с помощью Control.Concurrent.forkOn.
5.5.3. Рекомендации по использованию SMP-параллелизма
Добавьте опцию RTS -s [⟨file⟩] при запуске программы, чтобы увидеть статистику по времени, которая поможет узнать, ускорилась ли ваша программа за счёт использования нескольких процессоров или нет. Если пользовательское время больше, чем затраченное время, значит программа использовала более одного процессора. Вам также следует запустить программу без -N ⟨x⟩ для сравнения.
Вывод +RTS -s расскажет вам о том, сколько «искр» было создано и выполнено во время запуска программы (см. Параметры RTS для управления сборщиком мусора), что даст вам представление о том, насколько хорошо работают ваши par аннотации.
Поддержка параллелизма в GHC улучшилась в версии 6.12.1 благодаря многочисленным экспериментам и настройкам в системе времени выполнения. Мы по-прежнему хотели бы узнать, как это работает для вас, и мы также заинтересованы в сборе параллельных программ для добавления в наш набор бенчмарков.