繁体   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