[英]Bad F# code performance on simple loop compared to C# - Why?
I was wondering why I get such disparate results between apparently equal algorithms in C# and F#. 我想知道为什么我在C#和F#中显然相同的算法之间得到如此不同的结果。
F# code variants: 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
Full F# code (4s): 完整的F#代码(4s):
[<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
Full C# code (22s): 完整的C#代码(22s):
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();
}
The F# code takes more than 22s on any of its variants (I assumed different implementations would yield different running times but that doesn't seem to be the case). F#代码在其任何变体上都需要超过22秒(我假设不同的实现会产生不同的运行时间,但似乎并非如此)。 On the other hand, the C# code seems to be way faster. 另一方面,C#代码似乎更快。 Both yield the same final sum result, so I guess the algorithms are equivalent. 两者都产生相同的最终总和结果,所以我猜算法是等价的。 I double checked and the F# code seems to be compiled with the --optimize+
flag. 我仔细检查过,F#代码似乎是用--optimize+
标志编译的。
Am I doing something wrong? 难道我做错了什么?
Converting the F# code from 转换F#代码
{ 1I..(bigint (Int32.MaxValue / 100)) } |> Seq.sum;;
Real: 00:00:14.014, CPU: 00:00:14.196, GC gen0: 1743, gen1: 0
to 至
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
Approxiamately triples the speed, and is also closer to the original C# code. 接近三倍的速度,也更接近原始的C#代码。
The most likely reason is that both {...}
and for i in ...
create a dummy seq
. 最可能的原因是{...}
和for i in ...
创建了一个虚拟seq
。 By removing this, you avoid the seq
overhead. 通过删除它,您可以避免seq
开销。
EDIT 编辑
For some reason F# is generating a ridiculous amount of IL for this code, and is using a really weird comparison. 出于某种原因,F#为此代码生成了大量的IL,并使用了一个非常奇怪的比较。
If we explicitly force the comparison, the speed doubles (which is a little ridiculous) 如果我们明确强制比较,速度加倍(这有点荒谬)
This code gets pretty much exactly the same speed as the C# for me (on mono). 对于我来说,这段代码与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
but is unnecersarrily verbose. 但是不必要地冗长。
I will probably file a compiler bug about this. 我可能会提交一个编译错误。
This is the fastest/shortest functional version I could come up with - it cheats a little by using a sequence of ints. 这是我能想到的最快/最短的功能版本 - 它通过使用一系列int来欺骗一点。 It's roughly as fast as John Palmer's version on Mono. 它大约和John Palmer在Mono上的版本一样快。
{1..(System.Int32.MaxValue/100)} |> Seq.sumBy (fun x -> bigint(x)) |> printfn "%A"
I also made a functional version of what John Palmer did with one exception in that it includes the max value in the sum to match the above sequence based version: 我还制作了John Palmer所做的功能版本,但有一个例外,它包含总和中的最大值以匹配上述基于序列的版本:
let rec sum (cnt:bigint) (acc:bigint) (max:bigint) =
if bigint.op_LessThanOrEqual(cnt,max) then
sum (bigint.op_Increment(cnt)) (acc+cnt) max
else
acc
sum 1I 0I (bigint (System.Int32.MaxValue / 100)) |> printfn "%A"
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.