StringName Dictionary keys - #70096
Conversation
|
I tested this time and it fixes #68834 for real, without affecting usability. |
b70c2a2 to
c878ba5
Compare
c878ba5 to
b257c50
Compare
b257c50 to
4d9b509
Compare
Repiteo
left a comment
There was a problem hiding this comment.
Nice performance boost & helps iron out those last few string/stringname discrepancies, LGTM!
4d9b509 to
fc60dbe
Compare
There was a problem hiding this comment.
Tested locally (rebased on top of master aaa4560), it works as expected.
However, I can't really discern a noticeable performance difference before and after this PR. It's slightly faster in some cases (StringName assign create) but is slower in others (StringName dict String index read).
Using a release x86_64 Linux export template binary (optimize=speed lto=full) for running the MRP.
Binary size with this PR is 4 KB smaller compared to master.
Before
1.451s: String keys create
33.451s: StringName keys create
4.333s: String assign create
0.482s: String dict String index read
0.546s: String dict StringName index read
4.511s: StringName assign create
0.503s: StringName dict String index read
0.561s: StringName dict StringName index read
After
1.473s: String keys create
33.515s: StringName keys create
4.403s: String assign create
0.483s: String dict String index read
0.566s: String dict StringName index read
4.322s: StringName assign create
0.638s: StringName dict String index read
0.461s: StringName dict StringName index read
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 39)
|
it may be because StringName == String doesnt do an in-place comparison like it could, copying the internal String of the StringName (or allocating a new String if not from GDScript) before comparing it leading to an extra ref then deref, ive already made a fix including this change on my branch, ill find some time to test production builds with the settings u mentioned on all 3 variants [before PR, after PR, after PR with performance fix] |
|
The performance impact is what #68834 is about. |
dalexeev
left a comment
There was a problem hiding this comment.
Makes sense to me, and is essential for supporting typed dictionaries. It has some quirks, but the tradeoff between performance and compatibility seems worth it. String and StringName are interchangeable in most cases, so this shouldn't cause major compatibility breaks. However, users who explicitly check the key type or rely on its string representation may have some issues.
c2b601d to
282fda0
Compare
|
rebased. and looked through comparisons to also i recommend that #92570 gets merged too, so the performance gets better in all cases and not worse in some |
var dict1 := { x = 1 }
var dict2 := { "x": 1 }
print(dict1) # { &"x": 1 }
print(dict2) # { "x": 1 }I'm quite concerned about this, I can really see users running into unexpected bugs when I'm not sure it's worth it to introduce this kind of inconsistency in the API just based on how you declared a dictionary, for a relatively minor performance gain. The tradeoff seems bad a priori (didn't review the changes and discussion in depth). |
From what I understand, this would return |
|
yes dictionaries should behave the same as before (because of StringLikeVariantComparator), just that retrieving a key might give you a string name when it gave a string before (meaning comparing the type of a key to theres a later PR i have to add support for iterating stringname characters with a for loop (relies on #92570) theres also less than and greater than comparisons but to match that to string, the behavior of the stringname comparisons would need to change, so probly not |
I'm still a bit worried about this kind of inconsistency too. However, in practice it shouldn't cause major issues, since Conceptually, slightly1 different dictionary contents depending on the style sounds bad, but on the other hand it makes some sense. If you use Lua style, you probably have a fixed key set and use I think we could test this as early as possible and adjust the behavior later if users report issues. Or let's wait for @vnen's opinion on this. Footnotes
|
This is the same as the current behavior, the only difference is that currently all keys will be strings even if you us StringName. While in this case it might feel odd, I believe it is expected that using String to access a StringName key just replaces the value, not the key (and in practice does not even matter much, given how interchangeable they are).
The only breakage is printing pretty much. I don't think we guarantee compatibility to that level, relying on printed serialization was always finicky.
That is correct. Even with this PR this is still true. IMO the trade-off is good. The Lua-style was always supposed to use StringNames and only changed because of the issues with the type difference. But those issues are pretty much solved now, so it makes sense to allow users have better performance when they need it. |
vnen
left a comment
There was a problem hiding this comment.
Small nitpick, otherwise looks good to me.
also added 'is_string()' method to Variant and refactored many String type comparisons to use it instead
282fda0 to
154049c
Compare
|
Thanks! |
As I discovered in #96915 , one side effect of this change is that the following code: extends Node
func _ready() -> void:
var dict := {
&"B": "test",
&"A": "test"
}
var keys := dict.keys()
keys.sort() # Will sort strings alphabetically, but StringNames by pointer.
print(keys)Prints: While this is a consequence of the StringName not being casted to String, the resulting behavior may not be so obvious. Since I suspect this case might be quite common across people updating, I would suggest we mention it (prominently) in the release notes. |
StringName Dictionary keys
StringName Dictionary keys
follow up for #68747
removes conversion from StringName to String in Dictionary
it seems like a simple change, but many places still compare a Variants type with STRING
simply removing the conversion gave me alot of errors, so i did the sane thing and looked at every instance of an equality comparison to
Variant::STRING, and updated most of them with anis_string()method i added to variant:-- if it accepted String and StringName, i updated it to use
is_string()(might as well, it's much easier to read)-- if it was a closed system, (as in no external input, the type is always known to be string), i let it be
-- if it accepted input, (especially from GDScript), i updated it
-- if i couldn't tell for sure if it was a closed system (like in
editor_debugger_inspector.cpp):---- if it gave an error for StringName input, i left it (could be caught later)
---- if it silently failed, i updated it
note: Variant does not convert StringName to Color or to NodePath, i'm not sure whether it makes sense to accept StringName, so i let it be for now
fixes #68834
here are some numbers i got from this example, with different length keys (on a production build):
TestDictIndexing.zip:
with a long enough test key, StringName is faster even when finding a String key: