JuliaLang / Distributed.jl

Create and control multiple Julia processes remotely for distributed computing. Ships as a Julia stdlib.
https://docs.julialang.org/en/v1/stdlib/Distributed/
MIT License
20 stars 8 forks source link

Spurious `@spawnat` parallelism with single worker, single thread #13

Closed lukas-weber closed 5 months ago

lukas-weber commented 5 months ago

Consider the following

using Distributed

@everywhere function f()
    println("start")
    sleep(2)
    println("end")
end

fs = [@spawnat :any f() for _ = 1:4]

fetch.(fs)

When starting julia without any arguments, I see that nworkers() == 1 and Threads.num_threads() == 1, so I would expect the above example to run serially, since I have not provided any resources for parallel execution. What happens though is

start
start
start
start
[2 second wait]
end
end
end
end

This might be intended behavior, but to me it is quite confusing and inconvenient since I seemingly have no way to regulate the resource consumption with this setup. Has it always been like this?

vchuravy commented 5 months ago

This is very much expected behavior.

Julia's parallelism is fundamentally build around tasks (c.f. https://docs.julialang.org/en/v1/manual/parallel-computing/)

With @spawnat you launch 4 tasks to be executed on the worker the task runtime will pick them up and run them. The asynchronous execution is part of the IO subsystem.

This is equivalent to:

function f()
     println("start")
     sleep(2)
     println("end")
 end

fs = [Base.Threads.@spawn f() for _ = 1:4]
fetch.(fs)

Which will have the same behavior.

lukas-weber commented 5 months ago

Thanks for the quick answer! Sorry for the confusion, for a quick second, I forgot that concurrent execution on a single thread is a thing!