Как уже ясно объяснили Даниэль и Брайан, ваше решение, вероятно, создает слишком много кратковременных асинхронных вычислений (поэтому накладные расходы больше, чем выгоды от параллелизма). Операция AsyncGetDirectories
также, вероятно, не является неблокируемой, поскольку она не выполняет много работы. Я не вижу нигде по-настоящему асинхронной версии этой операции - как она определяется?
В любом случае, используя обычный GetDirectories
, я попробовал следующую версию (которая создает только небольшое количество параллельных асинхронных операций):
// Synchronous version
let rec folderCollectorSync path =
let dirs = Directory.GetDirectories path
for z in dirs do folderCollectorSync z
// Asynchronous version that uses synchronous when 'nesting <= 0'
let rec folderCollector path nesting =
async { if nesting <= 0 then return folderCollectorSync path
else let dirs = Directory.GetDirectories path
do! [for z in dirs -> folderCollector z (nesting - 1) ]
|> Async.Parallel |> Async.Ignore }
Вызов простой синхронной версии после определенного числа рекурсивных вызовов является распространенным приемом - он используется при распараллеливании любой очень древовидной структуры. Используя folderCollector path 2
, это запустит только десятки параллельных задач (в отличие от тысяч), поэтому оно будет более эффективным.
В образце каталога, который я использовал (с 4800 подкаталогами и 27000 файлами), я получаю:
folderCollectorSync path
занимает 1 секунду
folderCollector path 2
дубль занимает 600 мс (результат одинаков для всех вложений от 1 до 4)