2014-07-05 24 views
12

Một trong những điều tôi yêu thích về F # là một từ khóa thực sự inline. Tuy nhiên, mặc dù nó cho phép viết các hàm thứ tự đầu tiên thực hiện giống như các khối mã được dán, nhưng mọi thứ không phải là màu hồng cho các hàm bậc cao hơn. Cân nhắcTại sao không thể trình biên dịch hàm F inline đầy đủ chức năng của hàm bậc cao hơn?

let inline add i = i+1 
let inline check i = if (add i) = 0 then printfn ""  
let inline iter runs f = for i = 0 to runs-1 do f i 
let runs = 100000000 
time(fun()->iter runs check) 1 
time(fun()->for i = 0 to runs-1 do check i) 1 

Kết quả là 244 ms cho iter61 ms cho kiểm tra bằng tay. Hãy nghiên cứu sâu vào ILSpy. Chức năng liên quan được gọi cho cuộc gọi trực tiếp là:

internal static void [email protected](Microsoft.FSharp.Core.Unit unitVar0) 
{ 
    for (int i = 0; i < 100000000; i++) 
    { 
     if (i + 1 == 0) 
     { 
      Microsoft.FSharp.Core.PrintfFormat<Microsoft.FSharp.Core.Unit, System.IO.TextWriter, Microsoft.FSharp.Core.Unit, Microsoft.FSharp.Core.Unit> format = new Microsoft.FSharp.Core.PrintfFormat<Microsoft.FSharp.Core.Unit, System.IO.TextWriter, Microsoft.FSharp.Core.Unit, Microsoft.FSharp.Core.Unit, Microsoft.FSharp.Core.Unit>(""); 
      Microsoft.FSharp.Core.PrintfModule.PrintFormatLineToTextWriter<Microsoft.FSharp.Core.Unit>(System.Console.Out, format); 
     } 
    } 
} 

Với add được gạch chân. Các chức năng có liên quan để iter

internal static void [email protected](Microsoft.FSharp.Core.Unit unitVar0) 
{ 
    for (int i = 0; i < 100000000; i++) 
    { 
     [email protected](i); 
    } 
} 
internal static void [email protected](int i) 
{ 
    if (i + 1 == 0) 
    { 
     Microsoft.FSharp.Core.PrintfFormat<Microsoft.FSharp.Core.Unit, System.IO.TextWriter, Microsoft.FSharp.Core.Unit, Microsoft.FSharp.Core.Unit> format = new Microsoft.FSharp.Core.PrintfFormat<Microsoft.FSharp.Core.Unit, System.IO.TextWriter, Microsoft.FSharp.Core.Unit, Microsoft.FSharp.Core.Unit, Microsoft.FSharp.Core.Unit>(""); 
     Microsoft.FSharp.Core.PrintfModule.PrintFormatLineToTextWriter<Microsoft.FSharp.Core.Unit>(System.Console.Out, format); 
     return; 
    } 
} 

Và chúng ta có thể thấy các hình phạt hiệu quả xuất phát từ một cấp thêm về mình. Như kiểm tra hiệu năng cho thấy, sự chuyển hướng này cũng không bị loại bỏ bởi trình biên dịch JIT. Có lý do gì khiến tại sao các hàm bậc cao không thể hoàn toàn được nội tuyến? Đây là một nỗi đau khi viết một hạt nhân tính toán.

thời gian combinator của tôi (mặc dù không thực sự liên quan ở đây) là

let inline time func n = 
    func() |> ignore 
    GC.Collect() 
    GC.WaitForPendingFinalizers() 
    let stopwatch = Stopwatch.StartNew() 
    for i = 0 to n-1 do func() |> ignore 
    stopwatch.Stop() 
    printfn "Took %A ms" stopwatch.Elapsed.TotalMilliseconds 
+0

Vui lòng xác nhận rằng bạn đã chạy chương trình này ở chế độ Phát hành mà không đính kèm trình gỡ rối. Ngoài ra, điểm chuẩn có vẻ hợp lệ. Bạn có thể cải thiện nó bằng cách tăng công việc lên 10 lần để tránh ảnh hưởng của chi phí một lần. – usr

+0

@usr Có, tôi đã chạy nó mà không cần trình sửa lỗi và được biên dịch trong chế độ Phát hành. Có thể có không có nghi ngờ sự khác biệt hiệu suất là thực sự mặc dù bởi vì nó có thể được suy ra từ mã IL (chặn tối ưu hóa JIT). – Arbil

+0

@Arbil Tôi đã liên kết với câu hỏi này trên một trong các chủ đề UserVoice Thiết kế Ngôn ngữ F # về phân tích nội tuyến: https://fslang.uservoice.com/forums/245727-f-language/suggestions/6137978-better-inlining-analysis -and-heuristic-algorithm –

Trả lời

6

Chỉ cần được rõ ràng, F # biên dịch được nội tuyến mỗi định nghĩa mà bạn đã đánh dấu là inline. Nó chỉ là hành vi hiện tại của nội tuyến không phải là rất hữu ích khi sử dụng một chức năng nội tuyến như là một đối số bậc cao hơn. check chỉ có thể được gạch chân khi đưa ra một đối số, và vì vậy iter runs check được coi là iter runs (fun i -> check i). Sau đó check được inlined, kết quả là tương đương với

iter runs (fun i -> if (add i) = 0 then printfn "") 

(như bạn có thể nhìn thấy trong IL, không có cuộc gọi đến check trong IL được tạo ra, nhưng có một cuộc gọi đến [email protected] cơ thể tổng hợp cho lambda này , tương đương). iter cũng được inline. Có nói rằng, tôi đồng ý rằng hành vi hiện tại không phải là hữu ích vì nó có thể được - trình biên dịch cũng có thể nội tuyến của cơ thể lambda vào trang web cuộc gọi, mà sẽ được an toàn và cải thiện hiệu suất.

+4

Nói đúng là vấn đề không phải là 'kiểm tra' không được gạch chân nhưng một hàm không được gạch chân bắt nguồn từ 'kiểm tra'. Điều này tuy nhiên theo như tôi có thể nói là không đặc biệt với ví dụ của tôi nhưng xảy ra cho tất cả các cuộc gọi hàm cao hơn.Do đó hiệu suất-khôn ngoan nó là giống hệt với các đối số chức năng không được inlined. Tại sao chúng ta không (những người quan tâm đến phát triển hiệu suất cao/khoa học/trò chơi với F #) thúc đẩy một số vấn đề này được khắc phục? Số 1 là cấu trúc tuples, đây là không. 2. Hiện tại trên fslang có rất ít đề xuất liên quan đến hiệu suất bình chọn. – Arbil

+1

Tôi sẽ rất vui khi được tham gia một buổi biểu diễn tuyệt vời với bạn. Ngôn ngữ cấp cao là tuyệt vời nhưng thái độ của "hiệu suất không quan trọng" dẫn đến một chu kỳ chống đạo đức đến mức nó không quan trọng. – jackmott

Các vấn đề liên quan