朱莉娅 - 将GLPK.Prob发送给工人

时间:2016-08-03 09:19:26

标签: parallel-processing julia glpk

我正在使用GLPK和Julia,并使用spencerlyon编写的methods

sendto(2, lp = lp) #lp is type GLPK.Prob

但是,我似乎无法在工人之间发送类型GLPK.Prob。每当我尝试发送类型为GLPK.Prob时,它都会被“发送”并调用

remotecall_fetch(2, whos)

确认已发送GLPK.Prob

当我尝试通过调用

来解决问题时,会出现问题
simplex(lp)

错误

GLPK.GLPKError("invalid GLPK.Prob")

出现。我知道GLPK.Prob最初不是无效的GLPK.Prob,如果我决定在另一个worker上明确构造GLPK.Prob类型,fx worker 2,调用 simplex 运行就好了

这是一个问题,因为GLPK.Prob是从我的自定义类型生成的,有点偏重

tl; dr是否可能有些类型无法在工作人员之间正确发送?

更新

我现在看到调用

remotecall_fetch(2, simplex, lp)

将返回上述GLPK错误

此外,我刚刚注意到GLPK模块有一个名为

的方法
GLPK.copy_prob(GLPK.Prob, GLPK.Prob, Int)

但是复制GLPK.Prob

时,深度复制(当然也不复制)不起作用

示例

function create_lp()
    lp = GLPK.Prob()

    GLPK.set_prob_name(lp, "sample")
    GLPK.term_out(GLPK.OFF)

    GLPK.set_obj_dir(lp, GLPK.MAX)

    GLPK.add_rows(lp, 3)
    GLPK.set_row_bnds(lp,1,GLPK.UP,0,100)
    GLPK.set_row_bnds(lp,2,GLPK.UP,0,600)
    GLPK.set_row_bnds(lp,3,GLPK.UP,0,300)

    GLPK.add_cols(lp, 3)

    GLPK.set_col_bnds(lp,1,GLPK.LO,0,0)
    GLPK.set_obj_coef(lp,1,10)
    GLPK.set_col_bnds(lp,2,GLPK.LO,0,0)
    GLPK.set_obj_coef(lp,2,6)
    GLPK.set_col_bnds(lp,3,GLPK.LO,0,0)
    GLPK.set_obj_coef(lp,3,4)

    s = spzeros(3,3)
    s[1,1] =  1
    s[1,2] =  1
    s[1,3] =  1
    s[2,1] =  10
    s[3,1] =  2
    s[2,2] =  4
    s[3,2] =  2
    s[2,3] =  5
    s[3,3] =  6

    GLPK.load_matrix(lp, s)

    return lp
end 

这将返回一个lp :: GLPK.Prob(),它将在运行时返回733.33

simplex(lp)
result = get_obj_val(lp)#returns 733.33

然而,做

addprocs(1)
remotecall_fetch(2, simplex, lp)

将导致上面的错误

1 个答案:

答案 0 :(得分:1)

看起来问题是你的lp对象包含一个指针。

julia> lp = create_lp()
GLPK.Prob(Ptr{Void} @0x00007fa73b1eb330)

不幸的是,使用指针和并行处理很困难 - 如果不同的进程具有不同的内存空间,那么就不会清楚进程应该查看哪个内存地址以访问指针所指向的内存。这些问题可以克服,但显然它们需要针对涉及所述指针的每种数据类型进行单独的工作,请参阅this GitHub讨论以获取更多信息。

因此,我的想法是,如果你想访问worker上的指针,你可以在该worker上创建它。 E.g。

using GLPK
addprocs(2)

@everywhere begin
    using GLPK
    function create_lp()
        lp = GLPK.Prob()

        GLPK.set_prob_name(lp, "sample")
        GLPK.term_out(GLPK.OFF)

        GLPK.set_obj_dir(lp, GLPK.MAX)

        GLPK.add_rows(lp, 3)
        GLPK.set_row_bnds(lp,1,GLPK.UP,0,100)
        GLPK.set_row_bnds(lp,2,GLPK.UP,0,600)
        GLPK.set_row_bnds(lp,3,GLPK.UP,0,300)

        GLPK.add_cols(lp, 3)

        GLPK.set_col_bnds(lp,1,GLPK.LO,0,0)
        GLPK.set_obj_coef(lp,1,10)
        GLPK.set_col_bnds(lp,2,GLPK.LO,0,0)
        GLPK.set_obj_coef(lp,2,6)
        GLPK.set_col_bnds(lp,3,GLPK.LO,0,0)
        GLPK.set_obj_coef(lp,3,4)

        s = spzeros(3,3)
        s[1,1] =  1
        s[1,2] =  1
        s[1,3] =  1
        s[2,1] =  10
        s[3,1] =  2
        s[2,2] =  4
        s[3,2] =  2
        s[2,3] =  5
        s[3,3] =  6

        GLPK.load_matrix(lp, s)

        return lp
    end 
