简体   繁体   English

F#脚本中的性能调整/ F#交互式

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

I need to convert an arbitrary integer (that is, bigint ) into its digits, so I can access them by index. 我需要将任意整数(即bigint )转换为其数字,以便可以通过索引访问它们。

I found myself wo(a)ndering, though, between two possible implementations of the algorithm: 但是,我发现自己在算法的两种可能的实现之间感到困惑:

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

Which one is fastest I mumbled? 我喃喃地说哪一个最快? To help me answer the question, I devised a simple profile method 为了帮助我回答这个问题,我设计了一个简单的profile方法

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

that when conflated with F# Interactive's #time yielded the following output: 与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 = ()

This clearly showed toDigit 's superiority. 这清楚地表明了toDigit的优越性。 I'd like to know why, though, so I'm asking my fellow F# overflowers what should I do from this point on. 不过,我想知道为什么,所以我要问我的F#溢出者,从现在开始我该怎么办。

In a typical Java program I'd just fire up a profiler (jvisualvm, for instance) and have it tell me which were the hot methods in some kind of CPU Sampling view. 在一个典型的Java程序中,我将启动一个探查器(例如jvisualvm),并告诉我在某种CPU采样视图中哪些是热门方法。 I guess this would work exactly the same way were I developing with .fs files in a regular project. 我想这与在常规项目中使用.fs文件进行开发的方式完全一样。 As I'm in F# Interactive I'm a bit lost. 当我进入F#Interactive时,我有些失落。 Should I attach the .NET profiler (in this case, ANTS Memory Profiler) to F# Interactive? 我是否应该将.NET探查器(在这种情况下为ANTS内存探查器)连接到F#Interactive? Is there any special workflow for this kind of software development? 这种软件开发是否有特殊的工作流程?

Perhaps this is not the answer you were looking for, but in the world of functional programming the choice of right means (algorithm, data structures, ...) for the task may bring much more benefits, than whatever thorough OOP-like micro-level code analysis a .NET profiler may give. 也许这不是您要寻找的答案,但是在函数式编程的世界中,为任务选择正确的方式(算法,数据结构等)可能会带来比其他类似OOP的彻底微操作更多的收益。 .NET探查器可能提供的高级代码分析。

To illustrate my point, in your demo cases the choice to operate with sequences in both toDigits and toDigits' functions is hard to justify. 为了说明我的观点,在您的演示案例中,很难选择同时使用toDigitstoDigits'函数中的序列进行操作。 If we choose instead to stay within array space, than equivalent functionality can be expressed as 如果我们选择留在数组空间内,则等效功能可以表示为

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

Turning now to FSI-provided profiling we can observe 现在转到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

So, we got 5.6 times speedup just due to a better choice of data structures to operate upon and FSI profiler serves just as a confirmation of this fact. 因此,由于有更好的数据结构可供选择,因此我们获得了5.6倍的加速 ,而FSI Profiler恰恰证明了这一事实。

In a typical Java program I'd just fire up a profiler (jvisualvm, for instance) and have it tell me which were the hot methods in some kind of CPU Sampling view. 在一个典型的Java程序中,我将启动一个探查器(例如jvisualvm),并告诉我在某种CPU采样视图中哪些是热门方法。 I guess this would work exactly the same way were I developing with .fs files in a regular project. 我想这与在常规项目中使用.fs文件进行开发的方式完全一样。 As I'm in F# Interactive I'm a bit lost. 当我进入F#Interactive时,我有些失落。

Using fsx files and F# Interactive doesn't mean that you should abandon compiling projects completely. 使用fsx文件和F#Interactive并不意味着您应该完全放弃编译项目。 I would change Build Action in File Properties to Compile in order to compile fsx files directly. 我将在“ File Properties Build Action中将“ Build Action更改为“ Compile ,以便直接编译fsx文件。 We need to use conditional compilation at some places eg 我们需要在某些地方使用条件编译,例如

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

The advantages of compiling the code are: 编译代码的优点是:

  • You can use Visual Studio profilers to get statistics about hot spots in programs. 您可以使用Visual Studio分析器获取有关程序中热点的统计信息。
  • You can decompile programs using ILSpy. 您可以使用ILSpy反编译程序。 Sometimes ILs or even equivalent C# code can give you insights on how the programs behave. 有时,IL或等效的C#代码可以使您深入了解程序的行为方式。

As the code evolves, you can consider moving core functions to fs files and keep quick profiling functions in fsx files. 随着代码的发展,您可以考虑将核心功能移至fs文件,并在fsx文件中保留快速分析功能。

Back to your example, an improvement of toDigits' is to avoid using references: 回到您的示例, 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

Results show that toDigits'' is 1.5x faster than toDigits' and 1.5x slower than toDigits . 结果表明, toDigits''是1.5倍的速度比toDigits'和1.5倍慢toDigits

It's hard to beat toDigit since it doesn't use any arithmetic operations on bigint . 很难击败toDigit因为它没有对bigint使用任何算术运算。 One obvious drawback of toDigit is that it gives meaningless results on negative bigint s. toDigit一个明显缺点是,它对负的bigint给出无意义的结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM