# Spurious allocations with @threads, when setting variables using if statements

**URL:** <https://discourse.julialang.org/t/spurious-allocations-with-threads-when-setting-variables-using-if-statements/82233>\
**Category:** General Usage\
**Tags:** threads, potential-bug\
**Created:** [June 4, 2022, 11:50am UTC](https://discourse.julialang.org/t/spurious-allocations-with-threads-when-setting-variables-using-if-statements/82233 "2022-06-04T11:50:38Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![lukas.grunwald](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lukas.grunwald/32/36708_2.png) [@lukas.grunwald](https://discourse.julialang.org/u/lukas.grunwald)\
**Post date:** [June 4, 2022, 11:50am UTC](https://discourse.julialang.org/t/spurious-allocations-with-threads-when-setting-variables-using-if-statements/82233/1 "2022-06-04T11:50:38Z")

</div>

Annotating a for loop with @threads, that depends on a variable fixed in an if statement, leads to many additional allocations inside the loop. **Replacing the if-statement with a ternary-operator removes these additional allocations**.  
Minimal example:

```julia
function g!(f::AbstractVector, flag::Bool, dx::Float64, dy::Float64)
    Δ = 0.0 # Not really needed, since if-statement does not introduce new scope
    if flag Δ = dy else Δ = dx end

    @inbounds Threads.@threads for i in eachindex(f)
        f[i] = Δ * f[i]
    end
    return nothing
end

function g_ternary!(f::AbstractVector, flag::Bool, dx::Float64, dy::Float64)
    Δ = flag ? dy : dx

    @inbounds Threads.@threads for i in eachindex(f)
        f[i] = Δ * f[i]
    end
    return nothing
end

using BenchmarkTools
N, flag = 2^16, true
y = rand(ComplexF64, N)
dx, dy = rand(2)

@btime g!($y, $flag, $dx, $dy) # 259.165 μs (196140 allocations: 5.00 MiB)
@btime g_ternary!($y, $flag, $dx, $dy) # 7.934 μs (41 allocations: 3.86 KiB)
# Note: The 41 allocations are normal and appear due to @threads

```

Removing @threads, both variants become non-allocating, so the problem must lie somewhere in the multithreading.  
Did I understand something wrong or is this a bug?

```julia
julia> versioninfo()
Julia Version 1.7.3
Commit 742b9abb4d (2022-05-06 12:58 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: 11th Gen Intel(R) Core(TM) i7-11850H @ 2.50GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-12.0.1 (ORCJIT, tigerlake)
Environment:
  JULIA_NUM_THREADS = 8
```

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [June 4, 2022, 4:41pm UTC](https://discourse.julialang.org/t/spurious-allocations-with-threads-when-setting-variables-using-if-statements/82233/2 "2022-06-04T16:41:51Z")

</div>

```julia
julia> @code_warntype g!(y, flag, dx, dy)
MethodInstance for g!(::Vector{ComplexF64}, ::Bool, ::Float64, ::Float64)
  from g!(f::AbstractVector, flag::Bool, dx::Float64, dy::Float64) in Main at /tmp/foo.jl:51
Arguments
  #self#::Core.Const(g!)
  f::Vector{ComplexF64}
  flag::Bool
  dx::Float64
  dy::Float64
Locals
  threadsfor_fun::var"#150#threadsfor_fun#19"{var"#150#threadsfor_fun#18#20"{Vector{ComplexF64}, Base.OneTo{Int64}}}
  Δ::Core.Box

```

`Δ` is boxed. `Δ = if flag dy else dx end` matches more closely your ternary operator and with this change performance is identical for me for `g!` and `g_ternary!`

---

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [June 4, 2022, 5:14pm UTC](https://discourse.julialang.org/t/spurious-allocations-with-threads-when-setting-variables-using-if-statements/82233/3 "2022-06-04T17:14:18Z")

</div>

Why is it boxed, though? Is it because the variable is being reassigned conditionally?

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [June 4, 2022, 5:21pm UTC](https://discourse.julialang.org/t/spurious-allocations-with-threads-when-setting-variables-using-if-statements/82233/4 "2022-06-04T17:21:55Z")

</div>

it’s weird that it’s boxed only when `@threads` is used:

```julia
julia> function g(flag::Bool, dx::Float64, dy::Float64)
           Δ = 0.0
           if flag Δ = dy else Δ = dx end
           return Δ
       end

julia> @code_warntype g(true, 3.0, 1.0)
MethodInstance for g(::Bool, ::Float64, ::Float64)
  from g(flag::Bool, dx::Float64, dy::Float64) in Main at REPL[1]:1
Arguments
  #self#::Core.Const(g)
  flag::Bool
  dx::Float64
  dy::Float64
Locals
  Δ::Float64
Body::Float64
1 ─ (Δ = 0.0)
└── goto #3 if not flag
2 ─ (Δ = dy)
└── goto #4
3 ─ (Δ = dx)
4 ┄ return Δ

julia> function g(flag::Bool, dx::Float64, dy::Float64)
           Δ = 0.0
           if flag Δ = dy else Δ = dx end
           Threads.@threads for _ = 1:10
               f = Δ
           end
           return Δ
       end
g (generic function with 1 method)

julia> @code_warntype g(true, 3.0, 1.0)
MethodInstance for g(::Bool, ::Float64, ::Float64)
  from g(flag::Bool, dx::Float64, dy::Float64) in Main at REPL[3]:1
Arguments
  #self#::Core.Const(g)
  flag::Bool
  dx::Float64
  dy::Float64
Locals
  threadsfor_fun::var"#15#threadsfor_fun#2"{var"#15#threadsfor_fun#1#3"{UnitRange{Int64}}}
  Δ@_6::Core.Box
  threadsfor_fun#1::var"#15#threadsfor_fun#1#3"{UnitRange{Int64}}
  range::UnitRange{Int64}
  Δ@_9::Union{}
Body::Any

```

I don’t quite understand why compiler thinks `\Delta` may get modified in the loop

---

<div class="post-metadata">

**Author:** ![lukas.grunwald](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lukas.grunwald/32/36708_2.png) [@lukas.grunwald](https://discourse.julialang.org/u/lukas.grunwald)\
**Post date:** [June 6, 2022, 12:40pm UTC](https://discourse.julialang.org/t/spurious-allocations-with-threads-when-setting-variables-using-if-statements/82233/5 "2022-06-06T12:40:48Z")

</div>

So the type instability explains where the allocations come from.  
It’s somewhat strange that its only unstable in combination with @threads but since we have a workaround, @giordano 's answer resolves the issue for me 👍  
Thanks a lot everyone!

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [June 6, 2022, 12:45pm UTC](https://discourse.julialang.org/t/spurious-allocations-with-threads-when-setting-variables-using-if-statements/82233/6 "2022-06-06T12:45:27Z")

</div>

This is probably the infamous bug of the performance penalty of closures, related to `@threads` creating a closure.

See: [Type-instability because of @threads boxing variables](https://discourse.julialang.org/t/type-instability-because-of-threads-boxing-variables/78395)
