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