9

这是 2 个函数,fun1需要 1 个参数,fun2需要 4 个额外的无用参数。当我针对 x64 时,fun1需要 4 秒但fun2不到 1 秒。如果我以anycpu为目标,那么两者都需要不到1s。

我在这里问了一个类似的问题 ,如果目标是 x64,为什么 Seq.iter 比 for 循环快 2 倍?

它在 .Net 4.5 Visual Studio 2012、F# 3.0 中编译,在 windows 7 x64 中运行

open System
open System.Diagnostics

type Position =
    {
        a: int
        b: int
    }

[<EntryPoint>]
let main argv = 

    let fun1 (pos: Position[]) =  //<<<<<<<< here
        let functionB x y z = 4

        Array.fold2 (fun acc x y -> acc + int64 (functionB x x y)) 0L pos pos

    let fun2 (pos: Position[]) u v w x =  //<<<<<<<< here
        let functionB x y z = 4

        Array.fold2 (fun acc x y -> acc + int64 (functionB x x y)) 0L pos pos



    let s = {a=2;b=3}
    let pool = [|s;s;s|]

    let test1 n =
        let mutable x = 0L
        for i in 1 .. n do
            x <- fun1 pool

    let test2 n =
        let mutable x = 0L
        for i in 1 .. n do
            x <- fun2 pool 1 2 3 4

    let sw = new Stopwatch()
    sw.Start()
    test2 10000000
    sw.Stop()
    Console.WriteLine(sw.Elapsed)

    sw.Restart()
    test1 10000000
    sw.Stop()
    Console.WriteLine(sw.Elapsed)


    0 // return an integer exit code
4

2 回答 2

2

这不是一个完整的答案,它是问题的第一个诊断。

我可以使用相同的配置重现该行为。如果您在 中打开 F# Interactive 64-bit Tools -> Options -> F# Tools -> F# Interactive,您可以在那里观察到相同的行为。

另一个问题不同,x64 抖动不是问题。事实证明,Project 属性中的“生成尾调用”选项导致test1test2. 如果您关闭该选项,则两种情况的速度相似。

另一方面,您可以使用inline关键字 onfun1以便不需要尾调用。无论fun2是否内联,两个示例在执行时间上再次具有可比性。

也就是说,添加tail.操作码fun1使其比 (does the same with) 慢得多,这很奇怪fun2。您可以联系 F# 团队进行进一步调查。

于 2012-11-08T14:01:57.513 回答
0

这种差异几乎可以肯定是 JITer 的一个怪癖。它还解释了不一致的结果。这是像这样的微基准测试的常见问题。执行一个或多个方法的冗余执行,以便在幕后编译整个事情,并为最后一个计时。它们将是相同的。

由于这个怪癖,你可以获得比这更奇怪的结果。

于 2012-10-07T08:27:37.010 回答