A while ago I posted FLoops.jl RFC that demonstrates alternative for
loop framework on top of Transducers.jl. FLoops.jl lets users write fast for
loops over complex data structures. Since it received quite positive reactions [*1], it made me think that it might be nice to provide a nice syntax sugar that can give people imperative style feeling on top of functional style composable framework. In particular, I’ve been trying to expose the parallel computing part of Transducers.jl via FLoops.jl. I think I finally found a nice syntax and included in the latest version of FLoops.jl. You can install it via using Pkg; Pkg.add("FLoops")
.
So, here is the syntax I came up. I’m actually interested in if it is readable without looking at the docs. What do you think?
@floop for (i, v) in pairs([0, 1, 3, 2]), (j, w) in pairs([3, 1, 5])
d = abs(v - w)
@reduce() do (dmax = -1; d), (imax = 0; i), (jmax = 0; j)
if isless(dmax, d)
dmax = d
imax = i
jmax = j
end
end
end
@show (dmax, imax, jmax)
This is one of the most “complex” examples from the README. I’m kind of hoping that it is actually readable without explaining what the API of the macros are.
For simpler cases, there are OpenMP-like shorthands:
@floop for (x, y) in zip(1:3, 1:2:6)
a = x + y
b = x - y
@reduce(s += a, t += b)
# @reduce(s = 0 + a, t = 0 + b) # with explicit initializer
end
@show (s, t)
For more information, have a look at the documentation.
Compared to Threads.@threads
, there are a few advantages of @floop
:
- It supports a large set of data collection types with SplittablesBase.jl interface, as listed in SplittablesBase.jl README. This includes lazy iterators like
Iterators.filter
andIterators.product
. Iterator comprehensions work too. - Flexible reduction support with
@reduce
syntax. - Loop execution options such as
basesize
,simd
, andnestlevel
for tweaking the performance (seefoldxt docstring
). - Automatic detection of a possible data-race pattern.
- Control flow supports; i.e.,
continue
,break
,return
, and@goto
work inside the loop body. - Pluggable executors (schedulers).
@floop
currently supports sequential (default when no@reduce
), threaded (default with@reduce
), and distributed executor backends. - Interop with the rest of Transducers.jl-related packages.
- Adhere to nested and composable parallelism of Julia.
Since @floop
is built on top of the extensible fold API of Transducers.jl, it is possible to add, for example, another execution backend. It was quite easy to hack up Dagger.jl-based fold that can be used via Transducers.jl API including @floop
: GitHub - JuliaFolds/FoldsDagger.jl. For sequential loops, I’m experimenting easier-to-use interfaces for some casual performance tweaks using SIMD (via SIMD.jl), prefetching, unrolling, etc. in LoopRecipes.jl that can be composed with @floop
.
[*1] This is an aside, but, to be honest, the reactions to FLoops.jl were a bit surprising. It was just a bunch of macros to call Transducers.jl API. It does not let you write anything new. But I guess that’s what Julia designers mean by language design is applied psychology. Sometimes syntax sugars do make a rather big difference.