while working on a project I accidentally noticed that the same method with only one additional (unused) argument manages to run even ten times faster than the other one, with optimizations enabled.
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
When tested, Stream.Format1 runs much faster than Stream.Format, although the only difference between the private members Stream.format and Stream.format1 is just the o argument, which moreover is unused by the method itself.
How does the compiler treat in so different ways two almost identical methods?
EDIT: thanks for the explanation and sorry for the ignorance.
The problem is that when you call
Format1with just a single argument, it only returns a function. It doesn’t do the actual formatting yet. This means that if you compare the performance of:… then you’re actually comparing the performance of actual formatting (that creates the array and writes something in it) in the first case and the performance of code that simply returns a function value without doing anything.
If you’re not using the
oparameter offormat1for anything, then you can just pass in some dummy value, to actually evaluate the function and get the result. Then you should get similar performance: