2013-12-12 13 views
5

Tôi đã tự hỏi tại sao tôi nhận được kết quả khác nhau như vậy giữa các thuật toán rõ ràng bằng nhau trong C# và F #.Hiệu suất mã F # trên vòng lặp đơn giản so với C# - Tại sao?

F # mã biến thể:

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 # mã (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 code 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(); 
} 

Chiếc F # mã mất hơn 22s trên bất kỳ biến thể nào của nó (tôi giả định các cách triển khai khác nhau sẽ mang lại thời gian chạy khác nhau nhưng điều đó dường như không phải là trường hợp). Mặt khác, mã C# có vẻ nhanh hơn. Cả hai đều mang lại kết quả cuối cùng giống nhau, vì vậy tôi đoán các thuật toán là tương đương. Tôi đã kiểm tra kỹ và mã F # dường như được biên dịch với cờ --optimize+.

Tôi có làm gì sai không?

+2

Hãy thử sử dụng một 'để '' for' loop thay vì 'in'. – ildjarn

+0

@ildjarn - đối với các vòng có 'to' yêu cầu' int', nhưng ở đây chúng ta sử dụng 'BigInteger' để nó không hoạt động. –

+0

@ JohnPalmer: Ah, xin lỗi, tôi đã không nhận ra điều đó (theo nghĩa đen tôi chưa bao giờ sử dụng 'for' trong F # :-P). – ildjarn

Trả lời

8

Chuyển đổi F # mã từ

{ 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 

Approxiamately gấp ba tốc độ, và cũng là gần gũi hơn với C# mã gốc.

Lý do có nhiều khả năng nhất là cả hai {...}for i in ... tạo hình giả seq. Bằng cách xóa mục này, bạn tránh chi phí seq.

EDIT

Đối với một số lý do F # là tạo ra một số tiền vô lý của IL cho mã này, và đang sử dụng một so sánh thực sự kỳ lạ.

Nếu chúng ta rõ ràng buộc các so sánh, các nội dung đôi tốc độ (đó là một chút lố bịch)

Mã này được khá nhiều chính xác tốc độ tương tự như C# cho tôi (trên 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 

nhưng không cần thiết phải tiết lộ chi tiết.

Tôi có thể sẽ gửi lỗi trình biên dịch về vấn đề này.

+0

Điều đó thật kỳ lạ, như đã nêu trong OP tất cả 3 biến thể F # mang lại chính xác cùng tốc độ ở đây. Hoặc có lẽ tôi đang rất cần ngủ. Tôi sẽ chạy lại một lần nữa .. –

+0

@devouredelysium - Tôi đang đề xuất một giải pháp thay thế mới nhanh hơn đáng kể - tất cả các phiên bản của bạn mất khoảng 15 giây trên máy tính của tôi. –

+0

Thuật toán của bạn mất 8.8s tại đây. Nó thực sự tốt hơn, nhưng ngay cả như vậy tôi mong đợi nó để phù hợp với phiên bản C#: (Đó là chỉ là quá chậm –

1

Đây là phiên bản chức năng nhanh nhất/ngắn nhất tôi có thể đưa ra - nó cheats một chút bằng cách sử dụng một chuỗi các int. Nó nhanh bằng phiên bản của John Palmer trên Mono.

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

Tôi cũng làm một phiên bản chức năng của những gì John Palmer đã làm với một ngoại lệ ở chỗ nó bao gồm giá trị tối đa trong tổng để phù hợp với phiên bản chuỗi trên dựa:

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" 
Các vấn đề liên quan