作者热门文章
- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我想知道为什么在 C# 和 F# 中看似相同的算法之间会得到如此不同的结果。
F# 代码变体:
open System
{ 1I..(bigint (Int32.MaxValue / 100)) } |> Seq.sum
let mutable sum = 0I
for i in 1I..(bigint (Int32.MaxValue / 100)) do
sum <- sum + i
sum
let sum = ref 0I
for i in 1I..(bigint (Int32.MaxValue / 100)) do
sum := !sum + i
sum
完整的 F# 代码(4 秒):
[<EntryPoint>]
let main argv =
let sw = new Stopwatch()
sw.Start()
printfn "%A" ({ 1I..(bigint (Int32.MaxValue / 100)) } |> Seq.sum)
sw.Stop()
printfn "took %A" sw.Elapsed
Console.ReadKey() |> ignore
0
完整的 C# 代码(22 秒):
static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
sw.Start();
BigInteger sum = new BigInteger(0);
BigInteger max = new BigInteger(Int32.MaxValue / 100);
Console.WriteLine(max);
for (BigInteger i = new BigInteger(1); i <= max; ++i)
{
sum += i;
}
sw.Stop();
Console.WriteLine(sum);
Console.WriteLine(sw.Elapsed);
Console.ReadKey();
}
F# 代码在其任何变体上都需要超过 22 秒(我假设不同的实现会产生不同的运行时间,但事实似乎并非如此)。另一方面,C# 代码似乎要快得多。两者都产生相同的最终总和结果,所以我猜这些算法是等价的。我仔细检查了一下,F# 代码似乎是使用 --optimize+
标志编译的。
我做错了什么吗?
最佳答案
从中转换 F# 代码
{ 1I..(bigint (Int32.MaxValue / 100)) } |> Seq.sum;;
Real: 00:00:14.014, CPU: 00:00:14.196, GC gen0: 1743, gen1: 0
到
let mutable t = 1I
let mutable res = 0I
let max = bigint (Int32.MaxValue / 100)
while t < max do
res <- res + t
t <- t + 1I;;
Real: 00:00:05.379, CPU: 00:00:05.450, GC gen0: 748, gen1: 0
大约三倍的速度,也更接近原始的 C# 代码。
最可能的原因是 {...}
和 for i in ...
都创建了一个虚拟 seq
。通过删除它,您可以避免 seq
开销。
编辑
出于某种原因,F# 为这段代码生成了大量的 IL,并且使用了非常奇怪的比较。
如果我们明确强制比较,速度会翻倍(这有点荒谬)
对我来说,此代码的速度几乎与 C# 完全相同(单声道)。
let mutable t = 1I
let mutable res = 0I
let max = (bigint (Int32.MaxValue / 100));;
while System.Numerics.BigInteger.op_GreaterThan(max,t) do
res <- res + t;t<-System.Numerics.BigInteger.op_Increment(t)
printfn "%A" res
但是不必要的冗长。
我可能会提交一个关于此的编译器错误。
关于c# - 与 C# 相比,简单循环上的 F# 代码性能较差 - 为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20534191/
我是一名优秀的程序员,十分优秀!