Parallelism
We have seen how the spawn
expression allow us to evaluate an expression in another thread:
spawn (1 + 2)
This allows us to write both concurrent and parallel programs.
The downside is that we must manually coordinate communication between threads using channels.
If want parallelism, but not concurrency, a more light-weight approach is to use the par
expression.
The par
expression:
par (1 + 2, 3 + 4)
evaluates 1 + 2
and 3 + 4
in parallel and returns a tuple with the result.
If we have expressions e1
, e2
, and e3
and we want evaluate them in parallel, we can write:
let (x, y, z) = par (e1, e2, e3)
which will spawn a thread for each of e1
, e2
, and e3
and bind the result to the local variables x
, y
, and z
.
For convenience, Flix also offers the par-yield
construct:
par (x <- e1; y <- e2; z <- e3)
yield x + y + z
which evaluates e1
, e2
, and e3
in parallel, binds their results to x
, y
, and z
, and returns their sum.
We can use par-yield
to write a parallel List.map
function:
def parMap(f: a -> b, l: List[a]): List[b] = match l {
case Nil => Nil
case x :: xs =>
par (r <- f(x); rs <- parMap(f, xs))
yield r :: rs
}
This function will evaluate f(x)
and parMap(f, xs)
in parallel. Thus each recursive call spawns a new thread.