# Passing Function As Object VS Creating New Function

**URL:** <https://discourse.julialang.org/t/passing-function-as-object-vs-creating-new-function/66933>\
**Category:** Performance\
**Created:** [August 24, 2021, 6:32pm UTC](https://discourse.julialang.org/t/passing-function-as-object-vs-creating-new-function/66933 "2021-08-24T18:32:26Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![cmdenis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cmdenis/32/211517_2.png) [@cmdenis](https://discourse.julialang.org/u/cmdenis)\
**Post date:** [August 24, 2021, 6:32pm UTC](https://discourse.julialang.org/t/passing-function-as-object-vs-creating-new-function/66933/1 "2021-08-24T18:32:26Z")

</div>

Hello!

So, I’ve stumbled upon a performance difference between two situations, and I don’t understand the reason behind this difference. The difference in computation time occurs between 1) passing a function to a variable and 2) using a function as part of another function.

Here is what I mean:

**Scenario 1**  
I create a function `f`, and then pass it to another variable `g`.

```julia
f(x) = x^2
g = f

```

When I evaluate g, for a certain value, I will get the following computation time data:

```julia
julia> @benchmark g(2.1)
BenchmarkTools.Trial: 10000 samples with 995 evaluations.
 Range (min … max): 23.920 ns … 581.970 ns ┊ GC (min … max): 0.00% … 91.54%
 Time (median): 25.078 ns ┊ GC (median): 0.00%
 Time (mean ± σ): 26.954 ns ± 9.373 ns ┊ GC (mean ± σ): 0.50% ± 1.57%

  ▅█▆▅▇▆▄▂ ▂▁▁▃▅▄▂▁▁▂▂▃▃▃▃▂▁ ▂
  █████████▆▄▄▄▄▄▄▅▅███████████████████▇▆▄▆▃▄▃▄▃▃▂▂▄▄▅▅▅▆▅▅▄▃▄ █
  23.9 ns Histogram: log(frequency) by time 39.4 ns <

 Memory estimate: 16 bytes, allocs estimate: 1.

```

**Scenario 2**  
I create a function `f`, and then use it in the definition of a function `h`.

```julia
f(x) = x^2
h(x) = f(x)

```

When I evaluate h, for a certain value, I will get the following computation time data:

```julia
julia> @benchmark h(2.1)
BenchmarkTools.Trial: 10000 samples with 1000 evaluations.
 Range (min … max): 0.051 ns … 0.097 ns ┊ GC (min … max): 0.00% … 0.00%
 Time (median): 0.056 ns ┊ GC (median): 0.00%
 Time (mean ± σ): 0.059 ns ± 0.006 ns ┊ GC (mean ± σ): 0.00% ± 0.00%

         ▆ ▃ ▆ █     
  ▃▁▁▇▁▁▁█▁▁█▁▁▁█▁▁▅▁▁▁▂▁▁▂▁▁▁▂▁▁▁▁▁▁▁▁▁▁▂▁▁▂▁▁▁▅▁▁█▁▁▁█▁▁▆ ▃
  051 ns Histogram: frequency by time 067 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.

```

Why is there such an important computation time difference between these two protocols?

Thank you!

---

<div class="post-metadata">

**Author:** ![genkuroki](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/genkuroki/32/18030_2.png) [@genkuroki](https://discourse.julialang.org/u/genkuroki)\
**Post date:** [August 24, 2021, 6:57pm UTC](https://discourse.julialang.org/t/passing-function-as-object-vs-creating-new-function/66933/2 "2021-08-24T18:57:26Z")

</div>

`f` and `h` are functions. `g` is not a function but a global variable. This is the cause of the performance problem of `g(2.1)`.

---

<div class="post-metadata">

**Author:** ![lungben](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lungben/32/12314_2.png) [@lungben](https://discourse.julialang.org/u/lungben)\
**Post date:** [August 24, 2021, 7:42pm UTC](https://discourse.julialang.org/t/passing-function-as-object-vs-creating-new-function/66933/3 "2021-08-24T19:42:31Z")

</div>

You could try

```julia
const j = f
@benchmark j(2.1) # as fast as f(2.1)

k(x, f) = f(x)
@benchmark k(2.1, f) # as fast as f(2.1)

```

Thus, the bad performance of `g` comes from its type instability as a global variable.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [August 24, 2021, 8:51pm UTC](https://discourse.julialang.org/t/passing-function-as-object-vs-creating-new-function/66933/4 "2021-08-24T20:51:21Z")

</div>

0.05ns is not a real execution time. There is no function that executes that quickly. This is a benchmarking artefact.

---

<div class="post-metadata">

**Author:** ![genkuroki](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/genkuroki/32/18030_2.png) [@genkuroki](https://discourse.julialang.org/u/genkuroki)\
**Post date:** [August 25, 2021, 1:02pm UTC](https://discourse.julialang.org/t/passing-function-as-object-vs-creating-new-function/66933/5 "2021-08-25T13:02:30Z")

</div>

```julia
using BenchmarkTools

f(x) = x^2
g = f
const g_const = f

F(x) = f(x)
G(x) = g(x)
G_const(x) = g_const(x)

@btime f(2.1)
@btime F(2.1)
@btime G(2.1)
@btime G_const(2.1)
println()
@btime g(2.1)
@btime $g(2.1)
println()
@btime F($(Ref(2.1))[])
@btime G($(Ref(2.1))[])
@btime G_const($(Ref(2.1))[]);

```

Result:

```julia
  0.001 ns (0 allocations: 0 bytes)
  0.001 ns (0 allocations: 0 bytes)
  23.139 ns (2 allocations: 32 bytes)
  0.001 ns (0 allocations: 0 bytes)

  23.494 ns (1 allocation: 16 bytes)
  0.001 ns (0 allocations: 0 bytes)

  1.400 ns (0 allocations: 0 bytes)
  24.648 ns (2 allocations: 32 bytes)
  1.400 ns (0 allocations: 0 bytes)

```

The reason for the slowdown to about 23 ns is that `g` is a global variable, not the function `f` itself. The slowdown can be avoided by using `const`.

The cause of 0.001 ns is to “cheat” the benchmark by hoisting the calculation out of the benchmark code. I have used `$(Ref (2.1))[]` to avoid the “cheating”. See [GitHub - JuliaCI/BenchmarkTools.jl: A benchmarking framework for the Julia language](https://github.com/JuliaCI/BenchmarkTools.jl)

---

<div class="post-metadata">

**Author:** ![cmdenis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cmdenis/32/211517_2.png) [@cmdenis](https://discourse.julialang.org/u/cmdenis)\
**Post date:** [August 26, 2021, 1:56pm UTC](https://discourse.julialang.org/t/passing-function-as-object-vs-creating-new-function/66933/6 "2021-08-26T13:56:48Z")

</div>

Alright! Thanks everyone, that makes a lot of sense!
