Spec-Zone.ru › Haskell 9

6.15.1. Параллельный и конкурирующий Haskell

6.15.1.1. Конкурирующий Haskell

Конкурирующий Haskell — это название расширения GHC для работы с параллелизмом. Оно включено по умолчанию, поэтому не требуются никакие специальные флаги. Статья Concurrent Haskell по-прежнему является отличным ресурсом, как и Tackling the awkward squad.

Для программиста Concurrent Haskell не вводит новых конструкций языка; он просто предстаёт как библиотека, Control.Concurrent. Функции, экспортируемые этой библиотекой, включают:

  • Создание и уничтожение потоков.
  • Ожидание.
  • Синхронизированные изменяемые переменные, называемые MVars
  • Поддержка связанных потоков; см. статью Extending the FFI with concurrency.

6.15.1.2. Параллельный Haskell

GHC включает поддержку запуска программ Haskell параллельно на симметричных многопроцессорных системах с общей памятью (SMP). По умолчанию GHC запускает программу на одном процессоре; если вы хотите, чтобы она выполнялась параллельно, необходимо связать свою программу с флагом -threaded и запустить её с опцией RTS -N ⟨x⟩; см. Использование SMP-параллелизма). Система времени выполнения будет распределять работающие потоки Haskell по доступным потокам ОС, запуская столько параллельных потоков, сколько вы указали с помощью опции RTS -N ⟨x⟩.

6.15.1.3. Аннотирование чистого кода для параллелизма

Обычные однопоточные программы Haskell не получат выгоды от включения SMP-параллелизма: необходимо явно указать параллелизм компилятору. Один из способов сделать это — создание потоков с помощью Concurrent Haskell (Конкурирующий Haskell), но самый простой механизм для извлечения параллелизма из чистого кода — использование комбинатора par, который тесно связан с (и часто используется вместе с) seq. Оба доступны из библиотеки parallel:

infixr 0 `par`
infixr 1 `pseq`

par  :: a -> b -> b
pseq :: a -> b -> b

Выражение (x `par` y) запускает вычисление x (до слабой нормальной формы) и возвращает y. Запускаемые вычисления (задачи) помещаются в очередь FIFO, но не выполняются сразу. Если система времени выполнения обнаруживает свободный процессор, то она может преобразовать задачу в реальный поток и запустить этот поток на свободном процессоре. Таким образом, доступный параллелизм распределяется между реальными процессорами.

Например, рассмотрим следующую параллельную версию нашей старой проблемы, nfib:

import Control.Parallel

nfib :: Int -> Int
nfib n | n <= 1 = 1
       | otherwise = par n1 (pseq n2 (n1 + n2))
                     where n1 = nfib (n-1)
                           n2 = nfib (n-2)

Для значений n больше 1 мы используем par для запуска потока для вычисления nfib (n-1), а затем используем pseq для принудительного вычисления родительским потоком nfib (n-2) перед добавлением этих двух подвыражений. В этом подходе «разделяй и властвуй» мы запускаем новый поток только для одной ветви вычисления (родительский поток оценивает другую ветвь). Также необходимо использовать pseq для обеспечения оценки родительским потоком n2 до n1 в выражении (n1 + n2 + 1). Недостаточно переупорядочить выражение как (n2 + n1 + 1), так как компилятор может не генерировать код для вычисления слагаемых слева направо.

Обратите внимание, что мы используем pseq вместо seq. Они почти эквивалентны, но различаются по своему поведению во время выполнения: seq может вычислять свои аргументы в любом порядке, но pseq должен вычислить свой первый аргумент перед вторым, что делает его более подходящим для управления порядком вычисления в сочетании с par.

При использовании par общим правилом является то, что запущенное вычисление должно потребоваться в более позднее время, но не слишком рано. Кроме того, запущенное вычисление не должно быть слишком малым, в противном случае стоимость его запуска в параллельном режиме будет слишком велика по сравнению с количеством полученного параллелизма. Практически трудно правильно учитывать эти факторы.

Можно получить немного информации о том, насколько хорошо работает par, из статистики времени выполнения; см. Опции RTS для управления сборщиком мусора.

Более сложные комбинаторы для выражения параллелизма доступны из модуля Control.Parallel.Strategies в пакете parallel. Этот модуль строит функциональность вокруг par, выражая более сложные шаблоны параллельных вычислений, такие как параллельное map.

© 2002–2007 The University Court of the University of Glasgow. All rights reserved.
Licensed under the Glasgow Haskell Compiler License.
https://downloads.haskell.org/~ghc/9.12.1/docs/users_guide/exts/concurrent.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API