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
simplify stack in dfs #6366
simplify stack in dfs #6366
Conversation
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 personally find it harder to reason about with the depth tracked separately outside of the stack, but that's admittedly pretty subjective. Out of curiosity what's the primary motivation for the proposed change?
From the speed point of view I measured from a tiny 3% better to a somewhat more relavant 15% better in the best case, so not that much. I think that memory-wise the stack this way consumes probably 1/3 less space, which is good given that it is the second data structure in this method in terms of memory occupied, but all in all, it is not much of a difference, the primary motivation was that I found it interesting to think about this optimization :D |
Well, at least 1/3 the number of objects, which is not the same as memory since On mainThis PRSo that's a 400 MB reduction in total usage and 300 fewer allocations for this call (which should help with performance), but accounting for the other changes it ends up being about 300 MB saving for Sorry for the bad screengrab quality - to reproduce (though you may want to use fewer nodes for systems with <16GB ram):
Footnotes
|
that's interesting, thanks for the flamegraph and the explanation on how to reproduce it! Do you think it is worth it? I don't consider the code less readable, it is actually clearer with this edit at what level we are in the dfs in my opinion |
This is inherently subjective - there is no "right" way to do it. The reason I find it more readable is that the state (i.e. the current depth) is tracked with the current node, whereas the proposed change tracks the state separately. Though again, this is entirely subjective so we're unlikely to make any progress discussing on this front :) A minor improvement to memory consumption is a more concrete justification for the proposed change. I'd like to hear what others think! |
indeed I was too assertive, I meant to say that it is easier to track when we change levels in the dfs, but anyway I agree that the objective motivation for the change has to be evaluated in relation to the little improvement in memory/time! |
This makes sense to me. 👍 |
FWIW I agree that a local value is preferable to loading the stack down with longer state tuples. I prefer a slightly different idiom than the one used here: while stack:
parent, children = stack[-1]
try:
child = next(children)
....
except StopIteration:
stack.pop()
# without try/except
while stack:
parent, children = stack[-1]
for child in children:
...
break
else:
stack.pop() In avoiding exceptions, the |
@Tortar I have implemented my suggestion here: https://github.com/boothby/networkx/tree/tortar-patch-8 -- I would push the changes to your repo but the pre-commit hooks are currently faulty in your branch (not your fault; easily resolved with Note that we only need to while stack:
parent, children = stack[-1]
for child in children:
if child in visited:
yield parent, child, "nontree"
else:
yield parent, child, "forward"
visited.add(child)
if depth_now < depth_limit:
stack.append((child, iter(G[child])))
depth_now += 1
break
else:
yield parent, child, "reverse-depth_limit"
else:
stack.pop()
depth_now -= 1
if stack:
yield stack[-1][0], parent, "reverse" |
thanks @boothby for the improvements! will push them in a moment and make you a co-author of the pull :-) |
Co-authored-by: boothby <kelly.r.boothby@gmail.com>
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.
There is also #6714 which looks to fix #6479...
I think we can/should go ahead and merge these changes as a first step -- and then address the strange behavior seen with depth-limited search.
I'm approving this and I'll have @boothby press the green button. There are a lot of PRs related to this and the order may impact merge conflicts. Best to have control in those cases I guess. :}
Thanks!
* simplify stack in dfs * Update depth_first_search.py * Update depth_first_search.py * Add suggestions Co-authored-by: boothby <kelly.r.boothby@gmail.com> * Update depth_first_search.py --------- Co-authored-by: boothby <kelly.r.boothby@gmail.com>
* simplify stack in dfs * Update depth_first_search.py * Update depth_first_search.py * Add suggestions Co-authored-by: boothby <kelly.r.boothby@gmail.com> * Update depth_first_search.py --------- Co-authored-by: boothby <kelly.r.boothby@gmail.com>
* simplify stack in dfs * Update depth_first_search.py * Update depth_first_search.py * Add suggestions Co-authored-by: boothby <kelly.r.boothby@gmail.com> * Update depth_first_search.py --------- Co-authored-by: boothby <kelly.r.boothby@gmail.com>
The stack in dfs can have only parent and children, the depth can be controlled from the outside of it, making the function slightly better in terms of time and space, but also readibility in my opinion :-)