2013-12-12 5 views
5

Zastanawiam się, dlaczego dostaję tak różne wyniki między pozornie równymi algorytmami w C# i F #.Niepoprawna wydajność kodu F # na prostej pętli w porównaniu do C# - Dlaczego?

F # Kod warianty:

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 

Pełna F # kod (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 

Pełny kod 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 # Kod trwa dłużej niż 22s w każdym z jego wariantów (założyłem, że różne implementacje przyniosą różne czasy działania, ale wydaje się, że tak nie jest). Z drugiej strony kod C# wydaje się szybszy. Oba dają taki sam wynik końcowy, więc myślę, że algorytmy są równoważne. Sprawdziłem dwukrotnie i kod F # wydaje się być skompilowany z flagą --optimize+.

Czy robię coś nie tak?

+2

Spróbuj użyć 'do pętla 'for' zamiast' in'. – ildjarn

+0

@ildjarn - dla pętli z 'to' wymaga' int' types, ale tutaj używamy 'BigInteger', więc to nie zadziała. –

+0

@JohnPalmer: Ach, przepraszam, nie zdawałem sobie z tego sprawy (dosłownie nigdy nie użyłem 'for' w F # :-P). – ildjarn

Odpowiedz

8

Konwersja F # Kod z

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

do

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 trzykrotnie szybkość, a także jest bliżej do oryginalnego kodu C#.

Najbardziej prawdopodobną przyczyną jest to, że zarówno {...}, jak i for i in ... tworzą obojętne seq. Usuwając to, unikasz narzutu na seq.

EDIT

Z jakiegoś powodu F # generuje śmieszną ilość IL tego kodu, i wykorzystuje on naprawdę dziwne porównanie.

Jeśli jawnie wymusić porównania, podwaja prędkość (co jest trochę śmieszne)

Ten kod pobiera prawie dokładnie taką samą prędkością jak C# dla mnie (na mono).

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 

ale jest zbędnie verbose.

Prawdopodobnie zagram błąd kompilatora na ten temat.

+0

To dziwne, jak stwierdzono w OP wszystkie warianty 3 F # dają tu dokładnie taką samą prędkość. A może potrzebuję snu. Uruchomę je ponownie .. –

+0

@devouredelysium - Proponuję nową alternatywę, która jest znacznie szybsza - wszystkie twoje wersje zajęły około 15 na moim komputerze. –

+0

Twój algorytm ma tutaj 8,8 s. To jest rzeczywiście lepsze, ale mimo to spodziewam się, że pasuje do wersji C# :(To jest po prostu zbyt wolne –

1

Jest to najszybsza/najkrótsza wersja funkcjonalna, jaką mogłem wymyślić - oszukuje trochę za pomocą sekwencji int. Jest mniej więcej tak samo szybka jak wersja Johna Palmera na Mono.

{1..(System.Int32.MaxValue/100)} |> Seq.sumBy (fun x -> bigint(x)) |> printfn "%A" 

Ja również funkcjonalną wersję tego, co John Palmer zrobił z jednym wyjątkiem, że obejmuje wartość maksymalną w wysokości pasujące do wersji powyższej sekwencji w oparciu o:

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" 
Powiązane problemy