将 @everywhere 应用于 Julia 中已定义的函数

问题描述 投票:0回答:2

我想在运行时获取任意函数,该函数可能使用或不使用

@everywhere
宏定义,并以分布式方式运行它。

我的第一个天真的尝试是简单地尝试使用

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)

当然,这并没有奏效并导致了。

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

接下来我尝试将函数 f 作为 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)

这个也没用,还扔了

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

现在我很茫然,这样的事情在 Julia 中肯定是可能的。

julia distributed pmap
2个回答
1
投票

这是可能的,但你不应该这样做,因为这很容易导致工作量太大。

从根本上来说,问题是一个方法只存在于您想要调用它的多个进程中的一个上。正确的做法是在实例化该方法的源代码处放置

@everywhere
来预防;根据方法的需要,这可能位于
function
块本身、运行文件的
include
调用或包的
using
/
import
。请记住,作为一个宏,
@everywhere
不会获取现有实例并将其复制到多个进程中,它只是在每个进程中计算以下源代码表达式。

也就是说,可以从函数实例和参数派生方法的表达式实例(CodeTracking.jl 使这变得容易),并且可以使用

@eval
@everywhere
在其他进程上对其进行评估。这在您的简单示例中是可行的,但通常比这更复杂。方法定义本身不会复制它定义的名称空间或记住它是如何评估的,因此您需要单独派生相关模块或全局变量的表达式(据我所知,没有包可以让这变得容易)。正确地解决这个问题比前面提到的预防要困难得多,也更混乱。


0
投票

@BatWannBee 完全正确,你不应该这样做,应该只使用

@everywhere

但是,如果您想这样做,这里是代码片段。

首先我们进行设置

using Distributed
addprocs(2)
@everywhere using Serialization

addone(x::Int) = x+1 + 100myid()

现在我们将这个功能转移给其他工人

# 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)

现在我们可以测试我们所做的事情:

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
© www.soinside.com 2019 - 2024. All rights reserved.