Parallel processing 如何并行计算成对距离矩阵?

Parallel processing 如何并行计算成对距离矩阵?,parallel-processing,julia,distance-matrix,Parallel Processing,Julia,Distance Matrix,我的问题大致如下。给定一个数值矩阵X,其中每行为一项。我想找到每一行的最近邻,除了它自己以外,所有行的L2距离。我试图阅读官方文件,但对于如何实现这一点仍然有点困惑。有人能给我一些提示吗 我的代码如下 function l2_dist(v1, v2) return sqrt(sum((v1 - v2) .^ 2)) end function main(Mat, dist_fun) n = size(Mat, 1) Dist = SharedArray{Float64}

我的问题大致如下。给定一个数值矩阵X,其中每行为一项。我想找到每一行的最近邻,除了它自己以外,所有行的L2距离。我试图阅读官方文件,但对于如何实现这一点仍然有点困惑。有人能给我一些提示吗

我的代码如下

function l2_dist(v1, v2)
    return sqrt(sum((v1 - v2) .^ 2))
end

function main(Mat, dist_fun)
    n = size(Mat, 1)

    Dist = SharedArray{Float64}(n) #[Inf for i in 1:n]
    Id = SharedArray{Int64}(n) #[-1 for i in 1:n]
    @parallel for i = 1:n
        Dist[i] = Inf
        Id[i] = 0
    end
    Threads.@threads for i in 1:n
        for j in 1:n
            if i != j
                println(i, j)
                dist_temp = dist_fun(Mat[i, :], Mat[j, :])
                if dist_temp < Dist[i]
                    println("Dist updated!")
                    Dist[i] = dist_temp
                    Id[i] = j
                end
            end
        end
    end
    return Dict("Dist" => Dist, "Id" => Id)
end

n = 4000
p = 30

X = [rand() for i in 1:n, j in 1:p];


main(X[1:30, :], l2_dist)
@time N = main(X, l2_dist)
功能l2_dist(v1、v2)
返回sqrt(总和((v1-v2)。^2))
结束
功能主控台(垫子、分机)
n=尺寸(垫,1)
Dist=SharedArray{Float64}(n)#[Inf for i in 1:n]
Id=SharedArray{Int64}(n)#[-1表示1:n中的i]
@i=1:n时的平行
Dist[i]=Inf
Id[i]=0
结束
线程。@1:n中i的线程
对于1:n中的j
如果我J
println(i,j)
dist_temp=dist_fun(Mat[i,:],Mat[j,:])
如果距离温度<距离[i]
println(“Dist updated!”)
距离[i]=距离温度
Id[i]=j
结束
结束
结束
结束
返回Dict(“Dist”=>Dist,“Id”=>Id)
结束
n=4000
p=30
X=[rand()表示1:n中的i,1:p中的j];
干管(X[1:30,:],二区)
@时间N=主(X,l2_距离)

我试图将所有I(即计算每行最小值)分布在不同的核上。但是上面的版本显然不能正常工作。它甚至比顺序版本还要慢。有人能给我指一下正确的方向吗?谢谢。

