@threads in array comprehension

This is just a feature suggestion. It would be nice it the @threads macro would work also inside array comprehensions as:

[i @threads for i=1:4]

I.e. that example should produce the same result as:

julia> [i for i=1:4]
4-element Array{Int64,1}:
 1
 2
 3
 4

Yet, currently it is not supported:

julia> [i @threads for i=1:4]
ERROR: syntax: invalid comprehension syntax

Thanks!

2 Likes

That’s a nice idea. Maybe open an issue on GitHub? https://github.com/JuliaLang/julia/issues/new

Why not @threads [i for i in 1:4]?

Because to my understanding the @threads macro applies to the for loop it stands before. This doesn’t come out in my example,but
[i @threads for i in 1:4 for j in 1:3]
would not be the same as:
[i for i in 1:4 @threads for j in 1:3].
In the first case, the parallelization would be over i, in the second case over j.

Done: @threads in array comprehension · Issue #32000 · JuliaLang/julia · GitHub

Yes, but wouldn’t it be nice to put the macro in front of a comprehension (or a broadcast) to have it run threaded?

@threads x = sin.(pi/L.*1:1500000) .+ cos.(3*pi/L.1:1500000)

There’s also a technical reason to prefer @threads [x for x in xs] — it can work without any parser changes.

julia> :([x @threads for x in xs])
ERROR: syntax: invalid comprehension syntax

julia> :(@threads [x for x in xs])
:(#= REPL[2]:1 =# @threads [x for x = xs])
2 Likes

Some broadcasts can already be multi-threaded:

julia> using Strided

julia> v = pi/50 .* (1:15_000_000) |> collect;

julia> @time x = sin.(v) .+ cos.(v);
  0.421032 seconds (12 allocations: 114.441 MiB, 13.27% gc time)

julia> @time x = @strided sin.(v) .+ cos.(v);
  0.188288 seconds (28 allocations: 114.442 MiB, 7.12% gc time)

It would indeed be nice to have something similar for comprehensions too.

6 Likes

Maybe then @threads could take a keyword argument e.g. something like

@threads indices=(i, j) [i for i=1:4, j=1:3, k=1:5]

As you brought up the topic of broadcast, this could maybe be analogically done like this

@threads dims=(1,2) B .= sin.(A) .+ cos.(A);

assuming A and B are both arrays with 2 or more dimensions.

I am going to make a broad statement and say
macros are the wrong tool for the job.
(the current use of @threads included)

thread_map(f, xs) is the right tool,
and we should have that. which would for your example me
thread_map(identity, 1:4).

Ontop of such functions it is easy to build some macro based DSL,
if you want.
Or to build an Array type that automatically dispatches to using multiple threads,
when using normal map
etc.

2 Likes

Do you (or anyone else) know why @threads was implemented as a macro instead of as a function?

1 Like

If anyone is interested in such a threaded map, an implementation is available here
https://github.com/mohamed82008/KissThreading.jl

1 Like