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

Change Node set_name to use StringName, slightly improves performance #76560

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

aaronfranke
Copy link
Member

@aaronfranke aaronfranke commented Apr 28, 2023

This PR changes the Node::set_name() method to use StringName. This code skips StringName construction in the case where the desired name is already a valid node name. This improves performance by about 15% to 20% in my testing in the case where the desired name is already valid (the common case, the happy code path).

The code has been split up into multiple methods. invalid_node_name_index() finds the index of the first character that would be invalid for a node name. Then this is passed to validate_node_name_internal() which starts replacing the invalid characters from that index. The same logic used to exist combined into one method, but having it be separate allows us to speed up Node::set_name(). The logic in invalid_node_name_index() is even slightly simpler because it doesn't have to keep track of bool valid, it just returns if invalid. However, the existing validate_node_name() is still present as a wrapper, for being exposed to GDScript and is also used in many other parts of the codebase. I also renamed some local variables for readability. I'm open to any suggestions if this can be improved further.

Test and benchmarking code:

func _ready():
	var nodes: Array = []
	for i in range(1000000):
		nodes.append(Node.new())
	var start_time = Time.get_ticks_usec()
	for i in range(1000000):
		nodes[i].set_name(&"MyStringName")
	var end_time = Time.get_ticks_usec()
	print("Time elapsed: " + str(end_time - start_time))

Results:

Without calling set_name, just accessing the array:
Time elapsed: 28971 (varies between about 25k and 30k)

This PR with valid name, skips StringName construction:
Time elapsed: 132565 (varies between about 120k and 140k)

This PR with invalid name:
Time elapsed: 220087 (varies between about 200k and 230k)

Current master with valid name:
Time elapsed: 160325 (varies between about 150k and 170k)

Current master with invalid name:
Time elapsed: 213259 (varies between about 190k and 220k)

I also did some testing with longer StringNames and the speed improvement gets more significant as length increases.

@aaronfranke
Copy link
Member Author

Since this breaks ABI compatibility, what can I do to preserve compatibility?

@dsnopek
Copy link
Contributor

dsnopek commented Sep 25, 2023

Since this breaks ABI compatibility, what can I do to preserve compatibility?

This should be addressable via bind_compatibility_method() (following the usual pattern with node.compat.inc, and _bind_compatibility_methods(), etc - take a look at one of the examples already in the engine, ie in TileMap)

And after that you should remove this line from the expected errors (it won't be needed once you add the compatibility method):

Validate extension JSON: Error: Hash changed for 'classes/Node/methods/set_name', from 04FD3184 to C4FB126E.

Copy link
Member

@RedworkDE RedworkDE left a comment

Choose a reason for hiding this comment

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

And Validate extension JSON: Error: Field 'classes/Node/methods/set_name/arguments/0': type changed value in new API, from "String" to "StringName". needs to be added to 4.1-stable.expected instead of the hash error that you removed.

core/string/ustring.cpp Outdated Show resolved Hide resolved
scene/main/node.h Outdated Show resolved Hide resolved
@raulsntos
Copy link
Member

This doesn't break compatibility in C# because we don't expose the property accessors, so no additional changes should be needed for C#.

With this PR, we likely don't need to special-case this anymore in the C# bindings generator:

// Special case for Node::set_name
bool whitelisted = getter->return_type.cname == name_cache.type_StringName &&
setter_first_arg.type.cname == name_cache.type_String;

core/string/ustring.h Outdated Show resolved Hide resolved
scene/main/node.h Outdated Show resolved Hide resolved
misc/extension_api_validation/4.2-stable.expected Outdated Show resolved Hide resolved
@aaronfranke aaronfranke force-pushed the node-set-string-name branch 2 times, most recently from 79feb41 to 9b711b5 Compare January 31, 2024 06:07
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.

Node::set_name and Node::get_name have inconsistent types
8 participants