簡體   English   中英

F#腳本中的性能調整/ F#交互式

[英]Performance tuning in F# scripts / F# interactive

我需要將任意整數(即bigint )轉換為其數字,以便可以通過索引訪問它們。

但是,我發現自己在算法的兩種可能的實現之間感到困惑:

open System

let toDigits (x:bigint) =
    x.ToString() 
    |> Seq.map (fun c -> (int c) - (int '0'))
    |> Seq.toArray

let toDigits' (x:bigint) =
    seq {
        let x' = ref x
        while !x' <> 0I do
            yield (int (!x' % 10I))
            x' := !x' / 10I
    } |> Seq.toArray |> Seq.rev

我喃喃地說哪一個最快? 為了幫助我回答這個問題,我設計了一個簡單的profile方法

let profile f times = 
    let x = ref 0
    while !x < times do
        incr x
        f (bigint !x) |> ignore

與F#Interactive的#time混合時,將產生以下輸出:

> profile toDigits 10000000;;
Real: 00:00:11.609, CPU: 00:00:11.606, GC gen0: 825, gen1: 1, gen2: 0
val it : unit = ()
> profile toDigits' 10000000;;
Real: 00:00:28.891, CPU: 00:00:28.844, GC gen0: 1639, gen1: 3, gen2: 0
val it : unit = ()

這清楚地表明了toDigit的優越性。 不過,我想知道為什么,所以我要問我的F#溢出者,從現在開始我該怎么辦。

在一個典型的Java程序中,我將啟動一個探查器(例如jvisualvm),並告訴我在某種CPU采樣視圖中哪些是熱門方法。 我想這與在常規項目中使用.fs文件進行開發的方式完全一樣。 當我進入F#Interactive時,我有些失落。 我是否應該將.NET探查器(在這種情況下為ANTS內存探查器)連接到F#Interactive? 這種軟件開發是否有特殊的工作流程?

也許這不是您要尋找的答案,但是在函數式編程的世界中,為任務選擇正確的方式(算法,數據結構等)可能會帶來比其他類似OOP的徹底微操作更多的收益。 .NET探查器可能提供的高級代碼分析。

為了說明我的觀點,在您的演示案例中,很難選擇同時使用toDigitstoDigits'函數中的序列進行操作。 如果我們選擇留在數組空間內,則等效功能可以表示為

let toDigits'' (x: bigint) =
    x.ToString().ToCharArray() |> Array.map (fun c -> int(c) - int('0'))

現在轉到FSI提供的配置文件,我們可以觀察到

> profile toDigits 10000000;;
Real: 00:00:13.020, CPU: 00:00:13.000, GC gen0: 1649, gen1: 2, gen2: 0

> profile toDigits'' 10000000;;
Real: 00:00:02.334, CPU: 00:00:02.343, GC gen0: 604, gen1: 1, gen2: 0

因此,由於有更好的數據結構可供選擇,因此我們獲得了5.6倍的加速 ,而FSI Profiler恰恰證明了這一事實。

在一個典型的Java程序中,我將啟動一個探查器(例如jvisualvm),並告訴我在某種CPU采樣視圖中哪些是熱門方法。 我想這與在常規項目中使用.fs文件進行開發的方式完全一樣。 當我進入F#Interactive時,我有些失落。

使用fsx文件和F#Interactive並不意味着您應該完全放棄編譯項目。 我將在“ File Properties Build Action中將“ Build Action更改為“ Compile ,以便直接編譯fsx文件。 我們需要在某些地方使用條件編譯,例如

#if INTERACTIVE
#time "on";;
#endif

編譯代碼的優點是:

  • 您可以使用Visual Studio分析器獲取有關程序中熱點的統計信息。
  • 您可以使用ILSpy反編譯程序。 有時,IL或等效的C#代碼可以使您深入了解程序的行為方式。

隨着代碼的發展,您可以考慮將核心功能移至fs文件,並在fsx文件中保留快速分析功能。

回到您的示例, toDigits'的改進是避免使用引用:

let toDigits'' (x:bigint) =
    let rec loop x acc =
        if x <> 0I then
            loop (x/10I) (int(x%10I)::acc)
        else acc
    loop x [] |> List.toArray

結果表明, toDigits''是1.5倍的速度比toDigits'和1.5倍慢toDigits

很難擊敗toDigit因為它沒有對bigint使用任何算術運算。 toDigit一個明顯缺點是,它對負的bigint給出無意義的結果。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM