# Optimizing nested loops with conditional inside

**URL:** <https://discourse.julialang.org/t/optimizing-nested-loops-with-conditional-inside/101724>\
**Category:** Performance\
**Tags:** question, loops, physics, loopvectorization\
**Created:** [July 18, 2023, 11:32am UTC](https://discourse.julialang.org/t/optimizing-nested-loops-with-conditional-inside/101724 "2023-07-18T11:32:30Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Luis\_Alberto\_R](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/luis_alberto_r/32/51629_2.png) [@Luis\_Alberto\_R](https://discourse.julialang.org/u/Luis_Alberto_R)\
**Post date:** [July 18, 2023, 11:32am UTC](https://discourse.julialang.org/t/optimizing-nested-loops-with-conditional-inside/101724/1 "2023-07-18T11:32:30Z")

</div>

Hello, I’m trying to build hydrodynamic codes in 1D, 2D and 3D in Julia. So far I have the codes working with first order solvers, and I’ve managed to make the 1D and 2D faster than Python and C++, now I’m trying to implement a new solver called HLL (and HLLC) and I’ve been using `@turbo` to improve the performance of the loops I have in the code, but it seems that I can’t use `@turbo` if I have an if statement inside the for. This is the code I need to optimize:

```julia
function p2f(F,UPr)
    F[1] = UPr[1]*UPr[2]
    F[2] = UPr[1]*UPr[2]*UPr[2] + UPr[4]
    F[3] = UPr[1]*UPr[2]*UPr[3]
    F[4] = UPr[2]*(0.5*UPr[1]*(UPr[2]^2 + UPr[3]^2) + 
        gamma/(gamma-1)*UPr[4])
end
function p2g(F,UPr)
    F[1] = UPr[1]*UPr[3]
    F[2] = UPr[1]*UPr[2]*UPr[3]
    F[3] = UPr[1]*UPr[3]*UPr[3] + UPr[4]
    F[4] = UPr[3]*(0.5*UPr[1]*(UPr[2]^2 + UPr[3]^2) + 
        gamma/(gamma-1)*UPr[4])
end
function HLLFluxes(U,F,G,UPrim)
    FL = zeros(Float64, neq)
    FR = zeros(Float64, neq)
    GL = zeros(Float64, neq)
    GR = zeros(Float64, neq)
    wavespeed(UPrim)
    for i in 1:nx+1
        for j in 1:ny+1
            if sl[i,j] >= 0
                @views p2f(F[:,i,j],UPrim[:,i,j])
            elseif sr[i,j] <= 0
                @views p2f(F[:,i,j],UPrim[:,i+1,j])
            else
                @views p2f(FL,UPrim[:,i,j])
                @views p2f(FR,UPrim[:,i+1,j])
                @views F[:,i,j] = (sr[i,j]*FL[:] .- sl[i,j]*FR[:] .+ sl[i,j]*sr[i,j]*(U[:,i+1,j] .- U[:,i,j]))./(
                sr[i,j]-sl[i,j])
            end
            if sd[i,j] >= 0
                @views p2g(G[:,i,j],UPrim[:,i,j])
            elseif su[i,j] <= 0
                @views p2g(G[:,i,j],UPrim[:,i,j+1])
            else
                @views p2g(GL,UPrim[:,i,j])
                @views p2g(GR,UPrim[:,i,j+1])
                @views G[:,i,j] = (su[i,j]*GL[:] .- sd[i,j]*GR[:] .+ sd[i,j]*su[i,j]*(U[:,i,j+1] .- U[:,i,j]))./(
                su[i,j]-sd[i,j])
            end
        end
    end
end

```

The first two functions only make the code more readable, the third function has the conditions of when the code takes the fluxes from the “left” or the “right” but since i need this condition, I can’t use the turbo macro to accelerate the code. I also tried doing `A = sl[i,j] .>= 0` to get a BitMatrix and do the operations from `p2f` and `p2g` but didn’t improve the performance, so is there a way to use turbo with an if statement or there’s another way to ptimize the function HLLFluxes? Thanks in advance.

---

<div class="post-metadata">

**Author:** ![Tarny\_GG\_Channie](https://avatars.discourse-cdn.com/v4/letter/t/3bc359/32.png) [@Tarny\_GG\_Channie](https://discourse.julialang.org/u/Tarny_GG_Channie)\
**Post date:** [July 18, 2023, 12:26pm UTC](https://discourse.julialang.org/t/optimizing-nested-loops-with-conditional-inside/101724/2 "2023-07-18T12:26:36Z")

</div>

You can try using ifelse to do a branchless if/else. It works by evaluating both branches. You can also use min/max…  
These are basic branchless ops. I believe

```julia
@views p2f(FL,UPrim[:,i,j])
@views p2f(FR,UPrim[:,i+1,j])

```

and

```julia
@views p2g(GL,UPrim[:,i,j])
@views p2g(GR,UPrim[:,i,j+1])

```

could be taken outside then you do some ifelse or min/max arithmetic to get results without branch, then you could use Turbo. Not sure if it would help though, it depends on how frequently the code goes through each branch.

---

<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 18, 2023, 2:30pm UTC](https://discourse.julialang.org/t/optimizing-nested-loops-with-conditional-inside/101724/3 "2023-07-18T14:30:40Z")

</div>

Yes, having conditionals in inner loops of finite-difference codes can really hurt performance (as [we’ve found in other languages and problems too](http://ab-initio.mit.edu/~oskooi/papers/Oskooi10.pdf)). If at all possible, I generally try to put these conditionals outside the loops: divide your domain into rectangular “chunks” where the conditionals are constants. If your conditionals are just there to handle boundary conditions, then it is better to use “ghost cells” for the boundary conditions and loop only over the interior of the domain without conditionals.

> [@Luis\_Alberto\_R](#):
>
> `p2f(F[:,i,j],UPrim[:,i,j])`

On a separate note, if things like `UPrim[:,i,j]` are N-component arrays in N dimensions, representing 1d/2d/3d vectors for velocity etcetera, I would [strongly consider using StaticArrays instead](https://docs.julialang.org/en/v1/manual/performance-tips/#Consider-StaticArrays.jl-for-small-fixed-size-vector/matrix-operations).

---

<div class="post-metadata">

**Author:** ![Luis\_Alberto\_R](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/luis_alberto_r/32/51629_2.png) [@Luis\_Alberto\_R](https://discourse.julialang.org/u/Luis_Alberto_R)\
**Post date:** [July 19, 2023, 12:08am UTC](https://discourse.julialang.org/t/optimizing-nested-loops-with-conditional-inside/101724/4 "2023-07-19T00:08:05Z")

</div>

Sorry my ignorance, but what is a branchless ifelse? Is it `a ? b : c` or the function `ifelse(cond, b, c)` or something else?

---

<div class="post-metadata">

**Author:** ![Luis\_Alberto\_R](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/luis_alberto_r/32/51629_2.png) [@Luis\_Alberto\_R](https://discourse.julialang.org/u/Luis_Alberto_R)\
**Post date:** [July 19, 2023, 12:22am UTC](https://discourse.julialang.org/t/optimizing-nested-loops-with-conditional-inside/101724/5 "2023-07-19T00:22:47Z")

</div>

Yeah I’m trying to think a way to put the conditionals outside de for, I was thinking something like the thing I put at the end, doing `A = sl[I,j] .<= 0` and then `B = sr[I,j] .>= 0` doing the left or right or the average depending on these matrixes, (I haven thought this much, I’m afk) but I thing is a bit involve and I’m not sure if it would really improve performance

Also, the UPrim[1:4,1:nx+2,1:ny+2] has the primitives (the 1:4 are density, v\_x, v\_y and pressure in that order) in all the extension of the grid, and I checked the link you provided and it says that is recommended for dimensions \<100 and the nx and ny are normally bigger than that ( between 200 and 1000 usually ) so I don’t know if that would be an issue

---

<div class="post-metadata">

**Author:** ![Tarny\_GG\_Channie](https://avatars.discourse-cdn.com/v4/letter/t/3bc359/32.png) [@Tarny\_GG\_Channie](https://discourse.julialang.org/u/Tarny_GG_Channie)\
**Post date:** [July 19, 2023, 12:31am UTC](https://discourse.julialang.org/t/optimizing-nested-loops-with-conditional-inside/101724/6 "2023-07-19T00:31:19Z")

</div>

The ifelse function.

---

<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 19, 2023, 1:04am UTC](https://discourse.julialang.org/t/optimizing-nested-loops-with-conditional-inside/101724/7 "2023-07-19T01:04:00Z")

</div>

> [@Luis\_Alberto\_R](#):
>
> Also, the UPrim[1:4,1:nx+2,1:ny+2] has the primitives (the 1:4 are density, v\_x, v\_y and pressure in that order) in all the extension of the grid, and I checked the link you provided and it says that is recommended for dimensions \<100 and the nx and ny are normally bigger than that ( between 200 and 1000 usually ) so I don’t know if that would be an issue

You would only use a StaticArray to replace the first dimension `1:4`. That is, you would have a 2d (x by y) array of 4-component SVectors. This should enormously speed up your 4-component vector operations, which you are currently doing by slicing.

---

<div class="post-metadata">

**Author:** ![Luis\_Alberto\_R](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/luis_alberto_r/32/51629_2.png) [@Luis\_Alberto\_R](https://discourse.julialang.org/u/Luis_Alberto_R)\
**Post date:** [July 19, 2023, 1:04am UTC](https://discourse.julialang.org/t/optimizing-nested-loops-with-conditional-inside/101724/8 "2023-07-19T01:04:40Z")

</div>

Ok so I tried using the `ifelse` like so:

```julia
@turbo for i in 1:nx+1
        for j in 1:ny+1
            @views p2f(FL,UPrim[:,i,j])
            @views p2f(FR,UPrim[:,i+1,j])
            @views F[:,i,j] = (sr[i,j]*FL[:] .- sl[i,j]*FR[:] .+ sl[i,j]*sr[i,j]*(U[:,i+1,j] .- U[:,i,j]))./(
                sr[i,j]-sl[i,j])
            @views F[:,i,j] = ifelse(sr[i,j] <= 0,FR[:],F[:,i,j])
            @views F[:,i,j] = ifelse(sl[i,j] >= 0,FL[:],F[:,i,j])
            @views p2g(GL,UPrim[:,i,j])
            @views p2g(GR,UPrim[:,i,j+1])
            @views G[:,i,j] = (su[i,j]*GL[:] .- sd[i,j]*GR[:] .+ sd[i,j]*su[i,j]*(U[:,i,j+1] .- U[:,i,j]))./(
                su[i,j]-sd[i,j])
            @views G[:,i,j] = ifelse(su[i,j] <= 0, GR[:], G[:,i,j])
            @views G[:,i,j] = ifelse(sd[i,j] >= 0, GL[:], G[:,i,j])
        end
    end

```

but apparently turbo cant handle the p2f and p2g functions, any idea why?

```julia
LoadError: Don't know how to handle expression.
p2f
in expression starting at In[23]:7

```

---

<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 19, 2023, 1:07am UTC](https://discourse.julialang.org/t/optimizing-nested-loops-with-conditional-inside/101724/9 "2023-07-19T01:07:28Z")

</div>

> [@Luis\_Alberto\_R](#):
>
> ```julia
> F[:,i,j] = (sr[i,j]*FL[:] .- sl[i,j]*FR[:] .+ sl[i,j]*sr[i,j]*(U[:,i+1,j] .- U[:,i,j]))./(
> sr[i,j]-sl[i,j])
> 
> ```

Note that the right hand side of this allocates a new array because you aren’t using `.=`. An even better fix is to use SVector as I suggested above.

---

<div class="post-metadata">

**Author:** ![Luis\_Alberto\_R](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/luis_alberto_r/32/51629_2.png) [@Luis\_Alberto\_R](https://discourse.julialang.org/u/Luis_Alberto_R)\
**Post date:** [July 19, 2023, 3:18am UTC](https://discourse.julialang.org/t/optimizing-nested-loops-with-conditional-inside/101724/10 "2023-07-19T03:18:24Z")

</div>

Oh, I didn’t think of that, I’ll try that thx

---

<div class="post-metadata">

**Author:** ![Luis\_Alberto\_R](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/luis_alberto_r/32/51629_2.png) [@Luis\_Alberto\_R](https://discourse.julialang.org/u/Luis_Alberto_R)\
**Post date:** [July 19, 2023, 3:30am UTC](https://discourse.julialang.org/t/optimizing-nested-loops-with-conditional-inside/101724/11 "2023-07-19T03:30:34Z")

</div>

> [@stevengj](#):
>
> If at all possible, I generally try to put these conditionals outside the loops: divide your domain into rectangular “chunks” where the conditionals are constants.

Unfortunately I have to check the condition in all the domain to be sure if the flow of the conserved variables come from the left or right cells (up or down also) and since I’m doing the code to try to see the evolution of gas in presence of several explosions, I don’t know where the conditions are fulfilled.
