Julia 并行计算共享Darray

Julia 并行计算共享Darray,julia,Julia,我试图在Julia中实现一个简单的并行算法,用于矩阵上的乘法加法运算。这更是为了理解如何在Julia中进行并行计算,而不是实用性 最终,这应该实现C=C+A*B,其中A、B和C是矩阵。我使用的代码是: function MultiplyAdd!(C::SharedArray{Int64,2}, A::SharedArray{Int64,2}, B::SharedArray{Int64,2}) assert(size(A)[2]==size(B)[1]) const p = si

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

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

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
其次,在我看来,我不应该运行两个
@spawn
任务。我应该让第二个是一个
多个add!(C、A、B)
在每种情况下调用。换句话说,只需分配
a
fetch(a)

第三,Julia通过引用将数组传递给函数,因此所有的操作不是自然地在相同的
C
A
B
矩阵上运行吗?就目前情况而言,采取如下措施:

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

创建一个全新的对象,这解释了在上述代码中获取函数调用内部的切片。从本质上说,我是在避免在同一个对象上尝试和操作的任务。一定有比我所实施的更好的方法来做到这一点。最后,我想对内存中的相同数据进行操作,只需“在阵列上移动放大镜”就可以并行地对其子集进行操作。

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

至于你的代码,你的方法有几个问题

  • 按编码,
    MultiplyAdd最多只能并行两个工作进程
  • MultiplyAdd
    执行多个调用,如分配新数组的
    A[:,1:n]
  • 在这种情况下,像
    A[:,1:n]
    这样的调用将生成
    Array
    对象,而不是
    SharedArray
    对象,因此递归调用
    MultiplyAddSharedArray{Int,2}
    的参数的code>将不起作用
  • 最后,
    MultiplyAdd
    不遵守
    SharedArray
    对象的索引方案
  • 最后一点很重要。要求worker 1访问分配给worker 2的
    A
    B
    部分需要进行数据传输,这会扼杀并行化代码带来的性能提升。你可以通过跑步看到这一点

    for i in procs(A)
        @show i, localindexes(A)
    end
    
    在您的
    SharedArray
    对象
    A
    上。理想情况下,每个worker
    i
    应该只在自己的本地索引上运行,尽管允许在本地索引边界上共享数据可以帮助您省去一些簿记方面的麻烦

    如果您坚持对原型使用
    SharedArray
    s,那么您仍然有选择。
    SharedArray
    有一些好的建议。我发现这个构造

    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!
    )通常会在所有参与的工作人员之间很好地并行化操作。显然,您必须决定如何划分执行;
    平流\u共享示例是一个很好的指南


    你也可以考虑使用朱丽亚。这种并行框架与共享内存计算略有不同。但是,它允许您使用熟悉的迭代构造直接对
    数组
    对象进行操作。

    @everywhere
    放在函数定义前面,以便在每个进程上定义函数。另外,您可能希望在基本情况下使用
    muladd
    。我昨天问的问题似乎也与此相关
    @everywhere
    修复了该错误。第二个错误随后出现,因为混合了数组和sharedarray。我在前面加了
    convert(SharedArray
    调用递归函数中的每个切片。现在它运行了,但给出了错误的答案,尽管它至少完成了计算。我将检查切片的逻辑,看看为什么会出现这种情况。ooh@xave如果要并行计算,在混合
    SharedArray
    Array
    操作时应该小心el.我认为一个
    数组
    只能被主进程看到。如果你要求一个工作进程参与,那么它就看不到
    数组
    。最终,我根本不是在寻找性能。我只是想在Julia中并行实现一个算法来理解这个进程。这纯粹是一个教育练习。这是alg算法非常简单,如果我不能用它,我将无法做任何不寻常的事情。我不需要使用SharedArray。我只需要设置代码,使其按预期工作,这是一个并行实现的乘法加载项Julia。如果需要,我们可以完全更改所有代码。
    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