-
-
Notifications
You must be signed in to change notification settings - Fork 21.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
Node::add_child is extremely inefficient when running inside the editor #54177
Comments
This may not be a good idea, because the parent then needs to sync that up on every rename and possibly reorder. Usually it's not a great idea to keep multiple connected sets of data in sync in parallel as it's easy to get them out of sync, especially for future maintainers and contributors who may not know first hand that such system is in place. However, a solution to disable renames for bulk additions can be created, as that would directly address your problem. See also the discussion on godotengine/godot-proposals#3165.
Most PRs must be made against the |
Im not quite sure why the parent would need to sync with the child. The function only looks at direct children of the current node. So each node would just need to maintain that set individually. A different approach would be to just store all nodes in a map in the first place. But that would probably be a larger change overall. |
This is not what I was talking about. Children can be renamed by anything. They can name themselves, they can be renamed from user scripts. As such, parent needs to keep its internal list in sync with the actual state of the node tree; something that we don't need to do now when we live fetch all the nodes. |
Isn't it well-defined what functions change the node tree of a parent and what functions are invoked on name changes? From a purely conceptual understanding, the proposed change doesn't sound too unreasonable to me. Using a majority of CPU time on string lookups seems unreasonable for a game engine to me though. Especially since this behavior is completely unexpected and opaque from the outside without profiling and knowing the codebase. The issue causes major performance degradation for our import script, pushing the total required time up to minutes. For comparisons, if the mentioned check was deactivated, the same process would only take mere seconds. While I understand your concerns about maintainability to some degree, a proposed "bulk_add" method requires end-users to know about these (internal) specifics. Most likely, many would intuitively use |
@sehoffmann Again, my point wasn't that it's unknown when the rename happens (the node fires a signal on rename after all). My point was that keeping a separate list in sync is an added cost and a point of breakage if you are not careful. It's a pattern that can lead to errors in the engine when someone forgets to account for that second list. As for method discoverability, that's moot. That's what documentation is for and if one doesn't read it, we cannot help them. And in the docs, we can cross link everything and add notes. Plus, it's not a common case to add thousands of nodes, this is specific enough that if you understand that's a problem, you likely know that reading docs is useful. |
@pycbouh I understand your point, and as I said, I understand your concerns to some degree as well. To clarify my point though: If its well-defined when renames happen, or when the tree gets modified, then you simply have to incorporate the change at these few key points. There are only two ways how somebody can potentially break this then:
In case 1) he directly stumbles upon the code, reads it, and any additional comments. If he manages to break it regardless, then nothing in the world would have been able to prevent him from doing so IMO and who knows what else he might be capable of breaking as well. Case 2) is obviously trickier, since he might not be directly aware of what side effects his change might introduce. But any developer changing a contract, especially in such a big public library, should be aware that this has a major impact. Firstly you don't do so without an important reason, and secondly, if you do, you are extra careful and rigorous about it. In the end, the decision is yours. but my opinion is that you are too defensive about this. Having a core method with |
Well, decision isn't mine, we don't use authority to decide, only rationale :) I'm just voicing my concerns as well over the naive approach. I'm all for improvements here, it would likely speed up even such basic things like loading scenes in the editor. My suggestion is just that, a suggestion. |
I think there is some misunderstanding of what is going on here, most likely due to the OP lacking context. Adding children nodes in common cases should be extremely fast because:
So, in practice for games this is not a problem. The specific problem here is when creating the scene for the first time or creating it from the editor (because when doing it from the editor, proper unique names are always created). In other words, this is a very corner case that will happen in the editor. My suggestion here is most likely that we look for a specific solution to this problem, probably a way to add a large amount of children in bulk by passing an array of nodes. |
I can't reproduce the issue with this script: func _ready():
for i in range(40000):
var node = Node.new()
add_child(node) Adds 40000 instances in less than a second. Are you calling |
No and this is only true if running in editor. In editor godot forces human readable names for all nodes as @reduz pointed out above. |
@0x0ACB I took the liberty to fix the issue name so it's clearer where the problem happens. So I think in short I would advocate for a function that lets you add a lot of children nodes in bulk (and they require legible unique names). That should make validation much faster as a hashmap or similar can be used to validate everything together. |
What does 'in editor' mean here? Tool scripts? Plugins? |
All code that is running inside the editor so yes. Tools and Editor Plugins for example |
Maybe a: add_children(TypedArray<Node> p_children,bool p_legible_unique_names=false) might be good? Documentation can be explicit on this being faster when adding large number of nodes, specially when they lack names and legible unique names is specified, or when they come with names and they need to be validated. |
Yes that sounds like a good alternative |
So I think the problem here is |
Godot version
3.x and 4.x maybe all
System information
Windows 10, Ubuntu 20.4 LTS
Issue description
I was having some issues importing scenes with a semi large amount of (direct child) Nodes (4000). Using Visual Studio profiling I found that the function costing 96.7% of that time was
Node::add_child
which I found extremely curious since I was constructing whole meshes before that which only took up 0.5% of the time. Digging a bit deeper the issue stems fromNode::_generate_serial_child_name
which will ALWAYS iterate over ALL children at least once! In the case there is already a child with the name assigned to the Node this may happen even more often.An easy fix for this would be to use a
Set
with all child names for the current Node; reducing the amount of lookups needed. I would submit a pull request for this but I am not sure for which branch I should do this (this affects at least 3.x and 4.x and I would like to see this fixed in the version that can be downloaded directly from the godot Website [3.3.4]).Steps to reproduce
Generate a lot of direct child nodes in an editor (import) script.
Minimal reproduction project
No response
The text was updated successfully, but these errors were encountered: