Почему я не должен использовать асинхронные рабочие процессы 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, но, вероятно, недостаточно, чтобы быть значительным.

Другие вопросы по тегам