I’m trying to speed up an algorithm that computes various graph statistics. The relevant part of the code is:
struct erg{G <: AbstractArray{Bool},F <: Function} <: AbstractGraph{Int}
m::G
fs::Vector{F}
trans::G
realnodecov::Union{Dict{String,Array{Float64,1}},Nothing}
catnodecov::Union{Dict{String,Array{Any,1}},Nothing}
edgecov::Union{Array{Array{Float64,2}},Nothing}
indegree::Vector{Int64}
outdegree::Vector{Int64}
n_funcs::Int
kstars_precomputed::Array{Float64,2}
end
function change_scores(g::E, i, j) where {E <: AbstractGraph}
x = zeros(g.n_funcs)
func_list = g.fs
for k = 1:g.n_funcs
x[k] = func_list[k](g, i, j)
end
return x
end
In general, the different graph statistics (represented by a vector of functions g.fs
) can be anything; in practice, many of them are either O(1) functions such as:
# Toggle and edge and get the changes in total number of edges; really basic, but useful.
function delta_edge(g::E, s, r) where {E <: AbstractGraph}
if !has_edge(g, s, r)
return 1.0
else
return -1.0
end
end
# Toggle edge from s -> r, and get changes in count of reciprocal dyads
function delta_mutual(g::E, s, r) where {E <: AbstractGraph}
if !g.trans[s, r]
return 0.0
elseif !g.m[s, r]
return 1.0
else
return -1.0
end
end
or contain some sort of loop over the vertices of the graph, such as
function delta_ttriple(g::E, s, r) where {E <: AbstractGraph}
x = 0
@inbounds for i in vertices(g)
x +=
(g.trans[i, r] * g.trans[i, s]) +
(g.m[i, r] * g.trans[i, s]) +
(g.m[i, r] * g.m[i, s])
end
if !has_edge(g, s, r)
return convert(Float64, x)
else
return -convert(Float64, x)
end
end
The functions don’t mutate the graph, and can be computed in parallel (though I was not able to get any speedups from doing so). Is there a way to “annotate” functions of the second kind (the ones that loop over the vertices of the graph) in a way that would cause their for-loops to be “fused”? I’m not sure if this is the exact terminology - my goal is improve performance by reducing the number of passes over the vertices of the graph (ideally to a single pass, even if there are several functions of the second kind in g.fs
).
Any other performance tips would be appreciated, as well - the change_scores
function is the computational bottleneck of the algorithm, so even a small speedup is important.