Skip to content
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

Speed up GDScript::get_must_clear_dependencies() #85603

Merged

Conversation

eldidou
Copy link
Contributor

@eldidou eldidou commented Dec 1, 2023

get_must_clear_dependencies() has a N^3log(N) time complexity, and this can very quickly slow down the quitting process as more gdscripts are added in a project. This change improves it to N^2log(N).
Instead of using all the inverted dependencies, we do the same with all (non-inverted) dependencies, which is N times faster.

Fixes #85435

@eldidou eldidou requested a review from a team as a code owner December 1, 2023 10:48
@akien-mga akien-mga changed the title speedup GDScript::get_must_clear_dependencies() Speedup GDScript::get_must_clear_dependencies() Dec 1, 2023
@akien-mga
Copy link
Member

For the record, your commit seems not to be linked to your GitHub account. See: Why are my commits linked to the wrong user? for more info.

@akien-mga akien-mga added this to the 4.3 milestone Dec 1, 2023
@akien-mga akien-mga added the cherrypick:4.2 Considered for cherry-picking into a future 4.2.x release label Dec 1, 2023
@eldidou eldidou force-pushed the speed-up-get-must-clear-dependencies branch from f53ff0e to 1963d61 Compare December 1, 2023 11:44
@eldidou
Copy link
Contributor Author

eldidou commented Dec 1, 2023

Sorry about that (I'm quite new to this process), now it should be linked to the correct account.

@eldidou eldidou force-pushed the speed-up-get-must-clear-dependencies branch from 1963d61 to 9657341 Compare December 1, 2023 12:36
@kleonc
Copy link
Member

kleonc commented Dec 2, 2023

get_must_clear_dependencies() has a N^3_log(N) time complexity, and this can very quickly slow down the quitting process as more gdscripts are added in a project. This change improves it to N^2_log(N).

This is an improvement indeed but I think GDScriptLanguage::finish() shouldn't lead to any checks for dependencies like that? 🤔 On finalization all scripts are to-be-cleared anyway, so there's no point to check/clear the full chain of dependencies for every script. Iterating over every script and clearing only its own refs to any other script should be sufficient to get rid of all cross dependencies. Meaning the finalization should be doable linearly (O(N)).

Note that I'm not really familiar with the GDScript internals so maybe I'm missing something and it's not that simple?

Anyway, could potentially be done in another PR, this is already an improvement.

@YuriSizov YuriSizov changed the title Speedup GDScript::get_must_clear_dependencies() Speed up GDScript::get_must_clear_dependencies() Dec 4, 2023
@adamscott
Copy link
Member

This is an improvement indeed but I think GDScriptLanguage::finish() shouldn't lead to any checks for dependencies like that?

But this isn't about GDScriptLanguage::finish(), isn't it?

@RevoluPowered
Copy link
Contributor

Can confirm that with The Mirror we see a hugely expensive shutdown time because of this function.

Benchmarked using superluminal profiler and it showed this function was consuming 100% CPU for over 90 seconds during shutdown.

I'll test this fix and report back the app close time :)

@kleonc
Copy link
Member

kleonc commented Dec 4, 2023

But this isn't about GDScriptLanguage::finish(), isn't it?

It is and it isn't, depends how you look at it. 🙃

#85435 which this PR aims to fix is specifically about slow quiting (both editor/runtime) when there are many cross referencing/dependant scripts. This is caused by the slow execution of GDScriptLanguage::finish(). And why is GDScriptLanguage::finish() so slow? Because it's calling GDScript:clear() many times and GDScript::clear() is slow with the current implementation.

So this PR makes GDScript:clear() faster which is good on its own, regardless of what calls GDScript::clear(). 👍 This of course makes also GDScriptLanguage::finish() faster as a result, which improves the quitting time.

What I've said in #85603 (comment) is that GDScriptLanguage::finish() can potentially be made faster as it shouldn't need to call GDScript:clear() in its current form at all, because on finalizing all scripts clearing non-direct script dependencies shouldn't be needed (and that's what's done as a part of GDScript:clear()). Clearing only direct dependencies for all scripts would also result in no cross dependencies in the end and would be faster.

TLDR: this PR is good on its own but GDScriptLanguage::finish() could still be made faster.


BTW Yeah, would be nice if some benchmark times would be provided for before/after. In the end even if it helps with #85435 (it does) it still might need be considered as not fixing it if the timings are still not satisfying.

@adamscott adamscott requested review from vnen and dalexeev December 4, 2023 16:02
Copy link
Member

@adamscott adamscott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eldidou Thanks for your PR! For a first PR, that's a nice one!

I'm trying to see if there's things that could break with your implementation, but the logic seems fine. The idea to skip the dependencies if they don't include the current script is a good idea. It does what I thought to do when I implemented inverse dependency checks.

I asked @vnen and @dalexeev to review it, but so far, I like it.

@RevoluPowered
Copy link
Contributor

Can confirm that with The Mirror we see a hugely expensive shutdown time because of this function.

Benchmarked using superluminal profiler and it showed this function was consuming 100% CPU for over 90 seconds during shutdown.

I'll test this fix and report back the app close time :)

4 seconds to close the editor (previously multiple minutes)
4 seconds to close the app (previously 2 minutes or more)

This makes it much more deterministic for me.

Tested on windows.

@eldidou eldidou force-pushed the speed-up-get-must-clear-dependencies branch from 9657341 to 0780278 Compare December 9, 2023 11:54
get_must_clear_dependencies() has a N^3*log(N) time complexity, and this can very quickly slow down the quitting process as more gdscripts are added in a project.
This change improves it to N^2*log(N).
Instead of using all the inverted dependencies, we do the same with all (non-inverted) dependencies, which is N times faster.

Fixes godotengine#85435
@eldidou eldidou force-pushed the speed-up-get-must-clear-dependencies branch from 0780278 to 0d77c3e Compare December 9, 2023 12:02
Copy link
Member

@adamscott adamscott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems fine to me. It improved my logic and I don't see how the new logic would not cover my old one.

Especially with all the tests passing, I'm OK merging this very soon.

@YuriSizov YuriSizov merged commit f4b32b2 into godotengine:master Dec 16, 2023
15 checks passed
@YuriSizov
Copy link
Contributor

Thanks! And congratulations on your first merged Godot contribution! That's a very cool way to enter the space 🙂

@eldidou eldidou deleted the speed-up-get-must-clear-dependencies branch December 17, 2023 11:34
@eldidou
Copy link
Contributor Author

eldidou commented Dec 17, 2023

Thanks! Glad to help 🙂

@adamscott
Copy link
Member

@eldidou Don't hesitate to join our developer's chat. We have a GDScript public meeting tomorrow.

@YuriSizov YuriSizov removed the cherrypick:4.2 Considered for cherry-picking into a future 4.2.x release label Jan 25, 2024
@YuriSizov
Copy link
Contributor

Cherry-picked for 4.2.2.

@mihe
Copy link
Contributor

mihe commented Jul 18, 2024

Seeing as how this slowdown still remains a problem when you go into the hundreds of scripts (with more complex dependencies), I've gone ahead and made the change that was talked about earlier in this thread, by simply skipping the must_clear_dependencies work altogether when in GDScriptLanguage::finish. You'll find the change in #94505.

It seemed like a straight-forward enough of a change, and passes all the unit tests and whatnot, but I'd appreciate some more eyes on it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Exponentially slow quitting time with dependencies between (gdscript) scripts
8 participants