FWIW Я думаю, что проблемы, описанные здесь, просто сводятся к тому, что компилятор С# умнее, и делает эффективную модель на основе конечного автомата для обработки асинхронного кода, тогда как компилятор F # создает множество объектов и вызовов функций, которые обычно менее эффективны.
В любом случае, если у меня есть функция С# ниже:
public async static Task<IReadOnlyList<T>> CSharpAsyncRead<T>(
SqlCommand cmd,
Func<SqlDataReader, T> createDatum)
{
var result = new List<T>();
var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
var datum = createDatum(reader);
result.Add(datum);
}
return result.AsReadOnly();
}
И затем преобразуйте это в F # следующим образом:
let fsharpAsyncRead1 (cmd:SqlCommand) createDatum = async {
let! reader =
Async.AwaitTask (cmd.ExecuteReaderAsync ())
let rec readRows (results:ResizeArray<_>) = async {
let! readAsyncResult = Async.AwaitTask (reader.ReadAsync ())
if readAsyncResult then
let datum = createDatum reader
results.Add datum
return! readRows results
else
return results.AsReadOnly() :> IReadOnlyList<_>
}
return! readRows (ResizeArray ())
}
Затем я нахожу, что производительность кода f # значительно медленнее и больше головок процессора, чем версия С#. Мне было интересно, лучше ли это сочинить. Я попытался удалить рекурсивную функцию (которая казалась немного уродливой с отсутствием времени!) И не изменяет let! S) следующим образом:
let fsharpAsyncRead2 (cmd:SqlCommand) createDatum = async {
let result = ResizeArray ()
let! reader =
Async.AwaitTask (cmd.ExecuteReaderAsync ())
let! moreData = Async.AwaitTask (reader.ReadAsync ())
let mutable isMoreData = moreData
while isMoreData do
let datum = createDatum reader
result.Add datum
let! moreData = Async.AwaitTask (reader.ReadAsync ())
isMoreData <- moreData
return result.AsReadOnly() :> IReadOnlyList<_>
}
Но производительность была в основном одинаковой.
В качестве примера производительности, когда я загружал панель рыночных данных, например:
type OHLC = {
Time : DateTime
Open : float
High : float
Low : float
Close : float
}
На моей машине версия асинхронной версии F # заняла ~ два раза и потребляла ~ вдвое больше ресурсов ЦП за все время ее работы - таким образом, занимая примерно 4 раза больше ресурсов (т.е. внутренне она должна откручивать больше потоков?).
(Возможно, несколько сомневаюсь, что вы читаете такую тривиальную структуру? Я действительно просто выталкиваю машину, чтобы посмотреть, что она делает. По сравнению с не-асинхронной версией (то есть просто прямым чтением) С# один заканчивается в одно и то же время, но потребляет > вдвое больше CPU, т.е. прямое чтение() потребляет < 1/8 ресурсов f #)
Итак, мой вопрос в том, что я делаю F # async "правильным" способом (это было мое первое попытку использования)?
(... и если я есть, то мне просто нужно пойти и изменить компилятор, чтобы добавить реализацию на основе конечного автомата для скомпилированных Asyncs... насколько это может быть сложно:-))