-
-
Notifications
You must be signed in to change notification settings - Fork 21.6k
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 classnames enumeration #101489
base: master
Are you sure you want to change the base?
Optimize classnames enumeration #101489
Conversation
#define CACHE_FILE_NAME "filesystem_cache8" | ||
#define CACHE_FILE_NAME "filesystem_cache10" |
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.
Do we follow Microsoft versioning going from 8 to 10? :P
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.
In the version for 4.3, I raised to filesystem_cache9
. In case someone switches between 4.3 and 4.4 for some reason to edit a single project, that'd ensure compatibility. Or that was my reasoning at least.
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 guess that if the change is the same in both 4.3 and 4.4, and both were on version 8, they could both use version 9 and stay compatible.
Otherwise if we do have an incompatibility between the two implementations I agree they should use different numbers. It's bikeshedding, but I may argue it might be better for the master
version to stay sequential, and for potential feature backports to take care of special casing compatibility, e.g. by introducing a suffix system for version changes in stable branches (e.g. filesystem_cache8b
). But that's really nitpicky and not a hill I'd die on :P I'm also fine with 9/10.
The only (very theoretical) case where the current approach could be problematic is if we then make another version change in both 4.3 and 4.4, and we need to find where to go from 9 and 10. We'd end up with something like 9 -> 11 in 4.3 and 10 -> 12 in 4.4 or similar, which starts to be a bit weird.
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.
Maybe we could just fork both forever in a way that they can't clash. More detailedly, we'd use something like filesystem_cache0001
for master and filesystem_cache1001
for 4.3.
Alternatively, we can backport #100729, which is the one adding a need for checking 'is tool` so we can keep both file system cache formats the same for now...
9cddbcf
to
462966e
Compare
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 pretty good to me overall from reading the code. Haven't tested.
@@ -390,7 +390,7 @@ void ScriptServer::global_classes_clear() { | |||
inheriters_cache.clear(); | |||
} | |||
|
|||
void ScriptServer::add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path) { | |||
void ScriptServer::add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path, bool p_is_abstract, bool p_is_tool) { |
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 have a particular suggestion for improvement, but I can't help wonder if there won't be other types of qualifiers we need to keep track on in the future, leading to further boolean parameters. It's probably fine, though the GDExtension compatibility might need some care.
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.
Since I've added just two, I've kept them as separate args here and as separate fields in the classnames cache. For future extensions, I'd say moving to some kind of bitset or similar would be advisable. I'll check the GDExtension compat.
@@ -672,7 +672,7 @@ class ScriptLanguageExtension : public ScriptLanguage { | |||
|
|||
GDVIRTUAL1RC_REQUIRED(Dictionary, _get_global_class_name, const String &) | |||
|
|||
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const override { | |||
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr, bool *r_is_abstract = nullptr, bool *r_is_tool = nullptr) const override { |
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 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'm always—inexplicably—fuzzy about this GDExtension compatibility topic, like I used to be with GDNative... So, let's see if I'm getting this straight. This is the only related binding I'm being able to find: GDVIRTUAL_BIND(_get_global_class_name, "path");
Given it only cares about the first parameter, looks like we are lucky that second parameter onwards don't have an impact. Is that right?
CC @YYF233333 who has been working on #101435 recently, which seems partially related (both are probably relevant to merge but good to cross-review). |
This one change the sign of |
462966e
to
726715a
Compare
726715a
to
1b07897
Compare
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.
Tested locally, it works as expected. Code looks good to me.
Testing project: test_class_names.zip
This project includes a generate.sh
script that can be used to create projects with lots of class_name
s with custom icons.1
PC specifications
- CPU: Intel Core i9-13900K
- GPU: NVIDIA GeForce RTX 4090
- RAM: 64 GB (2×32 GB DDR5-5800 C30)
- SSD: Solidigm P44 Pro 2 TB
- OS: Linux (Fedora 41)
Using an optimized (LTO) editor build.
In a project with 10,000 named classes that have their own icon each, this is the delay before the Create New Node dialog shows up after clicking the button:
Cold:
Before | After |
---|---|
4.1 seconds | 1.6 seconds |
Warm:
Before | After |
---|---|
0.9 seconds | 0.3 seconds |
Footnotes
-
Same project without the pregenerated classes in case you have trouble extracting it: test_class_names.zip ↩
Unfortunately this breaks custom icons for scripts that aren't global classes. EDIT: |
ERR_FAIL_COND_V(!global_classes.has(p_class), false); | ||
return global_classes[p_class].is_abstract; |
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.
ERR_FAIL_COND_V(!global_classes.has(p_class), false); | |
return global_classes[p_class].is_abstract; | |
GlobalScriptClass *global_class = global_classes.getptr(p_class); | |
ERR_FAIL_COND_V(!global_class, false); | |
return global_class->is_abstract; |
Same below.
@@ -513,6 +527,8 @@ void ScriptServer::save_global_classes() { | |||
d["path"] = global_classes[E].path; | |||
d["base"] = global_classes[E].base; | |||
d["icon"] = class_icons.get(E, ""); | |||
d["is_abstract"] = global_classes[E].is_abstract; |
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.
It would be better if global_classes[E]
was performed only once.
const GlobalScriptClass &global_class = global_classes[E];
@@ -312,7 +326,7 @@ class EditorFileSystem : public Node { | |||
void _update_pending_scene_groups(); | |||
void _get_all_scenes(EditorFileSystemDirectory *p_dir, HashSet<String> &r_list); | |||
|
|||
String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const; | |||
EditorFileSystem::ScriptClassInfo _get_global_script_class(const String &p_type, const String &p_path) const; |
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.
EditorFileSystem::ScriptClassInfo _get_global_script_class(const String &p_type, const String &p_path) const; | |
ScriptClassInfo _get_global_script_class(const String &p_type, const String &p_path) const; |
if (fc->type.is_empty()) { | ||
if (fc->type == StringName()) { |
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.
StringName does have is_empty()
method, so this change is unnecessary.
@@ -255,9 +254,9 @@ class EditorData { | |||
void script_class_save_icon_paths(); | |||
void script_class_load_icon_paths(); | |||
|
|||
Ref<Texture2D> extension_class_get_icon(const String &p_class) const; | |||
String extension_class_get_icon_path(const String &p_class) const; |
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.
What's this change for? How is returning path better if you load it anyway?
Hereby, a number of items involved in presenting icons of global classes are rewired to leverage the global class cache much better. As a consequence, the time it takes for the node create dialog and autocomplete popup to show up (especially in cold, just after opening the editor) is reduced considerably. This is especially impactful in big projects. I've been a witness of a moderately big project taking about 10 seconds for the node create dialog to be shown for it.
This PR also makes an initial commit that simplifies file system cache parsing, something one of the other commits leverages when it comes to including more attributes in the global class cache.
Benchmarking on a test project with thousands of trivial globally named GDScript and C# classes, some of them with icons, these are the results:
Cold: 4692 ms (icon loads: 10, script loads: 2268)
Hot: 1810 ms (icon loads: 0, script loads: 0)
Cold: 3079 ms (icon loads: 10, script loads: 756)
Hot: 698 ms (icon loads: 0, script loads: 0)
Cold: 182 ms (icon loads: 10, script loads: 0
Hot: 172 ms (icon loads: 0, script loads: 0)
Cold: 91 ms (icon loads: 10, script loads: 0)
Hot: 83 ms (icon loads: 0, script loads: 0)
As the data above shows, one of the reasons for the speedup is that the new implementation doesn't need to load (potentially multiple times) all the scripts involved. The number of icon loads was measures as well to ensure no regressions in that area were caused by this PR. That is, the editor script icon cache is still used as most as possible.
Besides everything explained above, this PR removes part of the extra work the involved code was doing (such as checking the deprecated custom types' icons), to behave more consistently with what the file system dock does. Given the extent of the changes, there's some risk of regressions in picking up icons for classes.
Version of this PR for 4.3 submitted as #101490.
UPDATE: I forgot to add that another potential regression is that the create node dialog won't be able to show the global classes before they have been parsed and cached, which is especially true in the cold case. If that's true and a concern, we could still fall back to parsing the script files or let the create dialog wait until the global classes cache is ready.
Fixes #101194