juliadistributedpmap

Applying @everywhere to an already defined function in Julia


I would like to take an arbitrary function at runtime that may or may not be defined with the @everywhere macro, and run it in a distributed way.

My first, naive attempt was to simply try and use the function inside of pmap

@assert nprocs() > 1
function g(f)
    pmap(1:5) do x
        f(x)
    end
end

addone(x) = x+1
subone(x) = x-1
g(addone)
g(subone)

This, of course, did not work and resulted in.

On worker 2:
UndefVarError: `#addone` not defined

Next I tried passing the function f as an argument of pmap

@assert nprocs() > 1

function g(f)
    pmap(zip(1:5, Iterators.repeated(f))) do (x,f)
        f(x)
    end
end

addone(x) = x+1
subone(x) = x-1
g(addone)
g(subone)

This also did not work, it also threw

On worker 2:
UndefVarError: `#addone` not defined

Now I am at a loss, surely something like this must be possible in Julia.


Solution

  • @BatWannBee is totally right that you should not do it and should use just @everywhere.

    However if you want to do it here is the code snippet.

    Firstly, we perfom a set up

    using Distributed
    addprocs(2)
    @everywhere using Serialization
    
    addone(x::Int) = x+1 + 100myid()
    

    Now we move the function to the other workers

    # the name of the function to be moved around
    fname = :addone 
    
    # Serializing methods of the function fname to a buffer
    buf = IOBuffer()
    serialize(buf, methods(eval(fname)))
    
    # Deserializing the function on remote workers
    # Note that there are two steps
    # 1. creating an empty function 
    # 2. providing methods
    Distributed.remotecall_eval(Main, workers(), quote
     function $fname end 
     deserialize(seekstart($buf))
    end)
    
    

    Now we can test what we did:

    julia> fetch(@spawnat 3 methods(addone))
    # 1 method for generic function "addone" from Main:
     [1] addone(x::Int64)
         @ REPL[3]:1
    
    julia> fetch(@spawnat 3 addone(4))
    305