并行计算SharedArray

时间:2017-02-24 20:14:31

标签: julia

我正在尝试在Julia中实现一个简单的并行算法,用于矩阵上的乘加运算。这更像是为了理解如何在Julia中进行并行计算而不是实用性。

最终,这应该实现C = C + A*B,其中ABC是矩阵。我正在使用的代码是:

function MultiplyAdd!(C::SharedArray{Int64,2}, A::SharedArray{Int64,2}, B::SharedArray{Int64,2})
    assert(size(A)[2]==size(B)[1])

    const p = size(A)[1] #rows(A), rows(C)
    const m = size(A)[2] #cols(A), rows(B)
    const n = size(B)[2] #cols(C), cols(B) 

    # thresh is some threshold based on the number of operations required
    # to compute C before we should switch to parallel computation.
    const thresh = 190 
    M = 2*p*m*n 

    if ( M < thresh )
         C += A*B # base case for recursion
    elseif ( n >= max(p,m) )
    # Partition C and B (Vertical Split)
            if (iseven(p))
                #even partition of C and B
                #MultiplyAdd!( C0, A, B0 )
                #MultiplyAdd!( C1, A, B1 )
                a = @spawn MultiplyAdd!( C[:,1:convert(Int64,p/2)], A, B[:,1:convert(Int64,p/2)] ) 
                b = @spawn MultiplyAdd!( C[:,1:convert(Int64,p-(p/2))], A, B[:,1:convert(Int64,p-(p/2))]) 
                fetch(a), fetch(b)
            else
                #odd parition of C and B
                a = @spawn MultiplyAdd!( C[:,1:convert(Int64,p/2+0.5)], A, B[:,1:convert(Int64,p/2+0.5)] ) 
                b = @spawn MultiplyAdd!( C[:,1:convert(Int64,p-(p/2+0.5))], A, B[:,1:convert(Int64,p-(p/2+0.5))]) 
                fetch(a), fetch(b)
            end

    elseif ( p >= m )
    # Partition C and A (Horizontal Split)
            if (iseven(n))
                #even partition of C and A
                #MultiplyAdd!( C0, A0, B )
                #MultiplyAdd!( C1, A1, B )
                a = @spawn MultiplyAdd!(C[1:convert(Int64,n/2),:],A[1:convert(Int64,n/2),:],B)
                b = @spawn MultiplyAdd!(C1 = C[1:convert(Int64,n-(n/2)),:], A[1:convert(Int64,n-(n/2)),:], B) 
                fetch(a), fetch(b)
            else
                #odd parition of C and A
                # MultiplAdd!( C0, A0, B )
                # MultiplyAdd!( C1, A1, B )
                a = @spawn MultiplyAdd!( C[1:convert(Int64,n/2 + 0.5),:], A[1:convert(Int64,n/2),:], B ) 
                b = @spawn MultiplyAdd!( C[1:convert(Int64,n-(n/2 + 0.5)),:], A[1:convert(Int64,n-(n/2 + 0.5)),:], B ) 
                fetch(a), fetch(b)
            end   
    else
        #Begin Serial Recursion
        # A is Vertical Split, B is Horizontal Split
        #MultiplyAdd!( C,A0,B0 )
        #MultiplyAdd!( C,A1,B1 )
        if (iseven(m))
            MultiplyAdd!( C,A[:,1:convert(Int64,m/2)], B[1:convert(Int64,m/2),:] ) 
            MultiplyAdd!( C,A[:,1:convert(Int64,m-(m/2))], B[1:convert(Int64,m-(m/2) ),:] ) 
        else
            MultiplyAdd!( C,A[:,1:convert(Int64,m/2 + 0.5)], B[1:convert(Int64,m/2 + 0.5), :])
            MultiplyAdd!( C,A[:,1:convert(Int64,m-(m/2 + 0.5))], B[1:convert(Int64,m-(m/2 + 0.5)), :] ) 
        end     

    end
end

首先,这根本不起作用。我运行时遇到以下错误。

