# Behavior of \`Threads.@threads for\` loop

**URL:** https://discourse.julialang.org/t/behavior-of-threads-threads-for-loop/76042
**Category:** General Usage
**Tags:** multithreading
**Created:** [February 8, 2022, 7:31pm UTC](https://discourse.julialang.org/t/behavior-of-threads-threads-for-loop/76042 "2022-02-08T19:31:36Z")
**Posts on this page:** 11
**Page:** 2

<div class="post-metadata">

### Author: ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)
#### Post date: [February 14, 2022, 10:18am UTC](https://discourse.julialang.org/t/behavior-of-threads-threads-for-loop/76042/21 "2022-02-14T10:18:14Z")

</div>

This is my attempt for clarifying the behavior of the new default scheduling (aka `:dynamic`):

[https://github.com/JuliaLang/julia/pull/44168](https://github.com/JuliaLang/julia/pull/44168)

---

<div class="post-metadata">

### Author: ![ianshmean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ianshmean/32/216042_2.png) [@ianshmean](https://discourse.julialang.org/u/ianshmean)
#### Post date: [February 14, 2022, 10:33am UTC](https://discourse.julialang.org/t/behavior-of-threads-threads-for-loop/76042/22 "2022-02-14T10:33:52Z")

</div>

What if there was a new mechanic like this made up `BufferCache`

```julia
buffers = Base.BufferCache(makebuf, nthreads())
Threads.@threads x in xs 
    Base.acquire(buffers) do buf
        # do something with x and buf
    end
end

```

---

<div class="post-metadata">

### Author: ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)
#### Post date: [February 14, 2022, 11:08am UTC](https://discourse.julialang.org/t/behavior-of-threads-threads-for-loop/76042/23 "2022-02-14T11:08:06Z")

</div>

It is very inefficient to (re)acquire and release the lock for each iteration. If you don’t mind `O(nthreads())` allocations, I recommend `FLoops.@init` [Efficient and safe approaches to mutation in data parallelism](https://juliafolds.github.io/data-parallelism/tutorials/mutations/#mutable_temporary_objects_private_variables) (which also works on Distributed and GPU loops). If you want a more package-free approach, have a look at the worker pool pattern in [Concurrency patterns for controlled parallelisms](https://juliafolds.github.io/data-parallelism/tutorials/concurrency-patterns/) where something like `BufferCache` can be used but only with one acquirer/release for each task.

---

<div class="post-metadata">

### Author: ![antoine-levitt](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/antoine-levitt/32/4008_2.png) [@antoine-levitt](https://discourse.julialang.org/u/antoine-levitt)
#### Post date: [February 14, 2022, 12:00pm UTC](https://discourse.julialang.org/t/behavior-of-threads-threads-for-loop/76042/24 "2022-02-14T12:00:42Z")

</div>

@tkf was kind enough to suggest a solution to the pre-allocated problem in [https://github.com/JuliaMolSim/DFTK.jl/issues/588](https://github.com/JuliaMolSim/DFTK.jl/issues/588). Quoting from his post:

```julia
First of all, here's a trick you can use almost always. If you have this pattern

Threads.@threads for x in xs
    i = Threads.threadid()
    f(x, i)
end
you can mechanically convert this to

n = cld(length(xs), Threads.nthreads())
@sync for (i, chunk) in enumerate(Iterators.partition(xs, n))
    Threads.@spawn for x in chunk
        f(x, i)
    end
end

This is very likely correct if the loop body f only uses threadid() with arrays allocated only for this parallel loop (e.g., pre-1.3 reduction pattern).

```

The idea is to handle the range splitting into chunks yourself, and use per-chunk (rather than per-thread) buffers.

---

<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: [February 14, 2022, 1:54pm UTC](https://discourse.julialang.org/t/behavior-of-threads-threads-for-loop/76042/25 "2022-02-14T13:54:16Z")

</div>

I have been using this pattern in some code:

```julia
nchunks = Threads.nthreads() # not necessarily, but most commonly
@sync for ichunck in 1:nchuncks
    Threads.@spawn for i in ichunck:nchuncks:length(x)
        f(x[i],ichunck)
    end
end

```

The difference is that this patterns makes the access to the elements of `x` not contiguous (one iterates jumping over in steps of size `nchuncks`. This is simple but probably is worse if the access of the elements of `x` is a bottleneck.

What I did observe, and that may be an useful addition here, is that when using these patterns, one can have a performance advantage by setting `nchucks > nthreads()`, because sometimes one chunck gets overloaded, or stalled because of hardware stuff, and that improves load balancing.

---

<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: [February 14, 2022, 2:06pm UTC](https://discourse.julialang.org/t/behavior-of-threads-threads-for-loop/76042/26 "2022-02-14T14:06:32Z")

</div>

one problem is I don’t want my users to NEED to know all this in order to have multi-threading (they are physicists who barely know what is a macro and Julia would be a “sell” from me…)

one possible workaround I know is to use `Polyester.jl` or well, write a new macro within the package, but I wish `@threads` would just work since the underlying logic is really as stupid as possible…

---

<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: [February 14, 2022, 2:24pm UTC](https://discourse.julialang.org/t/behavior-of-threads-threads-for-loop/76042/27 "2022-02-14T14:24:03Z")

</div>

I agree. Couldn’t there be an even simpler syntax for that, that handled that properly by converting the loop into the pattern suggested above? Something even more “natural”, as:

```julia
@parallel nchuncks = nthreads() for i in 1:length(x)
     result[chunck_index()] = ...
end

```

where the macro just reinterprets that to something safe?

---

<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: [February 14, 2022, 2:32pm UTC](https://discourse.julialang.org/t/behavior-of-threads-threads-for-loop/76042/28 "2022-02-14T14:32:02Z")

</div>

it’s more subtle than that, basically the current behavior (regardless of `:static` or `:dynamic`) agrees with `nchunks = nthreads()`, as tkf has said:

> [@tkf](#):
>
> No, `@threads` currently spawns only `nthreads()` tasks, and each task processes a contiguous region of the input. In the _current_ implementation, the task tree structure of `:dynamic` and `:static` is identital.

it’s just that `:dynamic` would allow a task, which is handling a contiguous chunk, to be run on a different OS thread at some point. `nchuncks = nthreads()` doesn’t seem to be an extra constrain

---

<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: [February 14, 2022, 2:33pm UTC](https://discourse.julialang.org/t/behavior-of-threads-threads-for-loop/76042/29 "2022-02-14T14:33:55Z")

</div>

Yes, I didn’t mean exactly the possibility of that option. The suggestion was because there it is clear that `chunck_index()` is something not necessarily bound to the threads, but to some arbitrary counter.

A manual entry about that would be quite explicit: “with `nchuncks=N` one sets on how many threads one wants to split the work, and a buffer split into `N` chuncks can be updated in a thread-safe manner using `chunck_index()`”.

---

<div class="post-metadata">

### Author: ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)
#### Post date: [February 14, 2022, 3:42pm UTC](https://discourse.julialang.org/t/behavior-of-threads-threads-for-loop/76042/30 "2022-02-14T15:42:26Z")

</div>

> [@jling](#):
>
> one problem is I don’t want my users to NEED to know all this in order to have multi-threading

Why not just provide `foreach`-like API?

---

<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: [February 14, 2022, 4:28pm UTC](https://discourse.julialang.org/t/behavior-of-threads-threads-for-loop/76042/31 "2022-02-14T16:28:10Z")

</div>

how does that solve the problem? I assume `foreach()` will still yield an lazy collection

[Previous page](https://discourse.julialang.org/t/behavior-of-threads-threads-for-loop/76042.md?page=1)
