在计算图形中心性的代码并行化后,我的性能下降了。图形相对较大,100K顶点。单线程应用程序大约需要7分钟。正如julialang网站(http://julia.readthedocs.org/en/latest/manual/parallel-computing/#man-parallel-computing)所推荐的那样,我修改了代码并使用了pmap api来并行化计算。我开始用8个进程计算(julia -p 8 calc_centrality.jl)。令我惊讶的是,我减速了10倍。并行过程现在需要超过一小时。我注意到并行进程初始化并开始计算需要几分钟。即使在所有8个CPU都忙于使用julia应用程序后,计算速度也非常慢。
对于如何提高并行性能的任何建议表示赞赏。
calc_centrality.jl:
using Graphs
require("read_graph.jl")
require("centrality_mean.jl")
function main()
file_name = "test_graph.csv"
println("graph creation: ", file_name)
g = create_generic_graph_from_file(file_name)
println("num edges: ", num_edges(g))
println("num vertices: ", num_vertices(g))
data = cell(8)
data[1] = {g, 1, 2500}
data[2] = {g, 2501, 5000}
data[3] = {g, 5001, 7500}
data[4] = {g, 7501, 10000}
data[5] = {g, 10001, 12500}
data[6] = {g, 12501, 15000}
data[7] = {g, 15001, 17500}
data[8] = {g, 17501, 20000}
cm = pmap(centrality_mean, data)
println(cm)
end
println("Elapsed: ", @elapsed main(), "\n")
centrality_mean.jl
using Graphs
function centrality_mean(gr, start_vertex)
centrality_cnt = Dict()
vertex_to_visit = Set()
push!(vertex_to_visit, start_vertex)
cnt = 0
while !isempty(vertex_to_visit)
next_vertex_set = Set()
for vertex in vertex_to_visit
if !haskey(centrality_cnt, vertex)
centrality_cnt[vertex] = cnt
for neigh in out_neighbors(vertex, gr)
push!(next_vertex_set, neigh)
end
end
end
cnt += 1
vertex_to_visit = next_vertex_set
end
mean([ v for (k,v) in centrality_cnt ])
end
function centrality_mean(data::Array{})
gr = data[1]
v_start = data[2]
v_end = data[3]
n = v_end - v_start + 1;
cm = Array(Float64, n)
v = vertices(gr)
cnt = 0
for i = v_start:v_end
cnt += 1
if cnt%10 == 0
println(cnt)
end
cm[cnt] = centrality_mean(gr, v[i])
end
return cm
end
答案 0 :(得分:1)
我猜这与并行性无关。您的第二个centrality_mean
方法无法确定gr
,v_start
和v_end
的对象类型。所以它必须为那个"外循环使用非优化的慢代码。"
虽然有几种可能的解决方案,但最简单的方法是打破接收"命令的功能。来自pmap
:
function centrality_mean(data::Array{})
gr = data[1]
v_start = data[2]
v_end = data[3]
centrality_mean(gr, v_start, v_end)
end
function centrality_mean(gr, v_start, v_end)
n = v_end - v_start + 1;
cm = Array(Float64, n)
v = vertices(gr)
cnt = 0
for i = v_start:v_end
cnt += 1
if cnt%10 == 0
println(cnt)
end
cm[cnt] = centrality_mean(gr, v[i])
end
return cm
end
所有这一切都是创造一个中断,并让julia有机会优化输入的实际类型的第二部分(包含性能关键循环)。
答案 1 :(得分:1)
以下是https://stackoverflow.com/users/1409374/rickhg12hs在评论中建议的@everywhere代码。那个固定的性能问题!!!
test_parallel_pmap.jl
using Graphs
require("read_graph.jl")
require("centrality_mean.jl")
function main()
@everywhere file_name = "test_data.csv"
println("graph creation from: ", file_name)
@everywhere data_graph = create_generic_graph_from_file(file_name)
@everywhere data_graph_vertex = vertices(data_graph)
println("num edges: ", num_edges(data_graph))
println("num vertices: ", num_vertices(data_graph))
range = cell(2)
range[1] = {1, 25000}
range[2] = {25001, 50000}
cm = pmap(centrality_mean_pmap, range)
for i = 1:length(cm)
println(length(cm[i]))
end
end
println("Elapsed: ", @elapsed main(), "\n")
centrality_mean.jl
using Graphs
function centrality_mean(start_vertex::ExVertex)
centrality_cnt = Dict{ExVertex, Int64}()
vertex_to_visit = Set{ExVertex}()
push!(vertex_to_visit, start_vertex)
cnt = 0
while !isempty(vertex_to_visit)
next_vertex_set = Set()
for vertex in vertex_to_visit
if !haskey(centrality_cnt, vertex)
centrality_cnt[vertex] = cnt
for neigh in out_neighbors(vertex, data_graph)
push!(next_vertex_set, neigh)
end
end
end
cnt += 1
vertex_to_visit = next_vertex_set
end
mean([ v for (k,v) in centrality_cnt ])
end
function centrality_mean(v_start::Int64, v_end::Int64)
n = v_end - v_start + 1;
cm = Array(Float64, n)
cnt = 0
for i = v_start:v_end
cnt += 1
cm[cnt] = centrality_mean(data_graph_vertex[i])
end
return cm
end
function centrality_mean_pmap(range::Array{})
v_start = range[1]
v_end = range[2]
centrality_mean(v_start, v_end)
end
答案 2 :(得分:0)
来自Julia page on parallel computing:
Julia提供了一个基于消息传递的多处理环境,允许程序一次在不同的内存域中的多个进程上运行。
如果我解释这一点,Julia的并行性需要消息传递来同步进程。如果每个单独的进程只做了一点工作,然后进行消息传递,那么计算将由消息传递开销主导,而不做任何工作。
我无法从你的代码中看到,而且我不太清楚朱莉娅,看看并行性中断的地方。但是你有一个很复杂的图表可能会在多个流程中无处不在地传播。如果他们需要跨越图形链接进行交互,那么您将拥有这种开销。
您可以通过将图表的分区预先计算为大致相同的大小,高度内聚的区域来修复它。我怀疑分手需要您已经想要做的相同类型的复杂图形处理,因此您可能会遇到鸡和蛋问题。
Julia可能会为您提供错误的并行模型。您可能需要共享地址空间,以便遍历图形的线程不必使用消息来遍历弧。