2016-08-03 27 views
2

我使用GLPK与朱莉娅,并使用由spencerlyon书面朱莉娅 - 发送GLPK.Prob到工人

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

然而methods,我似乎无法派遣工之间的类型GLPK.Prob。每当我试图发送一个类型GLPK.Prob,它就会被“发送”,并呼吁

remotecall_fetch(2, whos) 

确认GLPK.Prob被罚

当我试图通过调用

解决出现的问题
simplex(lp) 

错误

GLPK.GLPKError("invalid GLPK.Prob") 

出现。我知道GLPK.Prob心不是原来的无效GLPK.Prob,如果我决定明确构建在另一名工人,工人FX 2 GLPK.Prob类型,调用单纯运行就好

这是一个问题,因为GLPK.Prob是从一个自定义类型的矿产生成的,这是一个很重的方面

tl; dr是否可能有一些类型不能在工人之间正确发送?

更新

我现在看到,在调用

remotecall_fetch(2, simplex, lp) 

将返回上面GLPK错误

而且我刚刚注意到,GLPK模块已经得到了一个名为

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

但是deepcopy的(当然不要复制)不会工作拷贝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) 

将导致在上面的错误

+0

你能发布一个可重复的例子吗? –

回答

1

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

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

不幸的是,指针和并行处理的工作是困难的 - 如果不同的进程有不同的内存空间,那么它不会明确其内存地址的过程应该看看,以访问内存的指针指向至。这些问题可以被克服,但显然他们需要针对涉及所述指针的每种数据类型进行单独的工作,参见this GitHub的讨论。

因此,我的想法是,如果你想访问worker上的指针,你可以在那个worker上创建它。例如。

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> @spawnat 2 println("hello world") 
RemoteRef{Channel{Any}}(2,1,3) 

julia> From worker 2: hello world 

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

在上面的例子中,我们只需要worker 2执行println函数。没有任何兴趣返回或从中检索。然而,我们发给工人的表达方式通常会产生我们想要检索的东西。在上面的例子注意,当我们叫@spawnat,之前我们从工人2打印输出,我们看到了以下内容:

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

这表明@spawnat宏将返回一个RemoteRef类型的对象。这个对象反过来将包含我们发送给worker的表达式的返回值。如果我们想要检索这些值,我们可以首先将RemoteRef指定@spawnat返回到一个对象,然后使用在RemoteRef类型对象上运行的fetch()函数来检索对工作人员执行的评估中存储的结果。

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来告诉工作人员进行评估。下面是一个例子,有如下解释:

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 

:()语法是什么朱莉娅使用指定expressions。然后,我们使用朱莉娅eval()功能,其计算表达式,我们使用@spawnat宏来指示该表达式对工人进行评估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时可以使用括号,这可能会使我们的语法更加清晰和易于管理。

+0

谢谢,这接近我最终做的。我从lp中获取每个相关数组并将其传输并在每个创建lp的worker上定义create_lp()方法。我仍然想用一种方法来使用copy_prob方法,并且能够将指针(如果这是你可以做的事情)“移动”到另一个工作者上,因为总是会寻找优雅 – isebarn

+0

@isebarn当然。另外,请注意,当您使用'remotecall_fetch()'并为函数提供参数时,您指定的对象将默认来自调用'remotecall_fetch()'的进程范围,然后这些对象将被发送到进程被激活,而不是本地使用工作进程范围内的参数的'remotecall'。这也可能导致这些类型的序列化错误。 –