2016-08-03 44 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'。這也可能導致這些類型的序列化錯誤。 –