[英]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探查器可能提供的高級代碼分析。
為了說明我的觀點,在您的演示案例中,很難選擇同時使用toDigits
和toDigits'
函數中的序列進行操作。 如果我們選擇留在數組空間內,則等效功能可以表示為
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
編譯代碼的優點是:
隨着代碼的發展,您可以考慮將核心功能移至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.