Почему я не должен использовать асинхронные рабочие процессы F # для parallelism?
Недавно я изучал F #, особенно интересуясь простотой использования данных parallelism. Идиома data |> Array.map |> Async.Parallel |> Async.RunSynchronously
кажется очень простой для понимания и простой в использовании и получения реальной ценности.
Так почему же async
на самом деле не предназначен для этого? Сам Дональд Симе говорит, что PLINQ и Futures, вероятно, лучший выбор. И другие ответы, которые я прочитал здесь, согласны с этим, а также рекомендуют TPL. (PLINQ не слишком сильно отличается от вышеупомянутых встроенных функций, если вы используете F # Powerpack для получения функций PSeq
.)
F # и функциональные языки имеют для этого большой смысл, а некоторые приложения достигли больших успехов с помощью async
parallelism.
Итак, почему я не должен использовать async
для выполнения параллельных процессов данных? Что я проиграю, написав параллельный код async
вместо использования PLINQ или TPL?
Ответы
Ответ 1
Я написал статью, которая повторяет один образец С# TPL с использованием как Task
, так и Async
, который также имеет некоторые комментарии к различию между ними. Вы можете найти его здесь, а также есть более расширенная версия на основе async.
Вот цитата из первой статьи, в которой сравниваются два варианта:
Выбор между двумя возможными реализациями зависит от многих факторов. Асинхронные рабочие процессы были разработаны специально для F #, поэтому они более естественно соответствуют языку. Они обеспечивают лучшую производительность для задач, связанных с вводом-выводом, и обеспечивают более удобную обработку исключений. Более того, последовательный синтаксис довольно удобен. С другой стороны, задачи оптимизированы для вычислений с привязкой к ЦП и упрощают доступ к результатам вычисления из других мест приложения без явного кэширования.
Ответ 2
Итак, почему я не должен использовать async для выполнения параллельных процессов данных?
Если у вас есть небольшое количество полностью независимых задач без async
и большого количества ядер, тогда нет ничего плохого в использовании async для достижения parallelism. Однако, если ваши задачи зависят каким-либо образом или у вас больше задач, чем ядер, или вы слишком сильно приближаетесь к использованию async
в код, тогда вы оставите большую производительность на столе и сможете сделать намного лучше выбирая более подходящую основу для параллельного программирования.
Обратите внимание, что ваш пример можно написать еще более элегантно, используя TPL из F #:
Array.Parallel.map f xs
Что я проиграю, написав параллельный асинхронный код вместо использования PLINQ или TPL?
Вы теряете способность записывать забытый код кэша и, следовательно, будете страдать от большого количества промахов в кеше и, следовательно, все ядра, останавливающиеся в ожидании общей памяти, что означает плохую масштабируемость в многоядерном режиме.
TPL основан на идее о том, что дочерние задачи должны выполняться в одном ядре с родителями с высокой вероятностью и, следовательно, выиграют от повторного использования одних и тех же данных, поскольку в локальном кэше процессора он будет горячим. Нет такой уверенности в async.
Ответ 3
Я всегда полагал, что TPL, PLinq и т.д. дают вам сверх того, что делает Async. (Механизм отмены - это тот, который приходит на ум.) Этот вопрос содержит несколько лучших ответов.
В этой статье намечено небольшое преимущество в производительности для TPL, но, вероятно, этого недостаточно, чтобы быть значительным.