【问题标题】:Does the Microsoft's .net CLR inline small functions at run-time?Microsoft 的 .net CLR 是否在运行时内联小功能?
【发布时间】:2012-05-04 18:22:41
【问题描述】:

这样的功能存在吗?类似 Java HotSpot 的东西在服务器模式下运行,但用于 .Net 应用程序。

编辑: 更多信息。我有一个小应用程序(用 F# 编写),里面有很多小功能。喜欢这个:

let printable b =
    if b >= ' 'B && b <= '~'B
    then b else '.'B

我意识到性能很差,在分析后我看到每个这样的函数都被调用了数百万次。我制作了它们inline 并获得了性能提升(5 倍以上,可能更多)。

好的,很好。现在性能很好。但是为什么框架不这样做呢?它有足够的关于我的代码和函数被调用频率的信息。为什么不内联一个被调用了 1M 次的函数?

EDIT2: 测量内联函数差异的示例测试:

    open System

    let printableByte b =
        if b >= ' 'B && b <= '~'B
        then b else '.'B

    let foo (arr : byte[]) = 
        for i in 0..arr.Length-1 do
            arr.[i] <- printableByte (arr.[i])
        arr.Length / 1000

    let main() =
        let sum = ref 0
        let arr = Array.create 1000000 0uy

        let stopWatch = System.Diagnostics.Stopwatch()
        stopWatch.Start()
        for x in 0..5000 do
            sum := !sum + (foo arr)

        stopWatch.Stop()
        printfn "%d" !sum 
        printfn "total time = %A" stopWatch.ElapsedMilliseconds
        ()

    main()

printableByte 未内联时运行 19.5 秒,内联时运行 13.6 秒。

EDIT3: 只有为 x86 目标编译并在 x64 主机上运行时,才能查看此时间差。如果编译为“anycpu”或x64,则没有时间差异。

因此,“小功能”和优化没有任何问题。

【问题讨论】:

  • 是的,这就是 CLR 的 JIT 所做的。
  • @HansPassantI 我添加了更多信息,问题与调试/发布模式无关。
  • 问题的标题歪曲了实际问题。对标题的有效回答是“是的,.net 在运行时做了 一些 优化”。这实际上不会回答你的问题。也许您应该改写为“Microsoft 的 .net CLR 是否在运行时内联小函数?”
  • @Mystere : server HotSpot 确实基于“热”代码路径(因此命名)进行自适应运行时优化,即更频繁执行的代码路径或分支可以使用不同或更激进的优化重新 JIT 编译。

标签: .net optimization f#


【解决方案1】:

是的,CLR 做了一些运行时优化,正如blog 所示。 请注意,根据这篇文章,虚方法是内联的。

这些方法都没有被内联:

  • 递归方法
  • 虚方法(即使接收者变量的静态类型是密封的)

您的printable 函数是如何在您的代码中调用的?如果 F# 编译器将它包装在一个闭包中,它经常这样做,即使在您一开始可能没有预料到的情况下,您也会陷入“虚拟方法”的情况。

【讨论】:

  • 当前版本在这里:github.com/qehgt/fsharp-netproxy/blob/… 以前的版本没有inline 关键字。
  • printableByte 在您的代码中是一个嵌套函数。这些有时被实现为闭包。不过,启用优化的发布版本应该会提升那些不会从包含函数中捕获任何内容的嵌套函数。我有点惊讶它没有在你的情况下这样做。
  • @Jon,我稍后会仔细检查。
  • @Jon,我更新了主题。看起来 F# 编译器不想内联没有 inline 关键字的函数。
【解决方案2】:

请记住,在 FSI 中测试此代码与在 Release 模式下编译项目不同

我的高度非科学测试表明,如果将inline 添加到printableByte,您提供的性能测试在Debug 中运行得更好。

但是,当在Release 模式下添加inline 时,程序实际上比没有它时执行更糟。我相信 F# 编译器团队或一些反汇编程序可以告诉你为什么会这样......

根据我使用 F# 的经验,您应该很少必须手动应用内联优化。只要确保你在Release 编译!

编辑:啊哈,是的!确保在“任何 CPU”模式下编译,除非您有特定的理由不这样做(通常我的原因是必须与 F# 中的 x86 COM 库进行互操作)

【讨论】:

  • 不,一切都在 Windows 上的 Release 中完成(或在 Linux 上使用 -O)。我没有使用 Fsi。
  • 在此处发布时没有内联:平均 32 秒。内联:平均 35 秒。 (很明显我的机器比你的旧,但我不知道什么可以解释这种倒置。)
【解决方案3】:

是的,.net 框架针对运行它的平台优化了代码。它考虑了许多因素。不过,有些事情它不会做。例如,如果 SIMD 指令可用,我不相信它会使用它们。但总的来说,它确实在优化方面做得很好。

【讨论】:

  • @ildjarn - 我认为它仍然有意义。编译器做了一些运行时优化,编辑并没有改变这个事实。 Joh 的回答有更多信息,所以我没有理由加强我的。
  • 不是真的 - OP 不是在询问特定于平台的优化,而是在询问自适应运行时优化。
  • @ildjarn - 这个问题刚刚被细化,它仍在询问运行时优化,我的回答仍然有效。仅仅因为他添加了额外的问题并不意味着它是无效的。
  • 我没有否决您的回答,无需防御。如果您不知道 OP 的编辑并想更新您的答案,我只是首先说了一些话。
猜你喜欢
  • 2010-09-14
  • 1970-01-01
  • 1970-01-01
  • 2021-08-31
  • 2018-01-29
  • 1970-01-01
  • 2020-05-16
  • 2011-02-01
  • 2010-10-12
相关资源
最近更新 更多