# Type-instability because of @threads boxing variables

**URL:** <https://discourse.julialang.org/t/type-instability-because-of-threads-boxing-variables/78395>\
**Category:** Performance\
**Tags:** parallel\
**Created:** [March 24, 2022, 2:17pm UTC](https://discourse.julialang.org/t/type-instability-because-of-threads-boxing-variables/78395 "2022-03-24T14:17:45Z")\
**Posts on this page:** 4\
**Page:** 2

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [March 25, 2022, 12:27pm UTC](https://discourse.julialang.org/t/type-instability-because-of-threads-boxing-variables/78395/21 "2022-03-25T12:27:13Z")

</div>

Yes.  
You could try adding `let` blocks.  
Note that `Threads.@spawn` has sugar where using `$` on variables automatically creates let blocks for you:

```julia
julia> @macroexpand Threads.@spawn foo($x)
quote
    #= threadingconstructs.jl:229 =#
    let var"##394" = x
        #= threadingconstructs.jl:230 =#
        local var"#14#task" = Base.Threads.Task((()->begin
                            #= threadingconstructs.jl:226 =#
                            foo(var"##394")
                        end))
        #= threadingconstructs.jl:231 =#
        (var"#14#task").sticky = false
        #= threadingconstructs.jl:232 =#
        if $(Expr(:islocal, Symbol("##sync#48")))
            #= threadingconstructs.jl:233 =#
            Base.Threads.put!(var"##sync#48", var"#14#task")
        end
        #= threadingconstructs.jl:235 =#
        Base.Threads.schedule(var"#14#task")
        #= threadingconstructs.jl:236 =#
        var"#14#task"
    end
end

```

But this isn’t supported for `Threads.@threads`, although it’s probably easy enough to manually create `let` blocks, unless you have an absurd number of variables (fun fact: `let` used to be `O(N^2)` in number of variables, until very recent versions of Julia [1.8+?], where it is now `O(N)`, thanks to some code with thousands of `let` causing this to actually be problematic). This is the classic workaround.

You could also manually chunk your iteration range, perhaps separating the code that iterates over a chunk into a separate function, and use `@spawn`. I also often do this anyway when I want more control over task-local state, e.g. rngs. `@spawn` is of course more dynamic than `@threads`, which you may or may not want.

---

<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:** [March 25, 2022, 1:26pm UTC](https://discourse.julialang.org/t/type-instability-because-of-threads-boxing-variables/78395/22 "2022-03-25T13:26:49Z")

</div>

I’m actually using `@spawn` in my code. Thanks for the tip.

I think that will be the best workaround here.

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [March 25, 2022, 10:14pm UTC](https://discourse.julialang.org/t/type-instability-because-of-threads-boxing-variables/78395/23 "2022-03-25T22:14:44Z")

</div>

Regardless of whether or not there are performance implications, I would still strongly prefer if mutating bindings.

```julia
julia> @testset "Closure" begin
       function foo(x)
         z = 2x
         return 3z
       end
       z = 3
       @test foo(z) == 18
       @test z == 3
       end
Closure: Test Failed at REPL[6]:8
  Expression: z == 3
   Evaluated: 6 == 3
Stacktrace:
 [1] macro expansion
   @ ~/Documents/languages/julia/usr/share/julia/stdlib/v1.9/Test/src/Test.jl:464 [inlined]
 [2] macro expansion
   @ REPL[6]:8 [inlined]
 [3] macro expansion
   @ ~/Documents/languages/julia/usr/share/julia/stdlib/v1.9/Test/src/Test.jl:1360 [inlined]
 [4] top-level scope
   @ REPL[6]:2
Test Summary: | Pass Fail Total Time
Closure | 1 1 2 0.3s
ERROR: Some tests did not pass: 1 passed, 1 failed, 0 errored, 0 broken.

```

This is just confusing.  
If you encounter this bug in the wild – without knowing to look for it – you might try copy/pasting it into the REPL, only to find out that the tests pass / you can’t reproduce it.  
Spooky heisenbugs at a distance.

When someone actually does want this behavior, it’d be much better to make it explicit via capturing and mutating a `RefValue` or some other mutable struct.

Still, would be nice for the performance gets fixed, too.

---

<div class="post-metadata">

**Author:** ![goerch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goerch/32/29122_2.png) [@goerch](https://discourse.julialang.org/u/goerch)\
**Post date:** [March 25, 2022, 11:38pm UTC](https://discourse.julialang.org/t/type-instability-because-of-threads-boxing-variables/78395/24 "2022-03-25T23:38:29Z")

</div>

> [@tkf](#):
>
> FWIW, `@floop` detects and warns this. You can also set [`FLoops.assistant(:error)`](https://juliafolds.github.io/FLoops.jl/dev/reference/api/#FLoops.assistant) (default ATM is `:warn` ) in a test suite to throw an error. See also [How to avoid Box · FLoops](https://juliafolds.github.io/FLoops.jl/dev/howto/avoid-box/) for a case-by-case how-to guide to workaround this.

Very interesting. My workflow regarding common performance pitfalls is usually asking a profiler and/or `JET.jl` for advice. Do you think the compiler itself should warn us somehow (this would resemble clang sanitizers)?

[Previous page](https://discourse.julialang.org/t/type-instability-because-of-threads-boxing-variables/78395.md?page=1)
