Skip to content

Commit 9fbbce4

Browse files
gdalleamontoison
authored andcommitted
Speed up dynamic degree orders
1 parent 5c9b8c7 commit 9fbbce4

1 file changed

Lines changed: 82 additions & 28 deletions

File tree

src/order.jl

Lines changed: 82 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -129,23 +129,38 @@ Instance of [`AbstractOrder`](@ref) which sorts vertices using a dynamically com
129129
"""
130130
struct DynamicDegreeBasedOrder{degtype,direction} <: AbstractOrder end
131131

132-
struct DegreeBuckets{B}
132+
struct DegreeBuckets
133133
degrees::Vector{Int}
134-
buckets::B
134+
bucket_storage::Vector{Int}
135+
bucket_low::Vector{Int}
136+
bucket_high::Vector{Int}
135137
positions::Vector{Int}
136138
end
137139

138140
function DegreeBuckets(degrees::Vector{Int}, dmax)
139-
buckets = Dict(d => Int[] for d in 0:dmax)
141+
# number of vertices per degree class
142+
deg_count = zeros(Int, dmax + 1)
143+
for d in degrees
144+
deg_count[d + 1] += 1
145+
end
146+
# bucket limits
147+
bucket_high = cumsum(deg_count)
148+
bucket_low = vcat(0, @view(bucket_high[1:(end - 1)]))
149+
bucket_low .+= 1
150+
# assign each vertex to the correct position inside its degree class
151+
bucket_storage = similar(degrees, Int)
140152
positions = similar(degrees, Int)
141-
for v in eachindex(degrees, positions)
153+
for v in eachindex(positions, degrees)
142154
d = degrees[v]
143-
push!(buckets[d], v) # TODO: optimize
144-
positions[v] = length(buckets[d])
155+
positions[v] = bucket_high[d + 1] - deg_count[d + 1] + 1
156+
bucket_storage[positions[v]] = v
157+
deg_count[d + 1] -= 1
145158
end
146-
return DegreeBuckets(degrees, buckets, positions)
159+
return DegreeBuckets(degrees, bucket_storage, bucket_low, bucket_high, positions)
147160
end
148161

162+
maxdeg(db::DegreeBuckets) = length(db.bucket_low) - 1
163+
149164
function degree_increasing(; degtype, direction)
150165
increasing =
151166
(degtype == :back && direction == :low2high) ||
@@ -155,43 +170,80 @@ end
155170

156171
function mark_ordered!(db::DegreeBuckets, v::Integer)
157172
db.degrees[v] = -1
158-
db.positions[v] = -1
173+
db.positions[v] = typemin(Int)
159174
return nothing
160175
end
161176

162177
already_ordered(db::DegreeBuckets, v::Integer) = db.degrees[v] == -1
163178

164179
function pop_next_candidate!(db::DegreeBuckets; direction::Symbol)
165-
(; buckets) = db
180+
(; bucket_storage, bucket_low, bucket_high) = db
181+
dmax = maxdeg(db)
166182
if direction == :low2high
167-
candidate_degree = maximum(d for (d, bucket) in pairs(buckets) if !isempty(bucket))
183+
candidate_degree = dmax + 1
184+
for d in dmax:-1:0
185+
if bucket_high[d + 1] >= bucket_low[d + 1] # not empty
186+
candidate_degree = d
187+
break
188+
end
189+
end
168190
else
169-
candidate_degree = minimum(d for (d, bucket) in pairs(buckets) if !isempty(bucket))
191+
candidate_degree = -1
192+
for d in 0:dmax
193+
if bucket_high[d + 1] >= bucket_low[d + 1] # not empty
194+
candidate_degree = d
195+
break
196+
end
197+
end
170198
end
171-
candidate_bucket = buckets[candidate_degree]
172-
candidate = pop!(candidate_bucket)
199+
high = bucket_high[candidate_degree + 1]
200+
candidate = bucket_storage[high]
201+
bucket_storage[high] = -1
202+
bucket_high[candidate_degree + 1] -= 1
173203
mark_ordered!(db, candidate)
174204
return candidate
175205
end
176206

177207
function update_bucket!(db::DegreeBuckets, v::Integer; degtype, direction)
178-
(; degrees, buckets, positions) = db
208+
(; degrees, bucket_storage, bucket_low, bucket_high, positions) = db
179209
d, p = degrees[v], positions[v]
180-
bucket = buckets[d]
210+
low, high = bucket_low[d + 1], bucket_high[d + 1]
181211
# select previous or next bucket for the move
182-
d_new = degree_increasing(; degtype, direction) ? d + 1 : d - 1
183-
bucket_new = buckets[d_new]
184-
# put v at the end of its bucket by swapping
185-
w = bucket[end]
186-
bucket[p] = w
187-
positions[w] = p
188-
bucket[end] = v
189-
positions[v] = length(bucket)
190-
# move v from the old bucket to the new one
191-
@assert pop!(bucket) == v
192-
push!(bucket_new, v)
193-
degrees[v] = d_new
194-
positions[v] = length(bucket_new)
212+
if degree_increasing(; degtype, direction)
213+
# put v at the end of its bucket by swapping
214+
w = bucket_storage[high]
215+
bucket_storage[p] = w
216+
bucket_storage[high] = v
217+
positions[w] = p
218+
positions[v] = high
219+
# move v to the beginning of the next bucket (mind the gap)
220+
d_new = d + 1
221+
low_new, high_new = bucket_low[d_new + 1], bucket_high[d_new + 1]
222+
bucket_storage[low_new - 1] = v
223+
# update v stats
224+
degrees[v] = d_new
225+
positions[v] = low_new - 1
226+
# grow next bucket to the left, shrink current one from the right
227+
bucket_low[d_new + 1] -= 1
228+
bucket_high[d + 1] -= 1
229+
else
230+
# put v at the beginning of its bucket by swapping
231+
w = bucket_storage[low]
232+
bucket_storage[p] = w
233+
bucket_storage[low] = v
234+
positions[w] = p
235+
positions[v] = low
236+
# move v to the end of the previous bucket (mind the gap)
237+
d_new = d - 1
238+
low_new, high_new = bucket_low[d_new + 1], bucket_high[d_new + 1]
239+
bucket_storage[high_new + 1] = v
240+
# update v stats
241+
degrees[v] = d_new
242+
positions[v] = high_new + 1
243+
# grow previous bucket to the right, shrink current one from the left
244+
bucket_high[d_new + 1] += 1
245+
bucket_low[d + 1] += 1
246+
end
195247
return nothing
196248
end
197249

@@ -205,6 +257,7 @@ function vertices(
205257
end
206258
db = DegreeBuckets(degrees, maximum_degree(g))
207259
π = Int[]
260+
sizehint!(π, nb_vertices(g))
208261
for _ in 1:nb_vertices(g)
209262
u = pop_next_candidate!(db; direction)
210263
direction == :low2high ? push!(π, u) : pushfirst!(π, u)
@@ -241,6 +294,7 @@ function vertices(
241294
maxd2 = maximum(degrees_dist2)
242295
db = DegreeBuckets(degrees, maxd2)
243296
π = Int[]
297+
sizehint!(π, n)
244298
visited = falses(n)
245299
for _ in 1:nb_vertices(g, Val(side))
246300
u = pop_next_candidate!(db; direction)

0 commit comments

Comments
 (0)