也许你在写下的内容之外还做了些什么,但从我看到的这一点来看,你实际上并没有并行地进行任何计算。Julia要求您告诉它您希望它能够访问多少个处理器(或线程)。你可以通过以下两种方式来实现

  • 使用多个处理器启动Julia
    Julia-p#
    (其中#是您希望Julia访问的处理器数量)
  • 启动Julia“会话”后,可以调用
    addprocs
    函数添加其他处理器
  • 要拥有多个线程,您需要运行命令
    export-JULIA_-NUM_-THREADS=.
    。我对线程不太了解,所以我将坚持使用
    @parallel
    宏。我建议阅读更多关于线程的详细信息——也许@Chris Rackauckas可以对差异进行更多的扩展
下面是关于我的代码和您的代码的一些评论:

  • 我的版本是
    0.6.1-pre.0
    。我不认为我在做任何0.6特定的事情,但这只是一个警告,以防万一
  • 我将在计算向量之间的距离时使用这个包。我认为将尽可能多的计算工作外包给编写良好、维护良好的软件包是一个好习惯
  • 我将计算列之间的距离,而不是计算行之间的距离。这是因为Julia是一种以列为主的语言,因此这将增加缓存命中数并提供一点额外的速度。显然,只需转置输入即可获得所需的行结果
  • 除非您希望有这么多的内存分配,否则这么多的分配就是代码中某些内容效率低下的标志。这通常是一类稳定性问题。我不知道以前的代码中是否存在这种情况,但在当前版本中这似乎不是问题(我还不清楚为什么会有这么多的分配)
代码如下

# Make sure all processors have access to Distances package
@everywhere using Distances

# Create a random matrix
nrow = 30
ncol = 4000

# Seed creation of random matrix so it is always same matrix
srand(42)
X = rand(nrow, ncol)

function main(X::AbstractMatrix{Float64}, M::Distances.Metric)
    # Get size of the matrix
    nrow, ncol = size(X)

    # Create `SharedArray` to store output
    ind_vec = SharedArray{Int}(ncol)
    dist_vec = SharedArray{Float64}(ncol)

    # Compute the distance between columns
    @sync @parallel for i in 1:ncol
        # Initialize various temporary variables
        min_dist_i = Inf
        min_ind_i = -1
        X_i = view(X, :, i)

        # Check distance against all other columns
        for j in 1:ncol
            # Skip comparison with itself
            if i==j
                continue
            end

            # Tell us who is doing the work 
            # (can uncomment if you want to verify stuff)
            # println("Column $i compared with Column $j by worker $(myid())")

            # Evaluate the new distance... 
            # If it is less then replace it, otherwise proceed
            dist_temp = evaluate(M, X_i, view(X, :, j))
            if dist_temp < min_dist_i
                min_dist_i = dist_temp
                min_ind_i = j
            end
        end

        # Which column is minimum distance from column i
        dist_vec[i] = min_dist_i
        ind_vec[i] = min_ind_i
    end

    return dist_vec, ind_vec
end

# Using Euclidean metric
metric = Euclidean()
inds, dist = main(X, metric)

@time main(X, metric);
@show dist[[1, 5, 25]], inds[[1, 5, 25]]
  • n个处理器(在本例中为4个)
    julia-pntestfile.jl

    % julia testfile.jl
      0.640365 seconds (16.00 M allocations: 732.495 MiB, 3.70% gc time)
      (dist[[1, 5, 25]], inds[[1, 5, 25]]) = ([2541, 2459, 1602], [1.40892, 1.38206, 1.32184])
    
    % julia -p 4 testfile.jl
      0.201523 seconds (2.10 k allocations: 99.107 KiB)
      (dist[[1, 5, 25]], inds[[1, 5, 25]]) = ([2541, 2459, 1602], [1.40892, 1.38206, 1.32184])
    

  • 我建议在以任何其他方式进行优化之前,先将字典从内部循环中删除。字典对运行时是有害的。如果你真的想要一个字典,那么在不使用字典的情况下创建数组,然后在循环后将它们添加到字典中。这可能会有很大帮助。@ChrisRackauckas谢谢。我想我可以创建这两个数组,并在最后一步使它们成为字典。除了使用
    线程对其进行多线程处理之外,您对并行化部分有什么提示吗?“您对并行化部分有什么提示吗?”。@Threads
    然后通过每个线程使用不同的数组并在之后合并使其线程安全?不,应该很标准。如果我有时间,我可以写出来。@ChrisRackauckas非常感谢。我只是试着按照你关于字典的建议修改代码(现在问题中更新了)。这么做就让我的速度提高了20倍。太神了它是否打算有
    dist_-fun(Mat[i,],Mat[j,])
    而不是
    dist_-fun(Mat[i,:],Mat[j,:])
    ?前者要快得多,但后者实际上给出了正确的答案。在表达式之前添加
    @views
    ,速度/记忆又有了提升。非常感谢您的回答!那正是我要找的!