我如何分析 F# 脚本

akr*_*hit 4 f# profiling

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

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

总的来说,我试图了解我的脚本的性能瓶颈。

附带说明一下,有没有办法将 F# 脚本编译为 exe?

Phi*_*ter 5

我建议将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
Run Code Online (Sandbox Code Playgroud)

在这种情况下,结果将表明 getNumsFaster函数分配了 0 个字节并且运行速度提高了约 33%。

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

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

无法将 F# 脚本编译为 .NET Core 的可执行文件。它仅适用于 Windows/.NET Framework,但这是被视为已弃用的旧行为。如果您希望它作为可执行文件运行,建议您将脚本中的代码转换为应用程序。