Skip to content

Make @threads work on array comprehensions #59019

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

IanButterworth
Copy link
Member

@IanButterworth IanButterworth commented Jul 16, 2025

Closes #209

Simple array comprehensions:

% julia -t6 -q
julia> using StressTest; f(x) = (busywait(0.01); x);

julia> @time a = [f(i) for i in 1:1000];
 10.014840 seconds (40.87 k allocations: 2.045 MiB, 0.15% compilation time)

julia> @time b = Threads.@threads [f(i) for i in 1:1000];
  1.710031 seconds (65.02 k allocations: 3.276 MiB, 6.69% compilation time)

julia> a == b
true

For length-less iterators :greedy works, but order is not preserved:

julia> ch = Channel{Int}(1000); foreach(i-> push!(ch, i), 1:1000); close(ch);

julia> @time c = Threads.@threads :greedy [f(i) for i in ch];
  1.774034 seconds (221.50 k allocations: 11.282 MiB, 472 lock conflicts, 12.77% compilation time)

julia> a == c
false

julia> a[1:5], c[1:5]
([1, 2, 3, 4, 5], Any[1, 7, 13, 19, 25])

With filtering:

julia> @time af = [f(i) for i in 1:1000 if iseven(i)];
  5.024671 seconds (50.23 k allocations: 2.517 MiB, 0.49% compilation time)

julia> @time bf = Threads.@threads [f(i) for i in 1:1000 if iseven(i)];
  0.875857 seconds (56.77 k allocations: 2.956 MiB, 10.46% compilation time)

julia> af == bf
true

julia> ch = Channel{Int}(1000); foreach(i-> push!(ch, i), 1:1000); close(ch);

julia> @time cf = Threads.@threads :greedy [f(i) for i in ch if iseven(i)];
  0.912532 seconds (106.89 k allocations: 5.317 MiB, 313 lock conflicts, 14.93% compilation time)

julia> af[1:5], cf[1:5]
([2, 4, 6, 8, 10], Any[6, 16, 26, 42, 56])

Developed with help from Claude.

@IanButterworth IanButterworth added the multithreading Base.Threads and related functionality label Jul 16, 2025
@IanButterworth IanButterworth added parallelism Parallel or distributed computation macros @macros labels Jul 16, 2025
@adienes
Copy link
Member

adienes commented Jul 16, 2025

the fact that this always returns a Vector{Any} is a bit rough

just some further cases that might want to be handled:

multiple loops

julia> @threads [i + j for i in 1:3, j in 1:3]
ERROR: TaskFailedException

non-indexable iterators

julia> @threads [i for i in Iterators.flatten(1:3)]
ERROR: TaskFailedException

@ericphanson
Copy link
Contributor

ericphanson commented Jul 17, 2025

it seems a little odd to me that :greedy does not preserve order. It seems mostly the different flavors change performance characteristics without changing semantics (or only changing semantics when you rely on details like threadids), but unordered is a pretty big semantic change.

Could it be called :unordered or :greedy_unordered or something to make it explicit? Or alternatively, could we just order it post-hoc by passing the index along?

(BTW: feels like there might be some similarity to Threads.foreach(f, c::Channel) from #34543 which I think is still not a well-known feature)

@IanButterworth
Copy link
Member Author

If Threads.foreach(f, c::Channel) could gain a filter kwarg, it might be worth just making this a wrapper for that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
macros @macros multithreading Base.Threads and related functionality parallelism Parallel or distributed computation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

add comprehension support to @threads
3 participants