几乎相同的方法之间存在巨大的性能差异

问题描述 投票:0回答:2

在开发一个项目时,我无意中注意到,在启用优化的情况下,仅使用一个附加(未使用)参数的相同方法的运行速度甚至比另一个方法快十倍。

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.format
Stream.format1
之间的唯一区别只是
o
参数,而且该方法本身未使用该参数。

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

编辑:感谢您的解释,并对我的无知表示歉意。

performance optimization f#
2个回答
7
投票

问题是,当您仅使用单个参数调用

Format1
时,它只返回一个函数。它还没有进行实际的格式化。这意味着如果您比较以下性能:

Stream.Format 42
Stream.Format1 42

...那么您实际上是在比较第一种情况下实际格式化(创建数组并在其中写入内容)的性能和仅返回函数值而不执行任何操作的代码的性能。

如果您不使用

o
format1
参数进行任何操作,那么您可以只传递一些虚拟值,以实际评估函数并获得结果。那么你应该会得到类似的性能:

Stream.Format 42
Stream.Format1 42 ()

4
投票

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# 文档中有一个标题为“参数的部分应用程序”的部分值得一读:函数 (F#)

© www.soinside.com 2019 - 2024. All rights reserved.