dev*_*ium 2 .net performance f# profiling 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
Run Code Online (Sandbox Code Playgroud)
我嘀咕哪一个最快?为了帮助我回答这个问题,我设计了一个简单的profile方法
let profile f times =
let x = ref 0
while !x < times do
incr x
f (bigint !x) |> ignore
Run Code Online (Sandbox Code Playgroud)
与 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 = ()
Run Code Online (Sandbox Code Playgroud)
这清楚地表明了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'))
Run Code Online (Sandbox Code Playgroud)
现在转向 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
Run Code Online (Sandbox Code Playgroud)
因此,由于更好地选择了要操作的数据结构,我们获得了5.6 倍的加速,而 FSI 分析器恰好证实了这一事实。