我如何配置F#脚本

时间:2019-07-19 17:02:10

标签: f# profiling

我正在通过使用F#脚本自动化一些任务来学习F#。我从命令行使用“ fsi / fsarpi --exec”运行此脚本。我正在使用.Net core进行工作。我要寻找的一件事是如何分析我的F#脚本。我主要是在寻找

  1. 查看我的整个脚本消耗的总时间,我尝试使用秒表进行某种功能,并且效果很好。有什么可以显示我各种顶级函数调用时间的吗?或函数调用的时间/计数。
  2. 查看我的脚本的总体内存消耗。
  3. 脚本中的热点。

总体而言,我试图了解我的脚本的性能瓶颈。

另一方面,有没有办法将F#脚本编译为exe?

1 个答案:

答案 0 :(得分:4)

我建议将BenchmarkDotNet用于所有基准测试任务(嗯,微基准测试)。由于它是一种统计工具,因此它可以解决手动基准测试无法解决的许多问题。只需应用一些属性,您就可以得到一个漂亮的报告。

创建一个.NET Core控制台应用程序,添加BenchmarkDotNet程序包,创建一个基准,然后运行它以查看结果。这是一个示例,测试两个简单的解析函数,其中一个作为比较基准,并通知BenchmarkDotNet在运行基准测试时捕获内存使用情况统计信息:

open System
open BenchmarkDotNet.Attributes
open BenchmarkDotNet.Running

module Parsing =
    /// "123,456" --> (123, 456)
    let getNums (str: string) (delim: char) =
        let idx = str.IndexOf(delim)
        let first = Int32.Parse(str.Substring(0, idx))
        let second = Int32.Parse(str.Substring(idx + 1))
        first, second

    /// "123,456" --> (123, 456)
    let getNumsFaster (str: string) (delim: char) =
        let sp = str.AsSpan()
        let idx = sp.IndexOf(delim)
        let first = Int32.Parse(sp.Slice(0, idx))
        let second = Int32.Parse(sp.Slice(idx + 1))
        struct(first, second)

[<MemoryDiagnoser>]
type ParsingBench() =
    let str = "123,456"
    let delim = ','

    [<Benchmark(Baseline=true)>]
    member __.GetNums() =
        Parsing.getNums str delim |> ignore

    [<Benchmark>]
    member __.GetNumsFaster() =
        Parsing.getNumsSpan str delim |> ignore

[<EntryPoint>]
let main _ =
    let summary = BenchmarkRunner.Run<ParsingBench>()
    printfn "%A" summary

    0 // return an integer exit code

在这种情况下,结果将显示getNumsFaster函数分配0字节并以大约33%的速度运行。

一旦发现性能一致且分配较少的东西,就可以将其转移到实际执行代码的脚本或其他环境中。

对于热点,最好的工具是在PerfView之类的探查器下实际运行脚本,并在脚本执行时查看CPU时间和由脚本引起的分配。这里没有简单的答案:正确解释性能分析结果是一项艰巨且耗时的工作。

无法将F#脚本编译为.NET Core的可执行文件。仅在Windows / .NET Framework上才有可能,但这是不推荐使用的旧行为。如果您希望将脚本中的代码作为可执行文件运行,建议将其转换为应用程序。