parallel: mutating an object from a worker process

102 views
Skip to first unread message

Constantin Berzan

unread,
May 1, 2015, 8:57:54 PM5/1/15
to julia...@googlegroups.com
I have a parallel application where I want to modify some objects from worker processes, and get the modified values back into the main process. Is there a clean way to do this in Julia?

Here is a contrived example illustrating the problem (run with "julia -p 2 myfile.jl"):

@everywhere function mutate_arr!(arr, x)
    println(arr)
    push!(arr, x)
    println(arr)
    nothing
end 

arr = [1, 2, 3]
println(arr)
remotecall_fetch(2, mutate_arr!, arr, 4)
println(arr)

This fails with the following error:

exception on 2: ERROR: cannot resize array with shared data
 in push! at ./array.jl:459
 in mutate_arr! at .../myfile.jl:3
 in anonymous at multi.jl:855
 in run_work_thunk at multi.jl:621
 in anonymous at task.jl:855

Is it the case that remotecall and @spawn do not allow modifying the function arguments? If so, this should be documented.

My second attempt uses a RemoteRef to pass the array explicitly:

@everywhere function mutate_arr!(arr, x)
    println(arr)
    push!(arr, x)
    println(arr)
    nothing
end
 
 
@everywhere function wrapper_mutate_arr!(ref, x)
    local_arr = take!(ref)
    mutate_arr!(local_arr, x)
    put!(ref, local_arr)
    nothing
end
 
 
arr = [1, 2, 3]
println(arr)
 
arr_ref = RemoteRef(2)
put!(arr_ref, arr)
remotecall_fetch(2, wrapper_mutate_arr!, arr_ref, 4)
arr = take!(arr_ref)
println(arr)

This fails with the same error ("cannot resize array with shared data"). What is shared here, and why?

If I use replace local_arr = take!(ref) with local_arr = copy(take!(ref)), it works. But I think this creates an additional copy (one for the serialization, and one for the copy() call), which I would like to avoid.

In summary: If I have a function like mutate_arr! that I want to offload to a worker process, what is the right way to do it? Obviously the functional way of thinking is to avoid mutation entirely, but if I already have a function like that, what should I do? Just use an additional copy() as above?

(In case it matters, I'm using Julia 0.3.4.)

Thanks,
Constantin

Tim Holy

unread,
May 2, 2015, 8:54:41 AM5/2/15
to julia...@googlegroups.com
Use a SharedArray.

--Tim

Constantin Berzan

unread,
May 2, 2015, 3:45:19 PM5/2/15
to julia...@googlegroups.com
On Saturday, May 2, 2015 at 5:54:41 AM UTC-7, Tim Holy wrote:
Use a SharedArray.

This should work in the toy example I posted, but in my real application, the object that I'm mutating has a bunch of DataStructures.SortedDict values.  The array that causes the "cannot resize array with shared data" error is buried deep inside the internals of SortedDict.  So I can't use a SharedArray without rewriting the internals of DataStructures.jl...

Any insight into what goes wrong in the second example from my original post?  Am I using RemoteRefs incorrectly?

Thanks,
Constantin

Tim Holy

unread,
May 3, 2015, 10:49:52 AM5/3/15
to julia...@googlegroups.com
Not sure, but I think you'll have to have the remote workers run the push!
operation in the parent process.

Using push! is going to give you trouble no matter what, though. If you can
preallocate all sizes, it will work much better. But I suspect at this point
it might be reasonable to ask yourself, "do I really need parallelism here?"
You could easily lose more time to passing data back and forth than you gain
by running in multiple processes.

--Tim
Reply all
Reply to author
Forward
0 new messages