[英]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.