# Optimizing or replacing IF-ELSEIF-ELSE statements

**URL:** https://discourse.julialang.org/t/optimizing-or-replacing-if-elseif-else-statements/126867
**Category:** Performance
**Tags:** question
**Created:** [March 12, 2025, 12:33pm UTC](https://discourse.julialang.org/t/optimizing-or-replacing-if-elseif-else-statements/126867 "2025-03-12T12:33:28Z")
**Posts on this page:** 10
**Page:** 1

<div class="post-metadata">

### Author: ![hssn15](https://avatars.discourse-cdn.com/v4/letter/h/8797f3/32.png) [@hssn15](https://discourse.julialang.org/u/hssn15)
#### Post date: [March 12, 2025, 12:33pm UTC](https://discourse.julialang.org/t/optimizing-or-replacing-if-elseif-else-statements/126867/1 "2025-03-12T12:33:28Z")

</div>

I am developing a simulator but I dont have a lot experience and tricks about Julia. I have a case that seems like it has some kind of other way around that is much more efficient and optimized.

I will give you and example that is similar to my case.

```julia
struct Struct1
    value::Float64
    type::Symbol
end

obj1 = Struct1(1.0, :A)
obj2 = Struct1(1.0, :B)
obj3 = Struct1(1.0, :C)

objs = [obj1, obj2, obj3]
results = zeros(length(objs)*10)

for i in eachindex(objs)
        obj = objs[i]
        type = obj.type
    for n in 1:10
        position = (i-1)*10+n
        ############Here we have 20-40 lines of code block############
        if type == :A
            val = obj.value
            results[position] = val # or much more complex operations in a few lines
        elseif type == :B
            val = obj.value
            results[position] = val*2 # or much more complex operations in a few lines
        elseif type == :C
            val = obj.value
            results[position] = val*3 # or much more complex operations in a few lines
        end

    end
end

```

Explanation:

Vector “objs” always stays constant. So types always will be same in that order. In that way, we always need to check if type is A or B or C for each iteration of loop 2 even we know that type will be always A through “loop 2” since we choose “obj1” at first iteration of “loop 1”.

one way to reduce meaningless condition checks 10 times in “loop2” is writing conditional statements before the “loop 2”:

```julia
for i in eachindex(objs)
    obj = objs[i]
    type = obj.type
    if type == :A
        val = obj.value
        for n in 1:10
            position = (i-1)*10+n
            ############Here we have 20-40 lines of code block############
            results[position] = val # or much more complex operations in a few lines
        end

    elseif type == :B
        val = obj.value
        for n in 1:10
            position = (i-1)*10+n
            ############Here we have 20-40 lines of code block############
            results[position] = val # or much more complex operations in a few lines
        end

    elseif type == :C
        val = obj.value
        for n in 1:10
            position = (i-1)*10+n
            ############Here we have 20-40 lines of code block############
            results[position] = val # or much more complex operations in a few lines
        end

    end
end

```

But in this way we need to write a loop for each condition so we need to write " 20-40 lines of code block" for each which is not efficient. is there a way around?

Thank You

---

<div class="post-metadata">

### Author: ![YabusameHoulen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yabusamehoulen/32/213258_2.png) [@YabusameHoulen](https://discourse.julialang.org/u/YabusameHoulen)
#### Post date: [March 12, 2025, 2:25pm UTC](https://discourse.julialang.org/t/optimizing-or-replacing-if-elseif-else-statements/126867/2 "2025-03-12T14:25:27Z")

</div>

DRY ? make “20-40 lines of code block” into a function  
since Julia can’t dispatch on value ?  
I don’t have a lot experience and tricks about Julia either 😹

---

<div class="post-metadata">

### Author: ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)
#### Post date: [March 12, 2025, 2:34pm UTC](https://discourse.julialang.org/t/optimizing-or-replacing-if-elseif-else-statements/126867/3 "2025-03-12T14:34:38Z")

</div>

Before worrying about this sort of micro-optimization, you’ve got some bigger fish to fry. Namely, you should put this computation into a function instead of running it at top-level:

```julia
objs = [obj1, obj2, obj3]
function do_computation(objs)
    results = zeros(length(objs)*10)
    for i in eachindex(objs)
        #...
    end
    return results
end
do_computation(objs)

```

A chain if `if`-`elseif`-`elseif`-`elseif` often performs better than you’d expect. The compiler may even re-implement it into a jump table if it thinks that’d be helpful. But the difference here will almost surely be peanuts compared to optimizing the “real work” of your algorithm. Be sure to check out the performance tips — which stresses the importance of functions like I suggested above, as well as many other tips:

> **[Performance Tips · The Julia Language](https://docs.julialang.org/en/v1/manual/performance-tips/)**
>
> Documentation for The Julia Language.

---

<div class="post-metadata">

### Author: ![Paul\_Schrimpf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/paul_schrimpf/32/10134_2.png) [@Paul\_Schrimpf](https://discourse.julialang.org/u/Paul_Schrimpf)
#### Post date: [March 12, 2025, 2:36pm UTC](https://discourse.julialang.org/t/optimizing-or-replacing-if-elseif-else-statements/126867/4 "2025-03-12T14:36:52Z")

</div>

You can use multiple dispatch if you make `Struct1` a parametric type.

```julia
struct Struct1{type}
    value::Float64
end

f(obj::Struct1{:A}) = obj.value
f(obj::Struct1{:B}) = 2*obj.value
f(obj::Struct1{:C}) = 3*obj.value

obj1 = Struct1{:A}(1.0)
obj2 = Struct1{:B}(1.0)
obj3 = Struct1{:C}(1.0)

objs = [obj1, obj2, obj3]
results = zeros(length(objs)*10)

for (i,obj) in enumerate(objs)
  for n in 1:10
    position = (i-1)*10+n
    results[position] = f(obj)
  end
end

```

Depending on the bigger context, this may not be the best solution, but it does shorten your loop.

---

<div class="post-metadata">

### Author: ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)
#### Post date: [March 12, 2025, 2:38pm UTC](https://discourse.julialang.org/t/optimizing-or-replacing-if-elseif-else-statements/126867/5 "2025-03-12T14:38:05Z")

</div>

That will certainly perform worse 🙂

---

<div class="post-metadata">

### Author: ![kellertuer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kellertuer/32/220707_2.png) [@kellertuer](https://discourse.julialang.org/u/kellertuer)
#### Post date: [March 12, 2025, 2:42pm UTC](https://discourse.julialang.org/t/optimizing-or-replacing-if-elseif-else-statements/126867/6 "2025-03-12T14:42:10Z")

</div>

Even in short you can also dispatch on value

```julia
f(::Val{:a}) = 1
f(::Val{:b}) = 2
f(s::Symbol) = f(Val(s))
[f(:a), f(:b)] # a long dway to wite [1,2]

```

but if you are lucky that improves readability somewhere, but not necessarily performance.

For exactly the case you have you could still store a `factor = Dict(:A=>1, :B=>2, :C=>3)`  
and do a

```julia
results[position] = factor[type] * val

```

thing? Depends of course bit what the concrete example needs, you could also store that factor in the struct for example.

---

<div class="post-metadata">

### Author: ![Paul\_Schrimpf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/paul_schrimpf/32/10134_2.png) [@Paul\_Schrimpf](https://discourse.julialang.org/u/Paul_Schrimpf)
#### Post date: [March 12, 2025, 3:10pm UTC](https://discourse.julialang.org/t/optimizing-or-replacing-if-elseif-else-statements/126867/7 "2025-03-12T15:10:48Z")

</div>

> [@mbauman](#):
>
> That will certainly perform worse

I checked because it wasn’t obvious to me. At least in this simple example, I’m finding there’s no difference. To measure, I wrapped the loop in function, changed 10 to a much bigger number, and preallocated `results` to focus on the difference in the conditional. I get identical benchmarks.

```julia
julia> @benchmark dispatch!(results,objs,multiple)

BenchmarkTools.Trial: 1942 samples with 1 evaluation per sample.
 Range (min … max): 2.477 ms … 3.203 ms ┊ GC (min … max): 0.00% … 0.00%
 Time (median): 2.513 ms ┊ GC (median): 0.00%
 Time (mean ± σ): 2.557 ms ± 108.804 μs ┊ GC (mean ± σ): 0.00% ± 0.00%

  ▆██▆▅▆▅▅▃▃▂ ▁▁▂▂▁ ▂▁ ▁
  ███████████████████████▇█████▆█▇▇▇▅▅▆▇▇█▆▇▇▇▇▇▆▆▄▅▁▄▆▆▅▄▄▄▄ █
  2.48 ms Histogram: log(frequency) by time 2.99 ms <

 Memory estimate: 0 bytes, allocs estimate: 0.

julia> @benchmark conditional!(results,objs2,multiple)
BenchmarkTools.Trial: 1923 samples with 1 evaluation per sample.
 Range (min … max): 2.477 ms … 3.328 ms ┊ GC (min … max): 0.00% … 0.00%
 Time (median): 2.519 ms ┊ GC (median): 0.00%
 Time (mean ± σ): 2.582 ms ± 145.734 μs ┊ GC (mean ± σ): 0.00% ± 0.00%

  ▇█▇▆▆▅▃▂ ▁▂▂▁▂▂▂▂▁▁▁▂▁ ▁▁ ▁ ▁
  ███████████████████████▇▇██████▇██▇▇▇▆▇▆▆▅▇▆▇▇▅▄▅▄▁▄▅▅▆▄▄▅▄ █
  2.48 ms Histogram: log(frequency) by time 3.16 ms <

 Memory estimate: 0 bytes, allocs estimate: 0.

```

I can imagine that (ab)using multiple dispatch in this way might cause performance problems in more complex situations, but I’m not sure where that line is.

Full code below.

```julia
struct Struct1{type}
    value::Float64
end

f(obj::Struct1{:A}) = obj.value
f(obj::Struct1{:B}) = 2*obj.value
f(obj::Struct1{:C}) = 3*obj.value

obj1 = Struct1{:A}(1.0)
obj2 = Struct1{:B}(1.0)
obj3 = Struct1{:C}(1.0)

objs = [obj1, obj2, obj3]

function dispatch!(results,objs,multiple)
  for (i,obj) in enumerate(objs)
    for n in 1:multiple
      position = (i-1)*multiple+n
      results[position] = f(obj)
    end
  end
  return(results)
end

struct Struct2
    value::Float64
    type::Symbol
end

obj1 = Struct2(1.0, :A)
obj2 = Struct2(1.0, :B)
obj3 = Struct2(1.0, :C)

objs2 = [obj1, obj2, obj3]

function conditional!(results,objs,multiple)
  for i in eachindex(objs)
    obj = objs[i]
    type = obj.type
    for n in 1:multiple
      position = (i-1)*multiple+n
      ############Here we have 20-40 lines of code block############
      if type == :A
        val = obj.value
        results[position] = val # or much more complex operations in a few lines
      elseif type == :B
        val = obj.value
        results[position] = val*2 # or much more complex operations in a few lines
      elseif type == :C
        val = obj.value
        results[position] = val*3 # or much more complex operations in a few lines
      end
    end
  end
  return(results)
end

using BenchmarkTools, Test

results = zeros(length(objs)*100)
dispatch!(results,objs,100)
results2 = zeros(length(objs)*100)
conditional!(results2,objs2,100)
@test results ≈ results2

multiple=1_000_000
results = zeros(length(objs)*multiple)
@benchmark dispatch!(results,objs,multiple)
@benchmark conditional!(results,objs2,multiple)

```

---

<div class="post-metadata">

### Author: ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)
#### Post date: [March 12, 2025, 3:30pm UTC](https://discourse.julialang.org/t/optimizing-or-replacing-if-elseif-else-statements/126867/8 "2025-03-12T15:30:36Z")

</div>

You’re dancing right at the edge of a 1000x tall cliff there:

```julia-repl
julia> @benchmark dispatch!($results,$objs,$multiple)
BenchmarkTools.Trial: 10000 samples with 1 evaluation per sample.
 Range (min … max): 370.125 μs … 1.523 ms ┊ GC (min … max): 0.00% … 0.00%
 Time (median): 411.916 μs ┊ GC (median): 0.00%
 Time (mean ± σ): 450.258 μs ± 105.652 μs ┊ GC (mean ± σ): 0.00% ± 0.00%

  ▅█▇▆▄▃▃▂▂▅▇▇▅▄▃▂▂▁▁ ▁ ▂
  █████████████████████▇▇▇▆▇▆▅▆▅▄▅▅▅▅▅▃▅▅▄▅▅▄▃▃▅▃▃▁▁▃▃▃▅▇▇█████ █
  370 μs Histogram: log(frequency) by time 964 μs <

 Memory estimate: 0 bytes, allocs estimate: 0.

julia> f(obj::Struct1{:D}) = 4*obj.value
f (generic function with 4 methods)

julia> @benchmark dispatch!($results,$objs,$multiple)
BenchmarkTools.Trial: 22 samples with 1 evaluation per sample.
 Range (min … max): 227.317 ms … 240.387 ms ┊ GC (min … max): 0.66% … 4.48%
 Time (median): 229.196 ms ┊ GC (median): 1.54%
 Time (mean ± σ): 230.290 ms ± 3.112 ms ┊ GC (mean ± σ): 1.57% ± 0.79%

        █
  ▅▁▅▅▁██▅▁▅█▁▁▅█▅▁▁▁▁▁▁▅▁▁▁▁▁▁▅▁▁▁▁▁▁▁▁▁▁▁▁▁▁▅▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▅ ▁
  227 ms Histogram: frequency by time 240 ms <

 Memory estimate: 91.54 MiB, allocs estimate: 5999489.

```

The more important point, though, is that parametric structs are more advanced techniques. Focus on the simple stuff first.

---

<div class="post-metadata">

### Author: ![Alexander-Barth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alexander-barth/32/3692_2.png) [@Alexander-Barth](https://discourse.julialang.org/u/Alexander-Barth)
#### Post date: [March 12, 2025, 4:00pm UTC](https://discourse.julialang.org/t/optimizing-or-replacing-if-elseif-else-statements/126867/9 "2025-03-12T16:00:56Z")

</div>

It is quite interesting to see the degradation of performance when you add a new type. The solution of @Paul_Schrimpf looks to me quite idiomatic. I don’t see why this could be an abuse of dispatch. I think one can also use union types, to keep the same performance:

```julia
f(obj::Struct1{:D}) = 4*obj.value

TU = Union{Struct1{:A},Struct1{:B},Struct1{:C},Struct1{:D}}
objs_union = TU[obj1, obj2, obj3]
@benchmark dispatch!(results,objs_union,multiple)
# same as conditional!(results,objs2,multiple)

```

---

<div class="post-metadata">

### Author: ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)
#### Post date: [March 12, 2025, 4:30pm UTC](https://discourse.julialang.org/t/optimizing-or-replacing-if-elseif-else-statements/126867/10 "2025-03-12T16:30:58Z")

</div>

The OP here came to us with code that didn’t even apply the very first and most important performance tip: **_use a function_**. I don’t think it’s very useful to dive straight into the deep end with parametric structs.

To be clear, I didn’t add a type. I added a method. When there are only a few methods that Julia _might possibly_ call, it’ll convert dispatch into a bunch of ifelses for you. But you’re dancing on the edge of a cliff. Similarly, if an array only has a few types it could possibly contain, it’ll convert dispatch to a bunch of ifelses for you. But you’re once again dancing on the edge of a cliff. If you don’t know that the cliffs are there, you’re gonna fall.
