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.
Threads.@threads, there are a few advantages of
- 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.product. Iterator comprehensions work too.
- Flexible reduction support with
- Loop execution options such as
nestlevelfor tweaking the performance (see
- Automatic detection of a possible data-race pattern.
- Control flow supports; i.e.,
@gotowork inside the loop body.
- Pluggable executors (schedulers).
@floopcurrently 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.
@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: https://github.com/JuliaFolds/DaggerFolds.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
[*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.