Okaaay, as I am sure you can tell I am discovering this world together with you. So this error made me correct a few misconceptions I had about parallel programming. Here is the summary:
- When you call
@spawnat
, any error that happens on another process is not directly visible to you through the REPL. spawnat
itself is asynchronous so process 1 will continue to run after telling the other process what needs to be done. The errors are only visible when you fetch
the Future
that comes out of the @spawnat
which basically resembles the final return value or state of the function call. This is obvious from the following piece of code.
julia> r = @spawnat 2 throw("Error")
Future(2, 1, 56, Nullable{Any}())
julia> fetch(r)
ERROR: On worker 2:
"Error"
....truncated
-
If you fetch a Future twice, Julia will freeze. This is probably a bug as it should at least give an error.
-
A Future is only defined in process 1 when you declare it like that r = @spawnat .....
. Proof:
julia> r = @spawnat 2 rand(10)
Future(2, 1, 5, Nullable{Any}())
julia> fetch(@spawnat 2 isdefined(:r))
false
julia> fetch(@spawnat 1 isdefined(:r))
true
julia> fetch(@spawnat 1 names(Main))
5-element Array{Symbol,1}:
:Base
:Core
:Main
:ans
:r
julia> fetch(@spawnat 2 names(Main))
3-element Array{Symbol,1}:
:Base
:Core
:Main
- A variable is only defined in the
Main
of another process if preceded with global
. This may or may not be a bug, someone more knowledgeable will probably have to comment. Proof:
julia> fut1 = @spawnat 2 r = rand(100, 100);
julia> fetch(@spawnat 2 names(Main))
3-element Array{Symbol,1}:
:Base
:Core
:Main
julia> fut1 = @spawnat 2 global r = rand(100, 100);
julia> fetch(@spawnat 2 names(Main))
4-element Array{Symbol,1}:
:Base
:Core
:Main
:r
Given all the above new revelations to me, I think the way to do the intended from the above code of yours is:
@everywhere using BenchmarkTools
@spawnat 2 global r = rand(100, 100); #No need to store the Future here because we don't want `r` back
x = @spawnat 2 @btime r.^2 #This will square the `r` matrix and benchmark it on process 2, returning a Future that should point to the element-wise squared `r` matrix. And if you wait a little you will see the result of @btime.
m = fetch(x); # This will synchronize with process 2, by waiting for it to finish then it will get the resulting matrix to process 1.
typeof(m) # Should give us an Array{Float64, 2}
# Happy ending!
I hope that makes some sense! I am sure there is a lot more to learn. At some point, PRing the docs on parallel programming might be a good idea, as it is obviously faaaar from comprehensive.
For potential experts reading this and trying to figure out the sources of the potential bugs mentioned above, here is the versioninfo:
julia> versioninfo()
Julia Version 0.6.0
Commit 9036443 (2017-06-19 13:05 UTC)
Platform Info:
OS: Linux (x86_64-linux-gnu)
CPU: Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz
WORD_SIZE: 64
BLAS: libopenblas (USE64BITINT DYNAMIC_ARCH NO_AFFINITY Haswell)
LAPACK: libopenblas64_
LIBM: libopenlibm
LLVM: libLLVM-3.9.1 (ORCJIT, skylake)