# Decide which function to use inside a loop based on a variable

**URL:** <https://discourse.julialang.org/t/decide-which-function-to-use-inside-a-loop-based-on-a-variable/48726>\
**Category:** New to Julia\
**Created:** [October 21, 2020, 7:09am UTC](https://discourse.julialang.org/t/decide-which-function-to-use-inside-a-loop-based-on-a-variable/48726 "2020-10-21T07:09:41Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![ash](https://avatars.discourse-cdn.com/v4/letter/a/f19dbf/32.png) [@ash](https://discourse.julialang.org/u/ash)\
**Post date:** [October 21, 2020, 7:09am UTC](https://discourse.julialang.org/t/decide-which-function-to-use-inside-a-loop-based-on-a-variable/48726/1 "2020-10-21T07:09:41Z")

</div>

I currently have some code that looks like this:

```julia
    for i = 1:Nₓ-1
        if algorithm == "2S"
           ψ[i+1, :] = T2(ψ[i, :], ω, dx, F, F̃)
        elseif algorithm == "4S"
            ψ[i+1, :] = T4S(ψ[i, :], ω, dx, F, F̃)
        elseif algorithm == "6S"
            ψ[i+1, :] = T6S(ψ[i, :], ω, dx, F, F̃)
        elseif algorithm == "8S"
            ψ[i+1, :] = T8S(ψ[i, :], ω, dx, F, F̃)
        else
            throw(ArgumentError("Algorithm type unknown, please check the documentation"))
        end
   end

```

Essentially the code uses the variable `algorithm` to decide which function to use. The problem is that this is done repeatedly inside an extremely long loop, hence degrading performance according to the profiler. It is also somewhat ugly. Is there any way around this like there is in some other languages?

EDIT:  
I am updating this to show the form of `F`, `F̃` and the function `T2`:

```julia
F = plan_fft!(sim.ψ[1, :]) # Plan
F̃ = plan_ifft!(sim.ψ[1, :]) # Inverse Plan

```

```julia
function T2(ψ, ω, dx, F, F̃)
    # Nonlinear (loop over vectorization for speed)
    for i in 1:length(ψ)
        ψ[i] *= exp(-im * dx/2 * (-1*abs2(ψ[i]))) 
    end
    # Kinetic
    F*ψ
    ψ .*= ifftshift(exp.(-im * dx * ω .^ 2 / 2)) 
    F̃*ψ

    # Nonlinear (loop over vectorization for speed)
    for i in 1:length(ψ)
        ψ[i] *= exp(-im * dx/2 * (-1*abs2(ψ[i]))) 
    end

    return ψ
end #T2

```

---

<div class="post-metadata">

**Author:** ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)\
**Post date:** [October 21, 2020, 7:21am UTC](https://discourse.julialang.org/t/decide-which-function-to-use-inside-a-loop-based-on-a-variable/48726/2 "2020-10-21T07:21:02Z")

</div>

It’s hard to say without more context but:

- It is quite unlikely that the string equality check has any performance impact. The allocation of `ψ[i, :]` should likely be much more expensive. Perhaps you can pass `@view ψ[i, :]` instead.
- You can use enums instead of strings, they are cheaper to compare and their type make more sense (it makes it impossible to pass an invalid input)

```julia
@enum Algorithms begin
    ALGORITHM_2S,
    ...
end
run(ALGORITHM_2S)

```

- You can just pass the function directly as an argument. `run(T2)` instead of `run("2S2")`.

---

<div class="post-metadata">

**Author:** ![ash](https://avatars.discourse-cdn.com/v4/letter/a/f19dbf/32.png) [@ash](https://discourse.julialang.org/u/ash)\
**Post date:** [October 21, 2020, 8:55am UTC](https://discourse.julialang.org/t/decide-which-function-to-use-inside-a-loop-based-on-a-variable/48726/3 "2020-10-21T08:55:02Z")

</div>

> [@kristoffer.carlsson](#):
>
> It is quite unlikely that the string equality check has any performance impact. The allocation of `ψ[i, :]` should likely be much more expensive. Perhaps you can pass `@view ψ[i, :]` instead

I have tried moving to views but surprisingly, `@time` says I only have 2 allocations less and the code is actually _slower_ by about 10%. I have updated the code above with the definition of `T2` and `F`. When I use views it’s simply:

```julia
F = plan_fft!(sim.ψ[1, :]) # Plan
F̃ = plan_ifft!(sim.ψ[1, :]) # Inverse Plan
@views ψ[i+1, :] = T2(ψ[i, :], ω, dx, F, F̃)

```

> [@kristoffer.carlsson](#):
>
> You can use enums instead of strings, they are cheaper to compare and their type make more sense (it makes it impossible to pass an invalid input)

I have moved my code over from strings to enums, thank you.

> [@kristoffer.carlsson](#):
>
> You can just pass the function directly as an argument. `run(T2)` instead of `run("2S2")` .

Could you possibly give me an example of this with `T2`’s arguments or point me to somewhere in the documentation? I’m not sure how to get this to work and all I see from `?run` is stuff about external programs.

---

<div class="post-metadata">

**Author:** ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)\
**Post date:** [October 21, 2020, 9:23am UTC](https://discourse.julialang.org/t/decide-which-function-to-use-inside-a-loop-based-on-a-variable/48726/4 "2020-10-21T09:23:23Z")

</div>

You must have somewhere a function you call like `fun("2S", ψ, ω, dx)`, which contains this if-else block, and Kristoffer is suggesting you change this to `fun(T2, ψ, ω, dx)`. Instead of testing the string, it would then just apply whatever function it receives.

In `T2`, you may want to do this:

```julia
    @inbounds for i in 1:length(ψ)
        ψ[i] *= cis(dx/2 *abs2(ψ[i])) 
    end

```

and to notice that `exp.(-im * dx * ω .^ 2 / 2)` allocates a few arrays (assuming `ω` is an array), you may want to pass T2 a scratch space to re-use, `@. cache = cis(-dx * ω^2 / 2)`. And perhaps write a function like `ifftshift_times!(ψ, cache)`

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [October 21, 2020, 1:28pm UTC](https://discourse.julialang.org/t/decide-which-function-to-use-inside-a-loop-based-on-a-variable/48726/5 "2020-10-21T13:28:04Z")

</div>

What version of Julia are much faster in 1.5. (and they usually don’t allocate anymore)

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [October 21, 2020, 1:33pm UTC](https://discourse.julialang.org/t/decide-which-function-to-use-inside-a-loop-based-on-a-variable/48726/6 "2020-10-21T13:33:48Z")

</div>

What Oscar tried to say is that `@view` is non-allocating from Julia 1.5 on, so if you are using a previous version maybe you will not see a positive difference.

---

<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:** [October 21, 2020, 1:34pm UTC](https://discourse.julialang.org/t/decide-which-function-to-use-inside-a-loop-based-on-a-variable/48726/7 "2020-10-21T13:34:07Z")

</div>

> [@ash](#):
>
> `ψ .*= ifftshift(exp.(-im * dx * ω .^ 2 / 2)) `

(Note that this allocates multiple temporary arrays, e.g. `ω .^ 2 / 2` allocates a temporary array for the `/ 2`, and `fftshift` allocates another temporary array.)

---

<div class="post-metadata">

**Author:** ![cojua8](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cojua8/32/11882_2.png) [@cojua8](https://discourse.julialang.org/u/cojua8)\
**Post date:** [October 21, 2020, 1:59pm UTC](https://discourse.julialang.org/t/decide-which-function-to-use-inside-a-loop-based-on-a-variable/48726/8 "2020-10-21T13:59:53Z")

</div>

Make use of multiple dispatch by creating types and structs:

````julia
# define types
abstract type Algorithm end

struct Algo2S <: Algorithm end
struct Algo4S <: Algorithm end
struct Algo6S <: Algorithm end
struct Algo8S <: Algorithm end

# then you define functions and methods

# default
run_algorithm(::Algorithm, ψ, ω, dx, F, F̃) = throw(ArgumentError("Not defined"))

run_algorithm(::Algo2S, ψ, ω, dx, F, F̃) = T2(ψ[i, :], ω, dx, F, F̃)
run_algorithm(::Algo4S, ψ, ω, dx, F, F̃) = T4S(ψ[i, :], ω, dx, F, F̃)
run_algorithm(::Algo6S, ψ, ω, dx, F, F̃) = T6S(ψ[i, :], ω, dx, F, F̃)
run_algorithm(::Algo8S, ψ, ω, dx, F, F̃) = T8S(ψ[i, :], ω, dx, F, F̃)

#lastly, in your program you just do

algorithm = Algo2S() 
for i = 1:Nₓ-1
    ψ[i+1, :] = run_algorithm(algorithm, ψ, ω, dx, F, F̃)
end

```
````

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [October 21, 2020, 2:38pm UTC](https://discourse.julialang.org/t/decide-which-function-to-use-inside-a-loop-based-on-a-variable/48726/9 "2020-10-21T14:38:38Z")

</div>

I am really not sure if this is a better way than just passing a function. If the receiving function had multiple distinct calls that change with the passed algorithm (i.e., you also wanted to query the name of the algorithm for the output, so you defined an `algorithm_name` too, and so on) then this would be the best solution, but seems overkill if the receiving function only needs to do a single thing differently because of the chosen internal algorithm.

---

<div class="post-metadata">

**Author:** ![cojua8](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cojua8/32/11882_2.png) [@cojua8](https://discourse.julialang.org/u/cojua8)\
**Post date:** [October 21, 2020, 2:57pm UTC](https://discourse.julialang.org/t/decide-which-function-to-use-inside-a-loop-based-on-a-variable/48726/10 "2020-10-21T14:57:37Z")

</div>

@Henrique_Becker you are right!

It would be much easier to do:

```julia
#set algorithm value to the function name, e.g.:
algorithm = T4S

#then, algorithm is the function you want to call 
for i = 1:Nₓ-1
    ψ[i+1, :] = algorithm(ψ, ω, dx, F, F̃)
end

```

---

<div class="post-metadata">

**Author:** ![ash](https://avatars.discourse-cdn.com/v4/letter/a/f19dbf/32.png) [@ash](https://discourse.julialang.org/u/ash)\
**Post date:** [October 22, 2020, 1:20am UTC](https://discourse.julialang.org/t/decide-which-function-to-use-inside-a-loop-based-on-a-variable/48726/11 "2020-10-22T01:20:58Z")

</div>

Thanks to everyone’s suggestions, I ended up with this:

```julia
function T2(ψ, W, dx, F, F̃)
    @inbounds for i in 1:length(ψ)
        ψ[i] *= cis(dx/2 * (-1*abs2(ψ[i]))) 
    end

    F*ψ
    @inbounds for i in 1:length(W)
        ψ[i] *= W[i]
    end
    F̃*ψ

    @inbounds for i in 1:length(ψ)
        ψ[i] *= cis(dx/2 * (-1*abs2(ψ[i]))) 
    end

    return ψ
end

function run(algorithm, ...)
    F = plan_fft!(sim.ψ[1, :]) 
    F̃ = plan_ifft!(sim.ψ[1, :]) 

    W = ifftshift(cis.(sim.box.dx * sim.box.ω .^ 2 / 2))

   for i = 1:sim.box.Nₓ-1
        ψ[i+1, :] = algorithm(ψ[i,:], W, dx, F, F̃)
   end #for

end

run(T2)

```

This has improved my performance by a factor of 3.5-4, which is fantastic. I would still like to get views working (currently they slow down my code on Julia 1.5), as well as multithreading the loops and FFTW (they SIGNIFICANTLY) slow down my code. I’ll ask that as a separate question.
