# Memory allocation with function passed as arguments

**URL:** <https://discourse.julialang.org/t/memory-allocation-with-function-passed-as-arguments/64575>\
**Category:** Performance\
**Created:** [July 13, 2021, 2:44pm UTC](https://discourse.julialang.org/t/memory-allocation-with-function-passed-as-arguments/64575 "2021-07-13T14:44:32Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![aaraujo71](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aaraujo71/32/10635_2.png) [@aaraujo71](https://discourse.julialang.org/u/aaraujo71)\
**Post date:** [July 13, 2021, 2:44pm UTC](https://discourse.julialang.org/t/memory-allocation-with-function-passed-as-arguments/64575/1 "2021-07-13T14:44:32Z")

</div>

When I call `test()` twice after compiling code

```julia
fn = (x -> 2*exp(x), x -> 2*sin(x))

function test_calc(x, fn1, fn2)
    @. x = x + fn1(x) + fn2(x)
end

function test()
    A = Float64.(collect(1:10))
    @time test_calc(A,x -> 2*exp(x), x -> 2*sin(x))
    fn1, fn2 = fn
    A = Float64.(collect(1:10))
    @time test_calc(A, fn1, fn2)
    A = Float64.(collect(1:10))
    @time test_calc(A, fn...)
    return nothing
end

```

I get

```julia
julia> test()
  0.055331 seconds (3.96 k allocations: 209.977 KiB, 99.92% compilation time)
  0.121727 seconds (251.28 k allocations: 14.448 MiB, 99.14% compilation time)
  0.000023 seconds (7 allocations: 144 bytes)

julia> test()
  0.000018 seconds (6 allocations: 128 bytes)
  0.000085 seconds (6 allocations: 128 bytes)
  0.000019 seconds (7 allocations: 144 bytes)

```

- Is there any way to pass functions as arguments without allocating memory (this is quite important to the code I’m developing)?
- Why do the 1st two calculation methods allocate more memory after being compiled (after the 1st `test()` call)?
- Why is the 3rd calculation method allocating more memory than the other two after the 2nd `test()` call?

---

<div class="post-metadata">

**Author:** ![anon56330260](https://avatars.discourse-cdn.com/v4/letter/a/f07891/32.png) [@anon56330260](https://discourse.julialang.org/u/anon56330260)\
**Post date:** [July 13, 2021, 3:36pm UTC](https://discourse.julialang.org/t/memory-allocation-with-function-passed-as-arguments/64575/2 "2021-07-13T15:36:50Z")

</div>

The solution is rather simple: add `@inline` to `test_calc`. Then allocation is gone.

```julia
fn = (x -> 2*exp(x), x -> 2*sin(x))

@inline function test_calc(x, fn1, fn2)
    @. x = x + fn1(x) + fn2(x)
end

function test()
    A = Float64.(collect(1:10))
    @time test_calc(A,x -> 2*exp(x), x -> 2*sin(x))
    fn1, fn2 = fn
    A = Float64.(collect(1:10))
    @time test_calc(A, fn1, fn2)
    A = Float64.(collect(1:10))
    @time test_calc(A, fn...)
    return nothing
end

```

Run `test()` twice time, you get:

```julia
julia> test()
  0.000006 seconds
  0.000003 seconds
  0.000003 seconds

```

> [@aaraujo71](#):
>
> Why is the 3rd calculation method allocating more memory than the other two after the 2nd `test()` call?

I haven’t observe that case in Julia 1.6 and 1.8. They both have 6 allocations (without `@inline`):

```julia
julia> test()
  0.000036 seconds (6 allocations: 128 bytes)
  0.000026 seconds (6 allocations: 128 bytes)
  0.000040 seconds (6 allocations: 128 bytes)

```

Also, if your anonymous function is complicated, you can add `@inline` to them to force inlining the function. like `@inline(x -> 2*exp(x))`.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [July 13, 2021, 4:33pm UTC](https://discourse.julialang.org/t/memory-allocation-with-function-passed-as-arguments/64575/3 "2021-07-13T16:33:35Z")

</div>

Note that in general I would use `@btime`, and then just interpolate global variables with `$`:

```julia
using BenchmarkTools
begin
    fn = (x -> 2*exp(x), x -> 2*sin(x))
    @inline test_calc(x, fn1, fn2) = @. x = x + fn1(x) + fn2(x)
    @btime test_calc(A, $(x -> 2*exp(x)), $(x -> 2*sin(x))) setup=(A = Float64.(collect(1:10))) evals=1
    fn1, fn2 = fn
    @btime test_calc(A, $fn1, $fn2) setup=(A = Float64.(collect(1:10))) evals=1
    @btime test_calc(A, $fn...) setup=(A = Float64.(collect(1:10))) evals=1
end;

```

which gives

```julia
  238.000 ns (0 allocations: 0 bytes)
  239.000 ns (0 allocations: 0 bytes)
  238.000 ns (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [July 13, 2021, 4:36pm UTC](https://discourse.julialang.org/t/memory-allocation-with-function-passed-as-arguments/64575/4 "2021-07-13T16:36:50Z")

</div>

Note also that you can also eliminate the allocations by using a type parameter for the function types, rather than `@inline`:

```julia
test_calc(x, fn1::F1, fn2::F2) where {F1,F2} = @. x = x + fn1(x) + fn2(x)

```

This is covered in the manual’s performance tips: Julia doesn’t always specialize functions on function arguments, but type parameters are a workaround: [Be aware of when Julia avoids specializing](https://docs.julialang.org/en/v1/manual/performance-tips/#Be-aware-of-when-Julia-avoids-specializing)

---

<div class="post-metadata">

**Author:** ![aaraujo71](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aaraujo71/32/10635_2.png) [@aaraujo71](https://discourse.julialang.org/u/aaraujo71)\
**Post date:** [July 13, 2021, 7:15pm UTC](https://discourse.julialang.org/t/memory-allocation-with-function-passed-as-arguments/64575/5 "2021-07-13T19:15:00Z")

</div>

Wow! Thank you very much. I am definitely going to read the manual so that I understand what’s going on.
