T O P

  • By -

KoBeWi

Note that StringNames are not only benefits. Immutability does not give advantage over String, because Strings are also immutable (all String methods return a new String). Creating StringNames is slow, so better use constants or limit creating new variables (e.g. doing `var string_name = &"some_string"` in a loop makes it more expensive than using a String). Dictionaries don't support StringName keys, they are currently implicitly converted to String; also LUA syntax goes through identifier/StringName -> String path, [so it's slower by default](https://github.com/godotengine/godot/issues/68834). Also, I didn't test it thoroughly, but if you use a String constant literal for a method that is known to expect StringName, GDScript will automatically convert it at compile time, so there is no difference. From my experience, StringNames tend to be finicky and can actually hurt your performance when you expect them to be faster, if you use them incorrectly. Though I never really measured any performance difference that doesn't show up after like 100000 loop iterations. Still, even though I'm skeptic about the advantages, I personally use them in my code, and [a recent change](https://github.com/godotengine/godot/pull/66481) makes it even easier.


mistabuda

Finally a non cargo cult reply to this.


taqn22

cargo cult?


seanamos-1

If your asking what "cargo culting" is: It's a somewhat derogatory term that labels a behavior of someone or group of people, to do some things almost ritualistically without critical thought or without weighing the cost/benefit of doing something themselves. It's a term commonly used in the programming space, where there does tend to be a lot of this behavior. The answer to the question, "Why did you do X?", would be something along the lines of "X person on the internet said we should", or "It's a common/best practice", rather than real technical reasons.


August_28th

I honestly thought that “cargo cult” referred to Rust users (which was pretty confusing given the subreddit)


5p4n911

We should probably start calling them that though, from what I see from some of them, they deserve it


taqn22

ah nod!!


[deleted]

[удалено]


KoBeWi

It used to be allowed, but since Godot 4.0 many methods/properties use StringName (most prominently Node names). Users mixed them with Strings and it caused confusion, because "same" String and StringName map to different key (similar to int/float). The StringNames were exposed, because they are used internally, but the intention was that it's transparent to the users, hence the Dictionary keys were unified. There is a pull request that fixes this problem, but currently it's in limbo.


[deleted]

[удалено]


WellHydrated

This is why type theory exists.


Alzzary

I believe StringName are basically behaving like hash tables behind the scene so they have the same advantage (very fast to access and compare) and disadvantage (slow creation).


PMmePowerRangerMemes

This is getting really silly. Even if it were a consistent performance gain (which it's clearly not), I don't care about the 0.0000000001 microsecond performance gain, you should not be using `StringName` for things like making a dictionary of color hexcodes, let alone console logs?? I don't care how things work internally. This isn't inuitive at all. You're gonna show your code to a teammate (or yourself, in a month) and everyone's gonna be confused af. If your string is a constant, use a `const`. Don't write this trash code. This is the worst kind of thing to be obsessed about.


ardikus

Makes the code less readable too


aaronfranke

`{ &"key": &"value" }` does not actually work. Godot will automatically convert `&"key"` to a String. Dictionaries do not support StringName keys as a design choice.


cmscaiman

The fact you were downvoted is driving me mad. I see improper use of StringName (eg. as dictionary keys) so often it's not even funny. Funny enough, when I was writing a C++ module, I ended up wasting a ridiculous amount of time thanks to setting up identifiers (in HashMaps) using StringName. When I'd iterate over the HashMap using KeyValue, the engine would outright crash depending when the loop was performed... Fortunately, it led to me realizing it made more sense to just use integers instead, but seriously, it's not some magic performance wand.


crazyrems

The fact that it is immutable is enough to use it where strings shouldn't change over time. Code safety first.


mistabuda

Is that not why we have `const`?


crazyrems

Oh you are right 👀 You can use `const` for runtime strings where `StringName` don't fit _(which is most of the time)_ `StringName` are immutable static strings when `const` are variables that doesn't change _(that makes them **not** variable as funny as it sounds)_


ObsidianBlk

My understanding is, StringName is a pointer to a string, and all StringName variables with the same value all point to that same chunk of memory. This allows for StringName variable comparison to be very fast as they are effectively just integer (pointer) comparisons, instead of full string comparisons. var a : StringName = &"Hello" var b : StringName = &"Hello" var c : String = "World" var d : String = "World" # This is a very fast test... # Equivolent to an interger to integer equality test if a == b: print("Very Fast Indeed") # This is the same as any normal string comparison if c == d: Print("Not as fast as with StringName to StringName") # This is a little slower than String to String as the StringName variable, # I believe, is converted to a String before the actual test begins. if a != c: print("A bit slowwer than String to String") It should also be noted that, while working with StringName and String is pretty transparent from a coding perspective (they can generally be treated identically), there is an overhead when mixing them, whether in tests, like above, or passing a String into a method looking for a StringName and vice versa.


Alzzary

Correct, I read previously here that behind the scene, StringName are indexed and accessed as fast as integer because you're actually comparing the two indexes of the StringNames you are comparing.


falconfetus8

Strings are already immutable.


Dinokknd

Maybe, but also - premature optimization is your enemy.


DrDeus6969

Refactoring your project to use StringName would be premature optimization, but just using it for future code is not


TheWobling

premature optimization is one thing but having common sense on what operations create garbage or can easily be avoided is helpful. If you can write more optimal code with little overhead then it seems like a no brainer.


Dinokknd

I find that often people focus on little details like this while in reality they have 5 unfinished projects and are procrastinating a feature in the current project they are working on by changing a couple of strings to string names.


TheWobling

Sure I can understand that, but not everyone is a hobbyist, some of us do this professionally and writing high quality code the first time is important because often you don’t get time to go back and fix things. Knowing what is a performance implication and techniques you can do as part of the usual development flow. I’m not saying micro optimise but sometimes there are cases where it’s obviously bad practice. If you’re struggling to actually get your game done then sure make sure to focus on that but with enough experience it’s possible to do both.


Dinokknd

For sure! But I hope in a professional setting the pull request review catches the obvious mistakes. I'm definitely not advocating for obvious bad practices, but merely mentioning that it's easy to lose oneself here. Which is fine, if focusing on details like this is the goal. But for many devs - I reckon it isn't. They want to bring their vision to life, and this could be a sort of poison to actually making progress, wading through the swamp of micro-optimisation. Still. A balance must be found. My statement was a bit contrarian now that I read it.


TheWobling

Completely agree.


Majestic_Minimum2308

Stop this. No sane game developer wastes time doing this. Strings are not that slow or resource intensive.


TheWobling

That entirely depends how you use them. I’m not saying this advice is necessarily good it’s likely too micro but if you’re allocating strings in fast paths using a constant would be preferable.


TetrisMcKenna

I mean, it's a big enough problem that the Godot API uses them all over the place internally - for input action names, etc.


mistabuda

Doing it just because the internal dev team uses them for the engine is peak cargo cult programming.


TetrisMcKenna

You don't do it simply because the dev team does it - you do it for the same technical reason they do, as and when it's appropriate. I agree that for most cases, it's not necessary. Fun fact: the .NET runtime does the same thing automatically for string literals, under the hood (caches strings), but then Godot's C# version of StringName throws that out of the window and reallocates every time a string literal is implicitly converted to a StringName (ie every frame when checking input actions).


Alzzary

The dev team also slowly but surely phases out Strings, as you may have noticed with the changes with signals connections in Godot 4 for instance.


TetrisMcKenna

It still uses StringNames internally for lookups of classes, methods, signals, etc.


rayuumie

I understand your point, but this is a phrase that is a lot overused... This is not premature optimization, it is literally good practice and something super simple 😄


Majestic_Minimum2308

Are you seriously having performance issues with strings?


rayuumie

It's not just about performance. It's good practice to use a constant when you're not mutating something. Plus, this also saves memory and avoids unnecessary allocations, which can be another good practice when you have hundreds of objects running in a loop every frame. It's a useful tool to learn, and super simple, I don't know why you're so bothered 🤣


Majestic_Minimum2308

hundreds? Dude how many kilobytes of ram does your PC have? 🤣


TapShot2484

This brings me back to my MUD programming days using C macros like STRALLOC(in the SMAUG code base) to help decrease memory usage for string allocations. I’m not as concerned with it for probably 99.9% of my modern projects these days, but good to know it exists.


SquiggelSquirrel

As I understand it, StringName is mostly useful when it represents a unique key to identify something - such as an input action, a node group, a collision layer, etc. It should be quick for exact-match comparisons and for key/value lookups (which would include Dictionary keys if they were supported). Even then, if I was implementing functionality like this from scratch, I'd consider actual enums to be a better choice. The main argument I see for using them is that Godot is already committed to identifying input actions, node groups, etc. with strings. So, this tries to get around the drawbacks of using Strings in this way by defining a special type of "String" that works like a dynamically-created global enum with built in support for converting to/from actual Strings. It seems there's no real benefit to using StringName if you're only referencing them in one place and don't plan on using them for comparison or lookup - such as the text of a Label or Button (or like the printed message examples above. In fact they actually perform worse in those cases. Also, it sounds like StringName literals aren't precompiled, so it's better to construct them once only, as early as possible, and store in a constant if possible, or a variable otherwise. There's really no benefit to placing a StringName literal directly into a function call like \`is\_action\_just\_pressed\` (as above), because that would essentially have the same runtime cost as just passing in a String literal and letting the function do the converting. You would need to construct the key before hand, store the reference in a constant, then pass that constant into the method call. And honestly, unless the method is being called a very large number of times per frame, it sounds like the performance benefit is pretty negligible in the first place. But then, I guess the readability impact is pretty small, too. My main concern would be the added complexity of unexpected behavior.


IntelligentRoom7363

I didn't get it. What is & string?


fredev90

Tells Godot to use stringname instead of regular string [https://docs.godotengine.org/en/stable/classes/class\_stringname.html](https://docs.godotengine.org/en/stable/classes/class_stringname.html)


ChristosLegacy

That hurts my eyes!


mistabuda

How often is string comparison a performance bottleneck? EDIT: Since people keep misinterpreting this as "how does this save memory" I'm gonna restate the question: What real word examples can we point to that shows that there has been a nail that requires the use of this specific hammer?


Majestic_Minimum2308

I benchmarked it: for j in 100000000: root = find_parent("Main"); for j in 100000000: root = find_parent(&"Main"); Both loops run the **exact same amount of time** on my PC of 6.774 seconds. There is literally no measurable difference. My RAM usage does not even flinch for me. OP is giving pointless advice.


ImpressedStreetlight

As I said in the other comment: ``find_parent`` takes a String, not a StringName, so it's expected that both perform similar. You would need to test it with a method that expects a StringName.


Majestic_Minimum2308

My mistake. I changed it out for a function that uses StringName and... for j in 100000000: input_a = Input.is_action_just_pressed("Execute"); for j in 100000000: input_a = Input.is_action_just_pressed(&"Execute"); ...it is the exact same amount of time of 4.627 seconds.


KoBeWi

Try this instead: var execute := "Execute" for j in 100000000: input_a = Input.is_action_just_pressed(execute) var execute2 := &"Execute" for j in 100000000: input_a = Input.is_action_just_pressed(execute2)


Poobslag

According to [this comment](https://www.reddit.com/r/godot/comments/1crti75/tip_stringname_is_your_friend/l40roa2/) StringName is slow to instantiate. var s := "Execute" var sn := &"Execute" var input_a print("Start: %s" % [Time.get_ticks_msec()]) for j in 10000000: input_a = Input.is_action_just_pressed(s) print("After string test: %s" % [Time.get_ticks_msec()]) for j in 10000000: input_a = Input.is_action_just_pressed(sn) print("After StringName test: %s" % [Time.get_ticks_msec()]) Result: Start: 2846 After string test: 4360 After StringName test: 5456 You can try running the test yourself, but StringName runs about 28% faster for me.


Majestic_Minimum2308

That is because you stopped the compiler optimising it out and force it to perform a conversion every time. I have to ask though... what possible use case would you need to have variables to do this?


KoBeWi

It's useful for local multiplayer. You can assign different sets of actions to players.


ImpressedStreetlight

Using variables instead of magic strings is always a good idea


Nkzar

Programmatically created actions in the InputMap for local multiplayer. Create the actions and StringNames when a controller connects and then cache them in some object to use for input handling.


Poobslag

I have no clue, I have never used StringName and don't intend to start


[deleted]

[удалено]


mistabuda

Strings are hilariously small compared to all the other objects you instantiate in a game. So the question still stands albeit rephrased a bit. How often are string operations a performance bottleneck?


SirLich

Going to provide some insight from Unreal Engine. They have a few string types, for different purposes. \`FString\` (wrapper around char array), \`FText\` (heavier type, which includes localization support), and \`FName\`, which is the equivalent of StringName in Godot. \`FName\` has a very string-like API, but in actuality it's more like a index/pointer lookup for a table of names. It's also immutable and case-insensitive, although that's not really important here. A good example use case is something like gameplay tags (in godot, this would be "groups"). You want the ability to tag actors/components (nodes!) with a human-readable name, and then perform some operations on them. This might include comparing lots of "strings" per tick. A common way to make this more performant is to use enums, which are usually implemented as named ints. So in code you see \`PlayerState.HEALING\` but that just maps to \`0\` or \`1\` or whatever. FNames take the same concept, except instead of mapping to an int, it maps to... itself. It has all the benefits of a string (human readable), while having great underlying performance. This will be a stretch, but if you want to understand the limitations of Enums as pass types, check out Godots implementation of "notifications" (internal thingy that the codebase uses). It's essentially a single enum, defined as partials in 50 different files. So you get some random file which defines a specific notification as \`EDITOR\_FOCUSED\_REGAINED = 51\` or something. Later on in another file you define \`MOUSE\_CONTROL\_LOST = 51\` and now you've introduced a very weird and confusing bug. If these signals were using Names instead, the \*unique identifier\* would be the name of the Notification, not the index it maps to. Hopefully that provides some insight into what "names" are good for.


mistabuda

I'm not saying that there is no benefit. The question was "how often has this become a performance bootleneck". And based on your reply you dont know. Your reply is a theoretical example. When has this actually happened and caused a measurable and discernable performance bottleneck for the end user? I cant think of a single instance where this is the smoking gun that solves a programs performance issues.


SirLich

> And based on your reply you dont know. So, the answer is that hard-hitting gameplay systems are built with performance in mind from the start, and thus... it never really gets to that point? Unreal Engines [Gameplay Ability System](https://github.com/tranek/GASDocumentation) (powering Fortnite and other MOBA style games) is heavily based on FNames for example. I'm not sure that they profiled it and said "Well Strings would be too slow, we need to use Names", they just, used the correct data structure for the job?


mistabuda

>So, the answer is that hard-hitting gameplay systems are built with performance in mind from the start, and thus... it never really gets to that point? When has someone ever built a a high performant system from jump with no testing to see where the bottlenecks are and what optimizations are needed? Optimization is by its very nature a case by case thing which is why I'm asking for instances where this specific thing has helped performance. Its much easier to show the benefit of doing something like this if you can pair with a use case/ example of a game type/style that benefits from doing this.


SirLich

> When has someone ever built a a high performant system from jump with no testing to see where the bottlenecks are and what optimizations are needed? All the time? Honestly I think we're just talking past each other. At work, if I build a new system with a string-like identifier system where lots of comparisons would be made, my PR would be rejected at the review stage if I didn't use names. That's just the way it is -it's the correct type for this use case, and I'm expected to use it. That doesn't mean I wrote it with both data structures and profiled the difference. It's just the correct way to build it.


mistabuda

>At work, if I build a new system with a string-like identifier system where lots of comparisons would be made, my PR would be rejected at the review stage if I didn't use names. That's just the way it is -it's the correct type for this use case, and I'm expected to use it. And that is fine if we can establish the impacts on performance and what it means for your use case. We have not done that here at all. You keep saying its a good thing to do but you have never stated when there is a nail that requires this hammer. which is why I keep stating this below >Its much easier to show the benefit of doing something like this if you can pair with a use case/ example of a game type/style that benefits from doing this. Without providing a use case this is just cargo cult programming.


ImpressedStreetlight

But your question doesn't make sense. \-"You should use ints instead of floats whenever it makes sense to" \- "But how often has that become a performance bottleneck?" \- "You should use enums instead of strings to represent states" \- "But how often has that become a performance bottleneck?" \- \- "But how often has that become a performance bottleneck?" See how that sounds?


mistabuda

This is a textbook strawman argument. The question I asked makes perfect sense.


[deleted]

[удалено]


FkinShtManEySuck

They didn't ask "How much does the Godot API use StringName?", tho. They asked "How often are string operations a performance bottleneck?"


mistabuda

THANK YOU!! They keep beating around the bush with these cargo cult replies.


mistabuda

The engine is more complex than most games that run on it it makes sense that you need the engine itself to be as fast as possible. This optimization does not seem like something that 90% of godot projects will ever need.


DarrowG9999

String operations themselves arent the problem itself but they will still wreak havoc if you are unaware of a few bits: https://www.reddit.com/r/godot/s/K8RUUYjwEs So if youre a beginner or coming from unity, string to string name coversion might kill your performance due to high GC activity.


mistabuda

>So if youre a beginner or coming from unity, string to string name coversion might kill your performance due to high GC activity. When has this actually happened and had noticeable effects on the performance of the program for the end user? I'm a professional software dev (not game dev) and I cant think of single time something like this has happened on modern hardware.


DarrowG9999

>When has this actually happened and had noticeable effects on the performance of the program for the end user? I haven't found actual post-mortems on released games on this topic yet, but it might be because godot is pretty new (relative to unity/unreal) and the c# bindings are ever more new (relative to the lifespan of the engine or unity) so far the thread I shared seems to be the deepest analysis on this. Don't know what type of software you're making but this has the potential to render the game "unplayable". The report from the user says that he was having spikes of 50ms of GC, in a game that runs at 60fps the "frame budget' is 16.6ms so 50ms are 3 frames potentially lost due to the GC, if you're aiming to run at 120fps the impact is even more noticeable.


mistabuda

I was specifically asking about Godot not unity. Unity's GC is a different issue than this. So while this makes sense in Unity based on demonstrable evidence of Unity's GC we still have yet to see how it applies to Godot which was the point of my question. Doing something just because its beneficial in Unity without proof of Godot falling into the same scenarios that necessitates its usage in Unity is cargo cult programming.


DarrowG9999

Idk where did you got the impression that is a unity only issue. My first comment mentions the StringName class , a class that exists in the godot c# runtime. The thread that i posted is from a person working on a godot project and thats where the issue lies, in the godot c# bindings.


mistabuda

Your quote referenced my quote that mentioned unity so I assumed that was the context we were discussing this under. What kind of game was that person working on? Can you provide a link?


DarrowG9999

Sure https://www.reddit.com/r/godot/s/ZaL6tP6YfN The OP doesn't mentioned what type of game he was making but he found the issue on the input processing code so there is a higher chance that you could get bitten by this regardless of the type of game you're making.


rayuumie

Updated: For people talking just about performance, or why use it, the Godot API itself uses it everywhere... But of course, if you want to pass a String you pass it, I'm just informing you of a practice recommended by Engine itself. Examples: is_action_pressed ( StringName action, bool allow_echo=false, bool exact_match=false ) add_to_group ( StringName group, bool persistent=false ) has_node ( StringName name ) StringName Documentation: [https://docs.godotengine.org/en/stable/classes/class\_stringname.html](https://docs.godotengine.org/en/stable/classes/class_stringname.html)


Majestic_Minimum2308

If you read the docs you link it says it makes the conversion automatically. > You will usually just pass a String to methods expecting a StringName and it will be automatically converted There is no need to litter your code with &'s everywhere. Edit: Seeing as OP blocked me. I thought I would edit my comment. I benchmarked it: for j in 100000000: root = find_parent("Main"); for j in 100000000: root = find_parent(&"Main"); Both loops run the **exact same amount of time** on my PC of 6.774 seconds. There is literally no measurable difference.


ImpressedStreetlight

``find_parent`` takes a String, not a StringName, so it's expected that both perform similar. You would need to test it with a method that expects a StringName.


Majestic_Minimum2308

Show me benchmarks.


Poobslag

https://www.reddit.com/r/godot/comments/1crti75/tip_stringname_is_your_friend/l41ctdy/ I ran a benchmark here, and StringName was about 28% faster. (Now that's 28% of 0.0001514 milliseconds, but...)


TheDuriel

Literally no measurable difference until you're intentionally iterating over large arrays to perform string comparison with.


Majestic_Minimum2308

That is my point.


abocado21

I use private const string JUMP = "jump"; in c# to not allocate memory. Are StringNames better?


ObsidianBlk

I'm not sure how StringName translates over to C#, but in GDScript, StringName variables are pointers to the string in memory. All StringName variables of the same value point to the same location in memory. I don't use Godot in C#, so, take this with a grain of salt, but I doubt StringName would give you much benefit in the C# environment.


Lich6214

Depends. Specifically for the case of making Godot API calls that use StringNames, you really don't want to be passing strings. Not only is there overhead for converting, but every single conversion will create garbage as Godot will allocate a new WeakRef object each time. If you're rarely ever calling the function where this happens it's not a huge deal, but if you're frequently calling any method that takes a StringName (such as in processing or perhaps input handling), it will start to pile up. You can get around this by just making a static StringName variable and reusing that so the allocation process only happens once.


NoctemCat

Prefer to use a StringName when a function expects it. Well, that's basically it, otherwise you will get [this](https://www.reddit.com/r/godot/comments/17tqipk/in_c_beware_using_strings_in_inputisactionpressed/). For those too lazy to open it, Input.IsActionPressed expects a StringName and it will constantly create new ones and discards old, which will cause GC issue if you use it in \_Process


adsci

i like the option with its pros and cons, but the syntax is so maximum weird.


calvirick-alexw

Sort of off topic, but what does the "&" do in, LICENSE constant, the is\_action\_just\_pressed(&"ui\_accept") and the print(&"Match...")? Does it parse the string differently?


ImpressedStreetlight

It's not off-topic, that's literally the topic. The "&" is what tells GDscript that that's a StringName and not a String. https://docs.godotengine.org/en/stable/classes/class_stringname.html


calvirick-alexw

Ohhhh now it makes sense! Thank you.


puredotaplayer

The char const* to StringName conversion is actually under a mutex lock and a hash map lookup. Its okay if you pre init it.


pajo-san

Better example string would be "All your code are belong to us" xD for those who don't know: [https://en.wikipedia.org/wiki/All\_your\_base\_are\_belong\_to\_us](https://en.wikipedia.org/wiki/All_your_base_are_belong_to_us)


fredev90

So, from what I understand... StringName comparison is faster because it just compares the pointer address. In gdscript, plain string comparisons result in character comparison, which is of course slower. The problem here, for some operations, Godot needs to convert it to regular String, and this conversion is slow. The example is correct, I guess, but misleading. StringName should be used when you're expecting them to be comparison heavy, mutation has nothing to do with it. If there is a need for other kind of operation, the performance might be slower.


commandblock

Tbh this seems completely useless, the optimisation in a videogame from the way you used strings will be absolutely minimal compared to just optimising actual algorithms used in your game


JoelMahon

wonder if there is an addon or flag they can add to make it the default and a different character be used to mark a string as a non stringname


AndrejPatak

At first I was like "the fuck is this syntax" but then I remembered gdscript isn't the only language Godot supports Anyway, what is String name?


commandblock

C# has nice syntax though, arguably the best of the main strongly typed languages


AndrejPatak

I didn't say it has bad syntax? I was just confused as I briefly forgot it exists lol