JM

Table of Contents

Packages

A very helpful package for cleaning up the multithreaded implementations is the ThreadsX.jl package. This provides a few functions which can help to write concise code for implementing common operations in parallel. Some common, helpful operations are:

  • map and map!
  • reduce
  • mapreduce
  • sum
  • findall
  • findfirst
  • findlast
  • foreach
  • sort
  • sort!

Another package which can be used is Transducers.jl which allows for piping operations to be easily composed. Let’s take an example of the Monte-Carlo π\pi estimation. We will create a function which “throws a dart” and returns a “hit” or a “miss” encoded as 1 or 0.

julia
square(x) = x*x
throw_dart() = square(rand()) + square(rand()) <= 1

We can create a lazy mapping of values using the pipe operator: |>. This array is then passed into the sum function:

julia
using Transducers

square(x) = x*x
throw_dart() = square(rand()) + square(rand()) <= 1

n = Int(1e6);
pi_est_transducers(n) = 4 * sum(1:n |> Map(_ -> throw_dart())) / n;
@btime pi_est_transducers($n)
Output
2.512 ms (0 allocations: 0 bytes)

As sum is just a reduction operation, we can also use the generic, threaded, reduction method foldxt from Transducers.jl to perform the same operation in parallel:

julia
pi_est_transducers_t(n) = 4 * foldxt(+, 1:n |> Map(_ -> throw_dart())) / n;
@btime pi_est_transducers_t($n)
Output
355.526 μs (272 allocations: 9.22 KiB)

Notice that this is a nearly perfect speed-up, without having to use a chunking approach. Additionally, this uses much less code than before, while being of a similar performance.

Check out the documentation for both of these packages to see what they can be used for. However, it should be noted that most of this functionality is mostly only for syntax. One can write the same code in pure Julia, without using these packages.