# Looking for advice on threading

**URL:** <https://discourse.julialang.org/t/looking-for-advice-on-threading/33301>\
**Category:** General Usage\
**Created:** [January 13, 2020, 1:51pm UTC](https://discourse.julialang.org/t/looking-for-advice-on-threading/33301 "2020-01-13T13:51:56Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Paul\_Soderlind](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/paul_soderlind/32/1753_2.png) [@Paul\_Soderlind](https://discourse.julialang.org/u/Paul_Soderlind)\
**Post date:** [January 13, 2020, 1:51pm UTC](https://discourse.julialang.org/t/looking-for-advice-on-threading/33301/1 "2020-01-13T13:51:56Z")

</div>

Are there any (fairly simple) examples of how to work with `Threads.@threads`?

Background;: so far I have tried splitting up my computations in threaded chunks and eventually recombine the results from the chunks, similar in spirit to the “partial sums” below. It reduces my computation time from 10 minutes to 1.5 minutes on the office desktop, but it’s not so pretty. Is there a better strategy? (Don’t take the partial sums example too literally.)

```julia
function fnPartialSum(x,t1,t2)
  y = 0.0
  for i = t1:t2
    y = y + x[i]
  end
  return y
end

function fnTotalSum(x,nChunks)
  T = length(x)
  m = cld(T,nChunks) #no. elements in each chunk
  xSum = zeros(nChunks) #pre-allocate space for partial sums
  Threads.@threads for i = 1:nChunks #do nChunks partial sums
    xSum[i] = fnPartialSum(x,1+(i-1)*m,min(i*m,T))
  end
  Sum = sum(xSum)
  return Sum
end

x = rand(999)
r1 = fnTotalSum(x,3) #use 3 chunks (threaded)
r2 = fnPartialSum(x,1,length(x)) #compare with no-thread
println("$r1 $r2")

```

---

<div class="post-metadata">

**Author:** ![Jakub\_Wronowski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakub_wronowski/32/204030_2.png) [@Jakub\_Wronowski](https://discourse.julialang.org/u/Jakub_Wronowski)\
**Post date:** [January 13, 2020, 2:33pm UTC](https://discourse.julialang.org/t/looking-for-advice-on-threading/33301/2 "2020-01-13T14:33:00Z")

</div>

Search for “map reduce” design pattern.

> [@Parallel reductions](https://discourse.julialang.org/t/parallel-reductions/30180):
>
> “Parallel mapreduce” is a really common need, and it’s surprisingly hard to find a Julia implementation. Say we have julia\> x = randn(10000000); julia\> f = log∘sqrt∘abs #56 (generic function with 1 method) The goal is to build a fast, parallel work-alike to julia\> @btime sum($f.($x)) 150.037 ms (2 allocations: 76.29 MiB) -3.1741049251367934e6 julia\> @btime mapreduce($f,$+,$x) 135.280 ms (0 allocations: 0 bytes) -3.1741049251367934e6 When I asked this on Julia Slack #multithreading, @a…

> [@Map and mapreduce with Threads](https://discourse.julialang.org/t/map-and-mapreduce-with-threads/31861):
>
> I would like to experiment with parallelizing an embarrassingly parallel computation on an iterable using [the new multithreading in 1.3](https://julialang.org/blog/2019/07/multithreading). I need map and mapreduce. I would prefer to leave return type determination to Julia, so I would prefer not to preallocate. Do I just need to @spawn and then fetch, as in using Base.Threads: @spawn, fetch, threadid, nthreads @show nthreads() function ploop(f, itr) map(fetch, map(i -\> @spawn(f(i)), itr)) end function pmapreduce(f, op, itr) mapreduc…

Anyway 6x on desktop seems to be very good for me.

---

<div class="post-metadata">

**Author:** ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)\
**Post date:** [January 14, 2020, 8:42pm UTC](https://discourse.julialang.org/t/looking-for-advice-on-threading/33301/3 "2020-01-14T20:42:24Z")

</div>

In addition to @Jakub_Wronowski’s very good answer, you may want to know that `Threads.@threads` can directly be used to parallelize the summation loop (which spares you the pain of computing the correct partition of the indices range).

Something like this yields approximately the same performances as your threaded version, but is perhaps more readable:

```julia
function fnSum(x)
    # Array holding one partial sum per thread
    partialSum = zeros(eltype(x), Threads.nthreads())

    # Threads.@threads directly parallelizes the loop over elements in x
    Threads.@threads for e in x
        @inbounds partialSum[Threads.threadid()] += e
    end

    # Final reduction
    sum(partialSum)
end

```

Some benchmarking (on my machine with `JULIA_NUM_THREADS=4`, matching the number of cores, and with a large enough vector):

```julia
julia> using BenchmarkTools

julia> let
           x = rand(1_000_000);
           r1 = @btime fnTotalSum($x,Threads.nthreads()) # threaded
           r2 = @btime fnPartialSum($x,1,$(length(x))) # not threaded
           r3 = @btime fnSum($x) # threaded
       
           println("$r1\n$r2\n$r3")
       end

# Benchmark
  405.389 μs (31 allocations: 3.16 KiB) # fnTotalSum (threaded)
  1.444 ms (0 allocations: 0 bytes) # fnPartialSum (not threaded)
  409.454 μs (31 allocations: 3.13 KiB) # fnSum (threaded)

# Results
499823.13580023544 # fnTotalSum (threaded)
499823.13580021414 # fnPartialSum (not threaded)
499823.13580023544 # fnSum (threaded)

```

  

* * *
  

PS: I sprinkled a few `@inbounds` here and there to try and benchmark comparable implementations. Here is the complete script I used:

> **Script**
>
> ```julia
> function fnPartialSum(x,t1,t2)
> y = zero(eltype(x))
> @inbounds for i = t1:t2
> y = y + x[i]
> end
> return y
> end
> 
> function fnTotalSum(x,nChunks)
> T = length(x)
> m = cld(T,nChunks) #no. elements in each chunk
> xSum = zeros(nChunks) #pre-allocate space for partial sums
> Threads.@threads for i = 1:nChunks #do nChunks partial sums
> @inbounds xSum[i] = fnPartialSum(x,1+(i-1)*m,min(i*m,T))
> end
> Sum = sum(xSum)
> return Sum
> end
> 
> function fnSum(x)
> partialSum = zeros(eltype(x), Threads.nthreads())
> 
> Threads.@threads for e in x
> @inbounds partialSum[Threads.threadid()] += e
> end
> 
> sum(partialSum)
> end
> 
> using BenchmarkTools
> 
> let
> x = rand(1_000_000);
> r1 = @btime fnTotalSum($x,Threads.nthreads()) #use 3 chunks (threaded)
> r2 = @btime fnPartialSum($x,1,$(length(x))) #compare with no-thread
> r3 = @btime fnSum($x)
> 
> println("$r1\n$r2\n$r3")
> 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:** [January 14, 2020, 10:26pm UTC](https://discourse.julialang.org/t/looking-for-advice-on-threading/33301/4 "2020-01-14T22:26:28Z")

</div>

FYI, with Transducers.jl (see [Thread- and process-based parallelisms in Transducers.jl (+ some news)](https://discourse.julialang.org/t/thread-and-process-based-parallelisms-in-transducers-jl-some-news/32285)), it’s `reduce(+, Map(identity), x; basesize=length(x) ÷ nChunks)`.

> [@Paul\_Soderlind](#):
>
> `xSum = zeros(nChunks)`

> [@ffevotte](#):
>
> `partialSum = zeros(eltype(x), Threads.nthreads())`

I find this kind of approaches limiting as it’s impossible to write a parallel version of `sum(f, xs)` this way without relying on compiler internal (aka `Core.Compiler.return_type`).

> [@ffevotte](#):
>
> `@inbounds partialSum[Threads.threadid()] += e`

I think this pattern would invoke [false sharing](https://en.wikipedia.org/wiki/False_sharing) and could be bad for performance.

---

<div class="post-metadata">

**Author:** ![Paul\_Soderlind](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/paul_soderlind/32/1753_2.png) [@Paul\_Soderlind](https://discourse.julialang.org/u/Paul_Soderlind)\
**Post date:** [January 15, 2020, 10:01am UTC](https://discourse.julialang.org/t/looking-for-advice-on-threading/33301/5 "2020-01-15T10:01:27Z")

</div>

Thanks. I’ve not worked with Transducers.jl, so just a quick question: can Transducers.jl handle more complicated functions (instead of summing, suppose it’s lots of linear algebra)?

---

<div class="post-metadata">

**Author:** ![Jakub\_Wronowski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakub_wronowski/32/204030_2.png) [@Jakub\_Wronowski](https://discourse.julialang.org/u/Jakub_Wronowski)\
**Post date:** [January 15, 2020, 5:14pm UTC](https://discourse.julialang.org/t/looking-for-advice-on-threading/33301/6 "2020-01-15T17:14:14Z")

</div>

They are generic, you can pass any function.  
Remember, that to use map reduce pattern your function has to have reduction property `f(a, f(b, c)) === f(f(a,b),c)`. I am not expert here, maybe there are workarounds for that I am no aware.

---

<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:** [January 15, 2020, 9:25pm UTC](https://discourse.julialang.org/t/looking-for-advice-on-threading/33301/7 "2020-01-15T21:25:47Z")

</div>

As Jakub\_Wronowski said, `reduce(op, xf, input)` should work as long as the function `op` is associative and the transducer `xf` is stateless. Maybe easier form to use is `reduce(op, eduction(iterator_comprehension))` where `iterator_comprehension` is, for example, something like `(f(x) for x in input if p(x))`. More explanations in the tutorial: [https://tkf.github.io/Transducers.jl/dev/examples/tutorial\_parallel/](https://tkf.github.io/Transducers.jl/dev/examples/tutorial_parallel/)
