I am learning parallel in Julia. And I saw below code of summing an array.
Background:
using Distributed
using DistributedArrays
addprocs(4)
@everywhere using DistributedArrays
a = rand(10^7)
adist = distribute(a)
The parallel function:
function mysum_dist(a::DArray)
r = Array{Future}(undef, length(procs(a)))
for (i, id) in enumerate(procs(a))
r[i] = @spawnat id sum(localpart(a))
end
return sum(fetch.(r))
end
Why do we use @spawnat and fetch here?
I think we can do it without @spawnat and fetch:
###WRONG!!! localpart(a) in local is empty.
function mysum_dist2(a::DArray)
r = Array{Float64}(undef, length(procs(a)))
for (i, id) in enumerate(procs(a))
r[i] = sum(localpart(a))
end
return sum(r)
end
Could you please tell me what’s the difference between the above functions?
How does @spawnat and fetch work?
When I timing those two function, they have similar time:
For @benchmark mysum_dist($adist):
BenchmarkTools.Trial:
memory estimate: 19.20 KiB
allocs estimate: 531
--------------
minimum time: 3.537 ms (0.00% GC)
median time: 4.989 ms (0.00% GC)
mean time: 5.045 ms (0.00% GC)
maximum time: 10.120 ms (0.00% GC)
--------------
samples: 989
evals/sample: 1
Did you check if the two versions return the same result? For me, mysum_dist2 returns zero.
When you create a distributed array, the whole data is distributed to the worker process, which is why
However, using the @spawnat macro you can run a command on a certain worker, so
julia> r = @spawnat 2 localpart(adist)
Future(2, 1, 115, nothing)
will run the localpart function on the worker with id 2. However, the result of the function only exists on the worker and only after the function has completed, so we can call fetch which will wait for the operation to finish and then copies the data from the worker back to the main process.
which is the part of the distributed array that belongs to worker 2. Because transferring data between processes is slow, you want to do the reduction (calling sum) on the worker and then only transfer the result back, which is a single number and
I also found why @spawnat and fetch() can do parallel computing.
The code below will print “last” immediately, which means the for-loop is running immediately. This is because @spawnat only assigns tasks and it assigns to each worker almost at the same time. However, each workers will doing sleep(10);rand() simultaneously.
for i in 1:3
r = @spawnat i (sleep(10);rand())
end
println("last")