Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F#: why using outer function version faster than pass function as argument

Tags:

f#

Version (calc1) using direct outer function take about 1s.

But version (calc2) with pass function as parameter of function take about 2s, that is 2x slower. Why?

open System.Diagnostics
open System.Numerics

let width = 1920
let height = 1200
let xMin = -2.0
let xMax = 1.0
let yMin = -1.0
let yMax = 1.0
let scaleX x = float x * (xMax - xMin) / float width + xMin
let scaleY y = float y * (yMax - yMin) / float height - yMax

let fn (z:Complex) (c:Complex) = z * z + c

let calc1 width height =
    let iterFn z c =
        let rec iterFn' (z:Complex) c n =
            if z.Magnitude > 2.0 || n >= 255 then n
            else iterFn' (fn z c) c (n + 1)
        iterFn' z c 0

    Array.Parallel.init (width * height) (fun i ->
        let x, y = i % width, i / width
        let z, c = Complex.Zero, Complex(scaleX x, scaleY y)
        (x, y, iterFn z c)
    )

let calc2 width height fn =
    let iterFn z c =
        let rec iterFn' (z:Complex) c n =
            if z.Magnitude > 2.0 || n >= 255 then n
            else iterFn' (fn z c) c (n + 1)
        iterFn' z c 0

    Array.Parallel.init (width * height) (fun i ->
        let x, y = i % width, i / width
        let z, c = Complex.Zero, Complex(scaleX x, scaleY y)
        (x, y, iterFn z c)
    )

Execute in F# interactive get the following results:

> calc1 width height |> ignore
Real: 00:00:00.943, CPU: 00:00:03.046, GC gen0: 10, gen1: 8, gen2: 2
val it : unit = ()

> calc2 width height fn |> ignore
Real: 00:00:02.033, CPU: 00:00:07.484, GC gen0: 9, gen1: 8, gen2: 1
val it : unit = ()

F# 4.0.1, .NET 4.6.1

like image 968
anpv Avatar asked Nov 09 '22 22:11

anpv


1 Answers

I suspect that in the first case, the fn is inlined.

Passing it as a paramter prevents this optimisation from occuring, so it is slower

like image 156
John Palmer Avatar answered Jan 01 '23 19:01

John Palmer