几乎相同的方法之间的极好的性能差异

问题描述:

我在一个项目上工作时意外地注意到,只有一个额外的(未使用的)参数的同一个方法的管理运行速度比另一个快10倍,并启用了优化。几乎相同的方法之间的极好的性能差异

type Stream() = 
    static member private write (x, o, a : byte[]) = (for i = 0 to 3 do a.[o + i] <- byte((x >>> 24 - i * 8) % 256)); 4 
    static member private format f x l = Array.zeroCreate l |> fun a -> (f(x, 0, a) |> ignore; a) 
    static member private format1 f x l o = Array.zeroCreate l |> fun a -> (f(x, 0, a) |> ignore; a) 
    static member Format (value : int) = Stream.format (fun (x: int, i, a) -> Stream.write(x, i, a)) value 4 
    static member Format1 (value : int) = Stream.format1 (fun (x: int, i, a) -> Stream.write(x, i, a)) value 4 

测试时,Stream.Format1运行比Stream.Format快得多,虽然私有成员Stream.formatStream.format1之间的唯一区别只是o说法,而且这是方法本身使用。

编译器如何以不同的方式处理两个几乎相同的方法?

编辑:感谢您的解释和抱歉的无知。

+0

多少次迭代并运行,以获得时序?一?一百万?您需要使您的示例足够宽泛,以平滑缓存效果,内核调度等。 – spraff 2012-01-12 16:03:50

问题是,当您只使用一个参数调用Format1时,它只会返回一个函数。它并没有执行实际的格式。这意味着,如果你比较的性能:

Stream.Format 42 
Stream.Format1 42 

...那么你实际上是比较实际的格式的性能(即创建数组和写入的东西),在第一种情况和性能代码只是简单地返回一个函数值而不做任何事情。

如果你不使用format1o参数作为任何事情,那么你可以传递一些虚拟值来实际评估函数并得到结果。那么你应该得到类似的性能:

Stream.Format 42 
Stream.Format1 42() 

Format实际上调用Array.zeroCreate l |> fun a -> (f(x, 0, a) |> ignore; a)

Format1返回一个函数,当传递一个对象时调用Array.zeroCreate l |> fun a -> (f(x, 0, a) |> ignore; a)

即,一方做实际工作,另一方只是部分功能应用;后者明显更快。

如果你不熟悉的部分功能的应用,有一个标题为“参数的部分应用程序”这是值得一读了在F#文档的一部分:Functions (F#)