如何提高图中心性计算并行性能?

时间:2014-12-10 08:08:34

标签: performance graph parallel-processing julia

在计算图形中心性的代码并行化后,我的性能下降了。图形相对较大,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

3 个答案:

答案 0 :(得分:1)

我猜这与并行性无关。您的第二个centrality_mean方法无法确定grv_startv_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可能会为您提供错误的并行模型。您可能需要共享地址空间,以便遍历图形的线程不必使用消息来遍历弧。