# Does the cost of function call matter in Julia?

**URL:** <https://discourse.julialang.org/t/does-the-cost-of-function-call-matter-in-julia/6990>\
**Category:** General Usage\
**Tags:** performance, optim\
**Created:** [November 10, 2017, 9:34am UTC](https://discourse.julialang.org/t/does-the-cost-of-function-call-matter-in-julia/6990 "2017-11-10T09:34:18Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![111](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/111/32/33971_2.png) [@111](https://discourse.julialang.org/u/111)\
**Post date:** [November 10, 2017, 9:34am UTC](https://discourse.julialang.org/t/does-the-cost-of-function-call-matter-in-julia/6990/1 "2017-11-10T09:34:18Z")

</div>

I’ve written a long function, and according to [https://docs.julialang.org/en/latest/manual/performance-tips/](https://docs.julialang.org/en/latest/manual/performance-tips/), I rewrote it with some functions to avoid the global variables. The performance is nicer now. My questions are, does the cost of function call matter in Julia? Is writing so many function necessary? how can I optimize my code (I found the line @. prop\_e = cis( ele \* (xplusy \* dt/2.0)) and the “get momentum” part might be the bottleneck and the @threading strategy seems unsuitable because the gc time is very large)?

The code is

```julia
function evolution(u::Array{Complex128})
    t = Array((1:nt) * dt)
    ele = map(GetEleField, t)
    save("elefield.jld", "t", t, "ele", ele)

    FFTW.set_num_threads(Sys.CPU_CORES)

    x, p = xpgrid()
    ########### common part
    p_fft! = plan_fft!( similar(u), flags=FFTW.MEASURE )
    prop_x, prop_p = operation_real_evolution(x, p)
    prop_e = similar( prop_x )

    r_a = 750 #ngrid * dx /2.0 - 80.0
    δ = 15.0
    r_sp = 120.0
    δ_sp = 10.0

    absorb = Array(Float64, ngrid, ngrid)
    splitter = Array(Float64, ngrid, ngrid)
    xplusy = Array(Float64, ngrid, ngrid)
    for j in 1:ngrid
        for i in 1:ngrid
            xplusy[i, j] = x[i] + x[j]
            absorb[i, j] = (1.0 - out(x[i], r_a, δ ))* (1.0 - out(x[j], r_a, δ))
            splitter[i, j] = out(x[i], r_sp, δ_sp) * out(x[j], r_sp, δ_sp)
        end
    end

    ##### splitting
    pp = similar(p)
    A = -cumsum(ele) * dt
    A² = A.*A
    uo = zeros(Complex128, ngrid, ngrid)
    up = zeros(Complex128, ngrid, ngrid)
    #####
    for i in 1:nt
        step_evolution_real!(u, p_fft!, prop_x, prop_p, ele[i], xplusy, absorb, prop_e)

        ########## get momentum
        if i % 3 == 0
            @. uo = (u * splitter) * cis(A[i] * xplusy)

            p_fft! * uo
            pp .= (p.^2/2.0) * (nt-i) .+ p*sum(@view A[i:nt]) + sum(@view A²[i:nt])/2.0
            for j2 in 1:ngrid
                for j1 in 1:ngrid
                    uo[j1, j2] = uo[j1, j2] * cis( -dt * (pp[j1] + pp[j2]) )
                end
            end
            up .+= uo
        end

        ########### end get momentum

        if (mod(i, 400) == 0)
            println(100*i/real(nt),"%, time = ", now())
        end
    end

    nothing
end

out(x, r_a, δ) = 1.0 ./ (1.0 + exp( -(abs(x) - r_a) ./δ ))

function step_evolution_real!(u::Array{Complex128, 2},
                              p_fft!::Base.DFT.FFTW.cFFTWPlan{Complex128, -1, true, 2},
                              prop_x::Array{Complex128, 2}, prop_p::Array{Complex128, 2},
                              ele::Float64, xplusy::Array{Float64, 2},
                              absorb::Array{Float64, 2}, prop_e::Array{Complex128, 2})
    @. prop_e = cis( ele * (xplusy * dt/2.0))
    u .*= prop_x .* prop_e

    p_fft! * u
    u .*= prop_p

    p_fft! \ u
    @. u *= prop_x * prop_e

    u .*= absorb
    nothing
end

function operation_real_evolution(x::Array{Float64, 1}, p::Array{Float64, 1})
    prop_x = Array{Complex128}(ngrid , ngrid)
    prop_p = Array{Complex128}(ngrid , ngrid)

    for j in 1:ngrid
        for i in 1:ngrid
            prop_x[i, j] = cis( -(dt / 2) * Potential(x[i], x[j]) )
            prop_p[i, j] = cis( -(dt / 2) * (p[i]^2 + p[j]^2) )
        end
    end
    prop_x, prop_p
end

```

---

<div class="post-metadata">

**Author:** ![mauro3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mauro3/32/292_2.png) [@mauro3](https://discourse.julialang.org/u/mauro3)\
**Post date:** [November 10, 2017, 10:25am UTC](https://discourse.julialang.org/t/does-the-cost-of-function-call-matter-in-julia/6990/2 "2017-11-10T10:25:21Z")

</div>

Small functions are generally in-lined, so there is no call-cost for them. If you are not satisfied with the inline-heuristics then decorate your function with `@inline` or `@noinline`.

> [@111](#):
>
> because the gc time is very large

Without having read your code-example, this suggests that you can improve your algorithm with per-allocating work arrays and then using them. Thus no new data gets allocated during a loop and GC time should be small.

---

<div class="post-metadata">

**Author:** ![saschatimme](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/saschatimme/32/10313_2.png) [@saschatimme](https://discourse.julialang.org/u/saschatimme)\
**Post date:** [November 10, 2017, 10:36am UTC](https://discourse.julialang.org/t/does-the-cost-of-function-call-matter-in-julia/6990/3 "2017-11-10T10:36:15Z")

</div>

At least from your code sample it seems that `ngrid` and `nt` are still global variables. This could explain the large gc time.

---

<div class="post-metadata">

**Author:** ![111](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/111/32/33971_2.png) [@111](https://discourse.julialang.org/u/111)\
**Post date:** [November 10, 2017, 10:39am UTC](https://discourse.julialang.org/t/does-the-cost-of-function-call-matter-in-julia/6990/4 "2017-11-10T10:39:05Z")

</div>

As far as i know, global variables won’t cause performance problems if they are not Vectors or Matrix. Will Numbers cause preallocation problem?

---

<div class="post-metadata">

**Author:** ![pfitzseb](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pfitzseb/32/45566_2.png) [@pfitzseb](https://discourse.julialang.org/u/pfitzseb)\
**Post date:** [November 10, 2017, 10:55am UTC](https://discourse.julialang.org/t/does-the-cost-of-function-call-matter-in-julia/6990/5 "2017-11-10T10:55:22Z")

</div>

Any non-`const` global variable might cause inference issues which propagate through your code, so yes, you should definitely take care when using them.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [November 10, 2017, 10:55am UTC](https://discourse.julialang.org/t/does-the-cost-of-function-call-matter-in-julia/6990/6 "2017-11-10T10:55:25Z")

</div>

I think you misunderstand about globals, please read the relevant part the [performance tips](https://docs.julialang.org/en/latest/manual/performance-tips/#Avoid-global-variables-1) (better, read the whole thing).

In many compiled but dynamically types languages, making large, “monolithic” functions is advantageous since it allows the compiler to figure out type information. With Julia, the situation is the opposite: the language is designed to work with type information as efficiently as statically typed languages, you just have to make this possible. One technique for that is actually factoring out behavior into smaller functions, called [function barriers](https://docs.julialang.org/en/latest/manual/performance-tips/#kernal-functions-1).

---

<div class="post-metadata">

**Author:** ![111](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/111/32/33971_2.png) [@111](https://discourse.julialang.org/u/111)\
**Post date:** [November 10, 2017, 11:04am UTC](https://discourse.julialang.org/t/does-the-cost-of-function-call-matter-in-julia/6990/7 "2017-11-10T11:04:11Z")

</div>

In my code, I have factored the behaviors into small functions. So the bottleneck is not about function calls.

---

<div class="post-metadata">

**Author:** ![111](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/111/32/33971_2.png) [@111](https://discourse.julialang.org/u/111)\
**Post date:** [November 10, 2017, 11:17am UTC](https://discourse.julialang.org/t/does-the-cost-of-function-call-matter-in-julia/6990/8 "2017-11-10T11:17:21Z")

</div>

In another module, I already declared them as

```julia
const ngrid=2^13
const nt = 10000

```

and used this module when calling the function evolution()  
and add “const” before r\_a, δ, r\_sp, δ\_sp doesn’t make any difference.

---

<div class="post-metadata">

**Author:** ![111](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/111/32/33971_2.png) [@111](https://discourse.julialang.org/u/111)\
**Post date:** [November 10, 2017, 11:29am UTC](https://discourse.julialang.org/t/does-the-cost-of-function-call-matter-in-julia/6990/9 "2017-11-10T11:29:55Z")

</div>

Actually, the code I show here is fast enough, which is about 1.7x slower than the Fortran version with “-O3 -mkl -parallel”. However, I just can’t be easily satisfied…

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [November 10, 2017, 12:05pm UTC](https://discourse.julialang.org/t/does-the-cost-of-function-call-matter-in-julia/6990/10 "2017-11-10T12:05:24Z")

</div>

> [@111](#):
>
> However, I just can’t be easily satisfied…

In that case, perhaps try profiling. When you get within 2x Fortran or C, it becomes harder to talk about code optimization in abstract terms, the solution may be specific to your code.