end

a = @spawnat 2 eval(:(lp = create_lp()))
b = @spawnat 2 eval(:(result = simplex(lp)))
fetch(b)

有关使用它的更多信息,请参阅下面@spawn上的文档,因为它可能需要一些时间来习惯。

@spawn@spawnat是Julia可用于向工作人员分配任务的两个工具。这是一个例子:

julia> @spawnat 2 println("hello world")
RemoteRef{Channel{Any}}(2,1,3)

julia>  From worker 2:  hello world

这两个宏都将评估工作进程的expression。两者之间的唯一区别是@spawnat允许您选择哪个工作人员将评估表达式(在上面的示例中指定了worker 2)而使用@spawn将根据可用性自动选择工作人员。

在上面的例子中,我们只是让worker 2执行println函数。没有兴趣返回或从中检索。但是,我们发送给工作人员的表达通常会产生我们希望检索的内容。请注意,在上面的示例中,当我们调用@spawnat时,在我们从worker 2获得打印输出之前,我们看到了以下内容:

RemoteRef{Channel{Any}}(2,1,3)

这表示@spawnat宏将返回RemoteRef类型对象。该对象将包含发送给worker的表达式的返回值。如果我们想要检索这些值,我们可以先将RemoteRef @spawnat返回给对象,然后再使用fetch()函数对RemoteRef进行操作type对象,用于检索从对worker执行的评估中存储的结果。

julia> result = @spawnat 2 2 + 5
RemoteRef{Channel{Any}}(2,1,26)

julia> fetch(result)
7

能够有效使用@spawn的关键是了解其运作的expressions背后的本质。使用@spawn向工作人员发送命令比直接输入你输入的"解释器"在其中一个工作者上或在他们身上执行代码。例如,假设我们希望使用@spawnat为工作者的变量赋值。我们可能会尝试:

@spawnat 2 a = 5
RemoteRef{Channel{Any}}(2,1,2)

有效吗?好吧,让工人2试图打印a

julia> @spawnat 2 println(a)
RemoteRef{Channel{Any}}(2,1,4)

julia> 

什么都没发生。为什么?我们可以使用上面的fetch()对此进行更多调查。 fetch()可以非常方便,因为它不仅可以检索成功的结果,还可以检索错误消息。没有它,我们甚至可能不知道出了什么问题。

julia> result = @spawnat 2 println(a)
RemoteRef{Channel{Any}}(2,1,5)

julia> fetch(result)
ERROR: On worker 2:
UndefVarError: a not defined

错误消息显示a未在工作人员2上定义。但为什么会这样?原因是我们需要将赋值操作包装到一个表达式中,然后我们使用@spawn来告诉worker进行求值。以下是一个示例,说明如下:

julia> @spawnat 2 eval(:(a = 2))
RemoteRef{Channel{Any}}(2,1,7)

julia> @spawnat 2 println(a)
RemoteRef{Channel{Any}}(2,1,8)

julia>  From worker 2:  2

:()语法是Julia用来指定expressions的语法。然后我们使用Julia中的eval()函数来计算表达式,我们使用@spawnat宏来指示在worker 2上计​​算表达式。

我们也可以获得与以下相同的结果:

julia> @spawnat(2, eval(parse("c = 5")))
RemoteRef{Channel{Any}}(2,1,9)

julia> @spawnat 2 println(c)
RemoteRef{Channel{Any}}(2,1,10)

julia>  From worker 2:  5

此示例演示了另外两个概念。首先,我们看到我们还可以使用在字符串上调用的parse()函数创建表达式。其次,我们看到在调用@spawnat时我们可以使用括号,这可能会使我们的语法更清晰易懂。