Parallel processing 如何并行计算成对距离矩阵?
我的问题大致如下。给定一个数值矩阵X,其中每行为一项。我想找到每一行的最近邻,除了它自己以外,所有行的L2距离。我试图阅读官方文件,但对于如何实现这一点仍然有点困惑。有人能给我一些提示吗 我的代码如下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}
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访问的处理器数量)Julia-p#
- 启动Julia“会话”后,可以调用
函数添加其他处理器addprocs
- 要拥有多个线程,您需要运行命令
。我对线程不太了解,所以我将坚持使用export-JULIA_-NUM_-THREADS=.
宏。我建议阅读更多关于线程的详细信息——也许@Chris Rackauckas可以对差异进行更多的扩展@parallel
- 我的版本是
。我不认为我在做任何0.6特定的事情,但这只是一个警告,以防万一0.6.1-pre.0
- 我将在计算向量之间的距离时使用这个包。我认为将尽可能多的计算工作外包给编写良好、维护良好的软件包是一个好习惯
- 我将计算列之间的距离,而不是计算行之间的距离。这是因为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]]
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
,速度/记忆又有了提升。非常感谢您的回答!那正是我要找的!