LoadError: On worker 5:
UndefVarError: #MultiplyAdd! not defined
 in deserialize_datatype at ./serialize.jl:823
 in handle_deserialize at ./serialize.jl:571
 in collect at ./array.jl:307
 in deserialize at ./serialize.jl:588
 in handle_deserialize at ./serialize.jl:581
 in deserialize at ./serialize.jl:541
 in deserialize_datatype at ./serialize.jl:829
 in handle_deserialize at ./serialize.jl:571
 in deserialize_msg at ./multi.jl:120
 in message_handler_loop at ./multi.jl:1317
 in process_tcp_streams at ./multi.jl:1276
 in #618 at ./event.jl:68
while loading In[32], in expression starting on line 73

 in #remotecall_fetch#606(::Array{Any,1}, ::Function, ::Function, ::Base.Worker, ::Base.RRID, ::Vararg{Any,N}) at ./multi.jl:1070
 in remotecall_fetch(::Function, ::Base.Worker, ::Base.RRID, ::Vararg{Any,N}) at ./multi.jl:1062
 in #remotecall_fetch#609(::Array{Any,1}, ::Function, ::Function, ::Int64, ::Base.RRID, ::Vararg{Any,N}) at ./multi.jl:1080
 in remotecall_fetch(::Function, ::Int64, ::Base.RRID, ::Vararg{Any,N}) at ./multi.jl:1080
 in call_on_owner(::Function, ::Future, ::Int64, ::Vararg{Int64,N}) at ./multi.jl:1130
 in fetch(::Future) at ./multi.jl:1156
 in MultiplyAdd!(::SharedArray{Int64,2}, ::SharedArray{Int64,2}, ::SharedArray{Int64,2}) at ./In[32]:24

其次,在我看来,我不应该执行两个@spawn任务。在这些情况中,我应该让第二个只是MultiplyAdd!(C,A,B)来电。换句话说,只需指定afetch(a)

第三,Julia通过引用将数组传递给函数,因此所有操作都不会自然地在相同的CAB矩阵上运行?就目前而言,采取如下的切片:

C0 = C[:, 1:p/2]
C1 = C[:, 1:p-p/2]

创建一个全新的对象,它解释了在上面的代码中获取函数调用中的切片。从本质上讲,我正在避免分配尝试并始终对同一个对象进行操作。必须有比我实施的更好的方法。最终,我希望在内存中运行相同的数据,只需“在数组上移动放大镜”就可以并行处理它的子集。

1 个答案:

答案 0 :(得分:1)

这里很难帮助你,因为你没有真正问过一个问题。冒着听起来居高临下的风险,我建议你偷看suggestions来问一个好的SO问题。

至于您的代码,您的方法存在一些问题。

  1. 按照编码,MultiplyAdd!仅与最多两名工人并行。
  2. MultiplyAdd!执行多个调用,例如分配新数组的A[:,1:n]
  3. 在这种情况下,A[:,1:n]之类的调用会生成Array个对象,而不是SharedArray个对象,所以对MultiplyAdd!的递归调用,参数严格类型为SharedArray{Int,2}不会起作用。
  4. 最后,MultiplyAdd!不尊重SharedArray个对象的索引方案。
  5. 最后一点很重要。要求工作人员1访问分配给工作人员2的AB部分需要数据传输,这会通过并行化代码来消除任何性能提升。你可以通过运行

    来看到这一点
    for i in procs(A)
        @show i, localindexes(A)
    end
    

    SharedArray对象A上。理想情况下,每个工作者i应仅在其自己的本地索引上运行,但允许在本地索引边界进行数据共享可以帮助您节省一些簿记费用。

    如果您坚持使用SharedArray s作为原型,那么您仍然可以选择。 SharedArray documentation有一些很好的建议。我发现了构造

    function f(g, S::SharedArray)
        @sync begin
            for p in procs(S)
                @async begin
                    remotecall_fetch(g, p, S, p)
                end
            end
        end
        S
    end
    

    使用某些内核函数g(例如MultiplyAdd!)通常会在所有参与工作者之间很好地并行化操作。显然你必须决定如何划分执行;文档中的advection_shared!示例是一个很好的指南。

    您也可以考虑使用Julia的native multithreading。该并行框架与共享内存计算略有不同。但是,它允许您使用熟悉的迭代结构直接对Array个对象进行操作。