Haskell: Почему `par` был определен именно так?

par объявляется как:

par  :: a -> b -> b

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

Если его целью является параллельное выполнение a и b, почему он не был определен следующим образом?:

par  :: (a, b) -> (a, b)

Взятие кортежа (неоцененных) выражений и возврат одних и тех же выражений, пока они потенциально материализуются в фоновых потоках.

Кажется, что последняя модель проще первой. Почему дизайн был выбран именно таким?


person usr    schedule 15.04.2012    source источник
comment
Я нахожу вашу версию более сложной для размышления. Пара, которую вы передаете на номинал, может быть не оценена. Кто оценивает и когда?   -  person augustss    schedule 16.04.2012


Ответы (2)


В первом случае вы можете легко запустить более двух вычислений,

c1 `par` c2 `par` c3 `par` c4 `pseq` something c1 c2 c3 c4

что было бы довольно громоздко в последнем.

person Daniel Fischer    schedule 15.04.2012
comment
Последний может быть перегружен до 8 аргументов и также иметь версию списка. - person usr; 16.04.2012
comment
Обратите внимание, что первый аргумент отбрасывается, что означает, что выживет только c4 из вашего примера (без дальнейших ухищрений). - person usr; 16.04.2012
comment
Как бы вы его перегрузили? class par a where и экземпляры до 8 кортежей? Фу. - person Daniel Fischer; 16.04.2012
comment
Хитрость заключается в том, что вы используете результаты инициированных вычислений в конечном значении, которое также получает pseq. - person Daniel Fischer; 16.04.2012
comment
@usr : функции haskell 'par' и 'pseq' тесно связаны с моделью оценки. Вы должны взглянуть на ленивую оценку с моделью совместного использования, а также на модель сборки мусора. - person Paul R; 16.04.2012
comment
Итак, я думаю, вы могли бы сказать, что компилятор Haskell должен распознавать, что выражение c1 используется несколько раз, и выделять один слот ленивой оценки для обоих случаев использования? Если бы он не выделял только один слот, одна копия вычислялась бы параллельно, но выбрасывалась бы. - person usr; 16.04.2012

Предлагаемую вами версию с кортежем можно найти как parTuple2 в Control.Parallel.Strategies с типом:

evalTuple2 :: Strategy a -> Strategy b -> Strategy (a, b)

Что касается того, почему par был разработан таким образом, par — это «более высокий уровень», как в главе 24 Real World Haskell обсуждает, где они распараллеливают быструю сортировку:

Эти изменения в нашем коде примечательны тем, что нам не нужно было говорить.

  • Сколько ядер использовать.
  • Что делают потоки, чтобы общаться друг с другом.
  • Как распределить работу между доступными ядрами.
  • Какие данные являются общими для потоков, а какие являются частными.
  • Как определить, когда все участники закончили.

В Монада для детерминированного параллелизма, Марлоу, Ньютон и Пейтон Джонс записывать:

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

person ja.    schedule 16.04.2012