4

有这样的功能吗?类似 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()

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

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

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

4

3 回答 3

4

是的,CLR 进行了一些运行时优化,如本博客所示。请注意,根据这篇文章,虚拟方法没有内联。

这些方法都没有内联:

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

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

于 2012-05-04T19:13:59.163 回答
1

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

我的高度非科学测试表明,您提供的性能测试在Debugifinline添加到printableByte.

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

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

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

于 2012-05-04T21:20:20.853 回答
0

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

于 2012-05-04T18:24:38.003 回答