gpt4 book ai didi

f# - F# 与 C# 中的异步性能(有没有更好的方法来编写 async { ... })

转载 作者:行者123 更新时间:2023-12-03 23:47:53 26 4
gpt4 key购买 nike

FWIW 我认为这里详述的问题归结为 c# 编译器更智能,并制作了一个基于状态机的高效模型来处理异步代码,而 F# 编译器创建了无数通常效率较低的对象和函数调用。

无论如何,如果我有下面的 c# 函数:

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# 代码的性能明显比 c# 版本慢,而且 CPU 占用率更高。我想知道是否有更好的作曲方法。我尝试删除递归函数(它看起来有点难看,没有一会儿!而且没有可变的 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# 异步版本花费了大约两倍的时间,并且在整个运行期间消耗了大约两倍的 CPU 资源——因此占用了大约 4 倍的资源(即,它必须在内部启动更多线程?)。

(可能读取这样一个微不足道的结构有点可疑?我真的只是在戳机器看看它做了什么。与非异步版本(即直接读取)相比,c# 完成~ 同一时间,但消耗 > 两倍的 CPU。即直接 Read() 消耗 < 1/8 的 f# 资源)

所以我的问题是,当我以“正确”的方式执行 F# async 时(这是我第一次尝试使用)?

(......如果我是,那么我是否只需要修改编译器以为编译的异步添加基于状态机的实现......这有多难:-))

最佳答案

F#的Async而 TPL 边界(Async.AwaitTask/Async.StartAsTask)是最慢的。但总的来说,F# Async 本身速度较慢,应该用于 IO 密集型而不是 CPU 密集型任务。你可能会觉得这个 repo 很有趣:https://github.com/buybackoff/FSharpAsyncVsTPL

基本上,我对这两个和一个任务构建器计算表达式进行了基准测试,最初来自 FSharpx项目。与 TPL 一起使用时,任务生成器要快得多。我在我的 Spreads 库中使用了这种方法——它是用 F# 编写的,但利用了 TPL。在 this line是高度优化的计算表达式绑定(bind),它在幕后有效地执行与 C# 的 async/await 相同的事情。我对 task{} 的每次使用进行了基准测试库中的计算表达式,它非常快(陷阱不是用于计算表达式的 for/while,而是递归)。此外,它使代码可以与 C# 互操作,而 F# 的异步不能从 C# 中使用。

关于f# - F# 与 C# 中的异步性能(有没有更好的方法来编写 async { ... }),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35762747/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com