-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
scc algorithm #235
scc algorithm #235
Conversation
P.S.: I'll need some time to review the implementation due to upcoming travel. |
I was actually going to ask about how to add things to regression suite, since it would be useful for |
I am actually ashamed of this script, and currently, I think rewriting a very simple benchmarking suite and getting rid of https://github.com/haskell-perf/graphs would be the best option. I would love to gave Anyway, adding |
@nobrakal It's always possible to improve things but it's not always necessary :-) The current implementation may be not pretty but the script does work and is very useful! If you could add |
@jitwit There are some merge conflicts, please resolve. |
I re-ran the benchmarks and the results were less favorable in some cases for AdjacencyMaps. The graphs where the current version of the new implementation does poorly are those with a high number of small SCCs, the graphs were it does well have a small number of large SCCs. The AdjacencyIntMap version is much faster regardless. The link to the criterion benchmarks from before has the current results. |
@jitwit I'm looking at this link which corresponds to https://jitwit.github.io/criterion/scc-bench.html This is better across all benchmarks, but can you show the results for |
By the way, the performance regression suite reports |
Yeah those names are unclear, I changed them to KL-alga, AM-alga, AIM-alga. I've looked closer, and the The result of the |
The benches are updated with the "hybrid" approach. PS. apologies for the messy code, I was aiming to see if the approach would even pay off! |
I tried the searches on a twitter graph from SNAP: https://snap.stanford.edu/data/ego-Twitter.html (~81k vertices ~1.7m edges) and the results are good: |
@jitwit Very impressive, especially the Twitter benchmark! I think it's worth adding it to the benchmark suite at https://github.com/haskell-perf/graphs. I'm concerned with the complexity of the resulting code though :( How much improvement is your hybrid approach bringing percentage-wise? If it's not too much then I think I'd prefer to remove the special tricks and instead focus on making a faster |
The number of passes over the graph with
is sort of what is needed. There are still a few cases where the current approach is slower than the old implementation, but for the most part it's faster. |
I also added a CPP statement to get rid of the redundant |
I think you can just import As long as there are no warnings in GHC 8.6.5, I'm happy! |
@jitwit I think I've noticed a few more places where we can save some time. Could you please have a look? |
Much better results! https://jitwit.github.io/benchmarks/criterion/scc-bench.html Just building the inner graphs (no difference lists) makes a big difference. |
@jitwit Excellent! And the code got simpler too. Looks like we're mostly beating KL now, with only one scenario where we're slightly behind. I think we can tolerate this. I think we can merge the PR now unless you'd like to do any further improvements. |
On larger graphs the performance gain is even bigger. An acyclic facebook network graph gets 200ms to 600ms (4000 vertices 90000 edges) and the twitter one gets 6.4s to 60s (80000 vertices 1,700,000 edges) The part I'd maybe want to improve is the incomplete pattern on |
@jitwit This is pretty cool! I guess when you're benchmarking KL you are including the overhead of converting an adjacency map to an array representation. What happens if you start with a graph stored in KL-style array? Is your implementation still significantly faster? I don't really mind |
I haven't done a proper benchmark yet, but from a quick profile quite a lot of the time is spent in Since the condensation construction is interleaved with the pre-order traversal, I'm not totally sure how the implementations should be compared. There's definitely an algorithmic win from avoiding the quadratic induce, besides the benefits of not converting representation to arrays. |
@jitwit Note that your implementation uses |
It looks like the There is also an inverse relation between the size of the components and how many components there are, which also helps limit the cost of |
Given
|
@jitwit Adjacency maps do not use alga/src/Algebra/Graph/AdjacencyMap.hs Lines 167 to 172 in 43ea348
The first check compares Pretty cool! |
Ah! Sorry, should have checked the |
Should I delete the old |
Indeed, I forgot that
No, let's keep it. It's always good to have a good testsuite. I'm pretty sure we'll be doing further tweaks to the implementation and it's good to know that (at least some) bugs will be caught. |
Makes sense! And yeah, would be interesting to see if caching for |
@jitwit OK, merged! Many thanks for your hard work and for tolerating such long reviews :-) |
Implementation of scc algorithm for adjacency maps and adjacency int maps based on https://en.wikipedia.org/wiki/Path-based_strong_component_algorithm.
Avoids using Data.Graph, and benchmarks indicate that this pays off https://jitwit.github.io/criterion/scc-bench.html. In report, old-alga corresponds to alga's KL implementation, new-alga the intmap implementation, ord-alga the new implementation for AMs. Graphs used are the real-world ones from haskell-perf/graphs. AM implementation seems to be around 10% faster and AIM version around 65% faster.
There is no NonEmpty.AdjacencyIntMap module, so for now AdjacencyIntMap.Algorithm returns type
AM.AdjacencyMap AdjacencyIntMap
.