[英]Bad F# code performance on simple loop compared to C# - Why?
我想知道為什么我在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#代碼(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
完整的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();
}
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
但是不必要地冗長。
我可能會提交一個編譯錯誤。
這是我能想到的最快/最短的功能版本 - 它通過使用一系列int來欺騙一點。 它大約和John Palmer在Mono上的版本一樣快。
{1..(System.Int32.MaxValue/100)} |> Seq.sumBy (fun x -> bigint(x)) |> printfn "%A"
我還制作了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.