与C#相比,简单循环上的F#代码性能不佳 - 为什么?

dev*_*ium 5 .net c# performance f#

我想知道为什么我在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
Run Code Online (Sandbox Code Playgroud)

完整的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
Run Code Online (Sandbox Code Playgroud)

完整的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();
}
Run Code Online (Sandbox Code Playgroud)

F#代码在其任何变体上都需要超过22秒(我假设不同的实现会产生不同的运行时间,但似乎并非如此).另一方面,C#代码似乎更快.两者都产生相同的最终总和结果,所以我猜算法是等价的.我仔细检查了,F#代码似乎是用--optimize+标志编译的.

难道我做错了什么?

Joh*_*mer 8

转换F#代码

{ 1I..(bigint (Int32.MaxValue / 100)) } |> Seq.sum;;
Real: 00:00:14.014, CPU: 00:00:14.196, GC gen0: 1743, gen1: 0
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

接近三倍的速度,也更接近原始的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
Run Code Online (Sandbox Code Playgroud)

但是不必要地冗长.

我可能会提交一个编译错误.

  • 编译器生成对[`HashCompare.GenericLessOrEqualIntrinsic.](http://msdn.microsoft.com/en-us/library/vstudio/ee789259.aspx)的调用,而不是类型特定的运算符(`BigInteger.op_LessThanOrEqual`) ),其他类型的注释没有帮助.也就是说,至少可以说,出乎意料. (2认同)