Type-instability because of @threads boxing variables

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

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.

2 Likes