-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Optimize DFS while marking connected components #14022
base: main
Are you sure you want to change the base?
Conversation
The force merge time shows some improvement. |
Please let me know if we need to run full benchmark suit on this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this change, we require that connectedNodes
should not be set for any nodes. This is slightly different from before, where you could pass a partially set connectedNodes
bitset and it'll get updated with all the values.
I don't think we have a need to support the partial bitset case (do we @msokolov ?), and the optimization seems worth it. But let's document this change (that the biset should be empty) in the function docstring.
I'm not sure -- doesn't it still expect that connectedNodes is preserved between calls? The overall flow is like: find the next not-connected node, and traverse all of its connections -- it might run into an already-connected node (marked as connected in the bitset) because the relation is asymmetric. We used to continue traversing anyway although it's kind of pointless. Maybe it would tell you the size of the "rooted" component of the graph, but we don't really use this size information, so I think it's OK to early-terminate once you find something that is already rooted in an earlier component. And we still expect to remember the visited set across calls. |
could you say what data set you used here -- is this random vectors? If so, it would be great to use some non-random vectors so we can have realistic expectations for impact |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall - are you able to address the comments? It's probably OK as is, but it would be great if we could remove the empties and address the testing question
@@ -163,6 +164,10 @@ private static Component markRooted( | |||
throws IOException { | |||
// Start at entry point and search all nodes on this level | |||
// System.out.println("markRooted level=" + level + " entryPoint=" + entryPoint); | |||
if (connectedNodes.get(entryPoint)) { | |||
return new Component(entryPoint, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should never happen, right? because we enter with the next non-connected node. Can we add an assert false
here before the return statement so we catch during testing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh wait, this can happen because we iterate over all the entryPoints. Q: do we need this zero-size component for anything? Can we recall what happens with these componentws when we're done - the only purpose is to use them for reconnecting the graph. Yeah it looks like we will try to connect them again, which we could skip. Let's not add these empty components to the list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh wait, this can happen because we iterate over all the entryPoints. Q: do we need this zero-size component for anything? Can we recall what happens with these componentws when we're done - the only purpose is to use them for reconnecting the graph. Yeah it looks like we will try to connect them again, which we could skip. Let's not add these empty components to the list.
I don't think we are adding the empty components to the list though. We are adding to the list with the total of the entryPoints for that level (which seems unlikely).
In the other places we add, we start the markRooted
process with the nextClearBit, so it won't return 0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we are adding the empty components to the list though. We are adding to the list with the total of the entryPoints for that level (which seems unlikely).
But seems like a good check. Updated the PR.
Yes, this was my understanding too. |
I used the knnPerfTest to run the benchmark. It uses |
Okay, I hadn't looked at the calling function Since we skip visited nodes now, can this function be impacted if new nodes got added to the graph in between the |
Part of #14002
Stack depth was growing more than it should causing excessive allocations. This should help reduce them, and may potentially speed up process.
Benchmark while indexing 100k docs:
cc: @msokolov @vigyasharma