Skip to content

Commit

Permalink
Speed up StableTopologicalSort
Browse files Browse the repository at this point in the history
The biggest savings come from just calling sort less often. Instead of
in the inner-most loop, we call it after processing the whole map of
predecessors.

The other portion is just not appending duplicate items to the queue. I
do this in a simple way by maintaining a set of queued things (similar
to the visited set).

I think the "proper" way to fix this would be to use a sorted list for
queue instead of sorting after every pass, but I'm too lazy to implement
that, and I don't think it's part of the go stdlib.

For our use case, on my machine, StableTopologicalSort took:

Before: 230.00s
After:    0.03s

Signed-off-by: Jon Johnson <[email protected]>
  • Loading branch information
jonjohnsonjr committed Jun 5, 2023
1 parent c0f5621 commit 1cfb869
Showing 1 changed file with 14 additions and 5 deletions.
19 changes: 14 additions & 5 deletions dag.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,12 @@ func StableTopologicalSort[K comparable, T any](g Graph[K, T], less func(K, K) b
}

queue := make([]K, 0)
queued := make(map[K]struct{})

for vertex, predecessors := range predecessorMap {
if len(predecessors) == 0 {
queue = append(queue, vertex)
queued[vertex] = struct{}{}
}
}

Expand All @@ -112,14 +114,21 @@ func StableTopologicalSort[K comparable, T any](g Graph[K, T], less func(K, K) b
for vertex, predecessors := range predecessorMap {
delete(predecessors, currentVertex)

if len(predecessors) == 0 {
queue = append(queue, vertex)
if len(predecessors) != 0 {
continue
}

sort.Slice(queue, func(i, j int) bool {
return less(queue[i], queue[j])
})
if _, ok := queued[vertex]; ok {
continue
}

queue = append(queue, vertex)
queued[vertex] = struct{}{}
}

sort.Slice(queue, func(i, j int) bool {
return less(queue[i], queue[j])
})
}

gOrder, err := g.Order()
Expand Down

0 comments on commit 1cfb869

Please sign in to comment.