Julia: 如何在Julia中将数据复制到另一个处理器

38

你如何在Julia中将数据从一个处理器移动到另一个处理器?

比如说我有一个数组

a = [1:10]

还有其他数据结构。把它放在所有其他可用的处理器上,使其作为相同变量名在这些处理器上可用的正确方式是什么?


5
这似乎应该是一个相当标准的请求,对吧? - bdeonovic
你可能会得到阅读文档的建议:http://julia.readthedocs.org/en/latest/manual/parallel-computing/。@everywhere宏是你要找的吗? - Guillermo Garza
我已经阅读了文档,@everywhere并不会做那件事。 - bdeonovic
在我的系统上,@everywhere a = [1:10] 可以将其放置在所有可用的处理器上。 - rickhg12hs
4
太好了,但如果我有一些需要花费时间构建的数据结构,我不想在每个处理器上重新构建它,我宁愿只构建一次,然后将其发送到其他所有处理器。简单的 a=[1:10] 数组只是一个例子。 - bdeonovic
4个回答

36

一开始我不知道如何做,所以花了一些时间来研究。

这里有一些我编写的传递对象的函数:

sendto

将任意数量的变量发送到指定的进程。

新变量将在指定进程的主模块中创建。名称将是关键字参数的键,值将是相关联的值。

function sendto(p::Int; args...)
    for (nm, val) in args
        @spawnat(p, eval(Main, Expr(:(=), nm, val)))
    end
end


function sendto(ps::Vector{Int}; args...)
    for p in ps
        sendto(p; args...)
    end
end

示例

# creates an integer x and Matrix y on processes 1 and 2
sendto([1, 2], x=100, y=rand(2, 3))

# create a variable here, then send it everywhere else
z = randn(10, 10); sendto(workers(), z=z)

getfrom

从任意进程的任意模块中检索一个对象。默认为主模块。

要检索的对象名称应为符号。

getfrom(p::Int, nm::Symbol; mod=Main) = fetch(@spawnat(p, getfield(mod, nm)))

示例

# get an object from named x from Main module on process 2. Name it x
x = getfrom(2, :x)

passobj

将任意数量的对象从一个进程传递到任意进程。变量必须在源进程的 from_mod 模块中定义,并将在每个目标进程的 to_mod 模块下以相同的名称进行复制。

function passobj(src::Int, target::Vector{Int}, nm::Symbol;
                 from_mod=Main, to_mod=Main)
    r = RemoteRef(src)
    @spawnat(src, put!(r, getfield(from_mod, nm)))
    for to in target
        @spawnat(to, eval(to_mod, Expr(:(=), nm, fetch(r))))
    end
    nothing
end


function passobj(src::Int, target::Int, nm::Symbol; from_mod=Main, to_mod=Main)
    passobj(src, [target], nm; from_mod=from_mod, to_mod=to_mod)
end


function passobj(src::Int, target, nms::Vector{Symbol};
                 from_mod=Main, to_mod=Main)
    for nm in nms
        passobj(src, target, nm; from_mod=from_mod, to_mod=to_mod)
    end
end

示例

# pass variable named x from process 2 to all other processes
passobj(2, filter(x->x!=2, procs()), :x)

# pass variables t, u, v from process 3 to process 1
passobj(3, 1, [:t, :u, :v])

# Pass a variable from the `Foo` module on process 1 to Main on workers
passobj(1, workers(), [:foo]; from_mod=Foo)

这正是我在寻找的内容:@spawnat(p, eval(Main, Expr(:(=), nm, val))) - conjectures
太棒了,很高兴它对你有用,感谢宏。 - spencerlyon2
2
这些宏非常有用,应该成为一个包甚至是Julia的一部分! - user1438310
3
@user1438310 我为此编写了ParallelDataTransfer.jl。目前在v0.4.x上测试失败,但在v0.5上通过。人们可以添加更多内容使其变得更好。 - Chris Rackauckas

13

为了让大家知道,我将这些想法打包成ParallelDataTransfer.jl。因此,你只需要执行:

using ParallelDataTransfer

(安装后)为了使用这里提到的函数,为什么?这些函数非常有用!我添加了一些测试,一些新的宏,并对它们进行了更新(它们在v0.5上通过,在v0.4.x上失败)。随意提交拉取请求以编辑并添加更多内容。


13

使用@eval @everywhere...并转义本地变量,就像这样:

julia> a=collect(1:3)
3-element Array{Int64,1}:
  1
  2
  3

julia> addprocs(1)
1-element Array{Int64,1}:
 2

julia> @eval @everywhere a=$a

julia> @fetchfrom 2 a
3-element Array{Int64,1}:
 1
 2
 3

2

为了补充@spencerlyon2的回答,这里提供一些宏:

function sendtosimple(p::Int, nm, val)
    ref = @spawnat(p, eval(Main, Expr(:(=), nm, val)))
end 

macro sendto(p, nm, val)
    return :( sendtosimple($p, $nm, $val) )
end

macro broadcast(nm, val)
    quote
    @sync for p in workers()
        @async sendtosimple(p, $nm, $val)
    end
    end
end
< p > @spawnat 宏在特定进程上将一个值绑定到一个符号上。

julia> @sendto 2 :bip pi/3
RemoteRef{Channel{Any}}(9,1,5340)

julia> @fetchfrom 2 bip
1.0471975511965976

@broadcast宏将一个值绑定到除1进程以外的所有进程中的符号上(因为我发现这样做可以使未来使用该名称的表达式复制自进程1的版本)

julia> @broadcast :bozo 5

julia> @fetchfrom 2 bozo
5

julia> bozo
ERROR: UndefVarError: bozo not defined

julia> bozo = 3             #these three lines are why I exclude pid 1
3

julia> @fetchfrom 7 bozo
3

julia> @fetchfrom 7 Main.bozo
5

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接