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:
mapandmap!reducemapreducesumfindallfindfirstfindlastforeachsortsort!
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 estimation. We will create a function which “throws a dart” and returns a “hit” or a “miss” encoded as 1 or 0.
square(x) = x*x
throw_dart() = square(rand()) + square(rand()) <= 1We can create a lazy mapping of values using the pipe operator: |>. This array is then passed into the sum function:
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)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:
pi_est_transducers_t(n) = 4 * foldxt(+, 1:n |> Map(_ -> throw_dart())) / n;
@btime pi_est_transducers_t($n)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.