My question is simple, should I pass FName by value or by reference ?
I realize that in the engine code, most of the time FName are passed by value. But functions in FName class pass FName by reference.
As a C++ dev, I have always been told that trivial type (char, int, float, …) should be passed by value but object and struct should be passed by reference.
I did a quick sizeof(myFName) and sizeof(&myFName) and I got respectively 12 bytes and 8 bytes. (Win64 - Shipping config with the case preserving feature).
FName only contain an union of 1 struct with 3 int32 in it. Sizeof(myInt32) is 4 so it make sense that sizeof(myFName) is 12 bytes.
Even if there is 4 bytes of difference, a reference has an indirection cost. So I’m not sure if it’s a bad trade off.
PS: Please, DO NOT ANSWER : “It depends if you want to modify the argument passed in”, this is not the point here.
When passing by const reference you express your intentions - “i just want to look, promise don’t modify”. Engine often uses const FName&.
Non-const refs are other topic -> Open-std
I’m just curious about which one is more efficient, performance wise.
I don’t think that using a const reference solve my issue, but thank you for the link, it was really interesting.
For 1) Why Epic is usually passing FName by copy in the engine code instead of using const reference ?
For 2) Using a const ref doesn’t change the sizes of a FName reference. So my comparison with a FName pass by value is still the same.
For 3) Even if you use a const reference there is still an indirection.
There was quite a lengthy discussion about this a few weeks ago in the UE4 Slackers Discord - the general consensus is that passing by const value is the correct method due to the indirection cost of a reference.
It’s also worth pointing out that FName size actually changes depending on the build type you are using. In Editor they are larger, because they are case-sensitive - whereas outside of editor they are case-insensitive. In Editor they are 12 bytes, but outside of editor they are actually 8 bytes.
I think compiler do the work and there is practically no difference in performance.
Im asking because Im sure dealing with FName isnt critical part of your code to worry about performance.
Ad.1) You sure? TotalCommander counts above 500 files with ‘const FName&’ phrase (just in /runtime subdir)
Ad.2) Yes, but const is important part of language. Optimizers loves const
Seriously? Discussion about eight-byte structure, which perfectly fits in x64 registers? Lol.
Just the messenger. I won’t pretend I understood the full discussion because UE4 Slakcers is full of plenty of smarter people than me - but that was the consensus. It’s not about the size it’s about the indirection cost. A reference is still a lookup to some other location in memory, which could potentially mean a lot of hopping around. I’m sure the difference is practically negligible.
Most routinely-used engine functions use const value (const FName Value) - such as for bone names in animation, body names for physics, attach locations, data table lookups etc. This is all stuff that is used in high volumes at runtime. If in doubt, follow what the engine does. At the end of the day, I seriously doubt any of this really makes a difference to the average game.
It’s not that complicated.
You will see pass-by-value when the engine is dealing with multi threaded data, like animations. When it’s a self-contained function usually it’s a simple const ref.
@TheJamsh no offence. I’m not an expert, but it just seems pretty funny to me.
Core classes like UObject, AActor, etc. are bloated, engine heavily use polymorphism and virtual calls, which destroys cache and branch-prediciton… and people are debating whether to send int64 by value or by reference. Hilarious
Something to add since this thread comes up often in searches:
When exposing functions to BPs (with BlueprintCallable) that use FNames (probably other types too), passing by reference const FName& Name as apposed to FName Name does not allow inputting values in an “input field” on the blueprint node – unless I’ve missed a UPARAM setting somewhere – which for me was a determining factor.
You didn’t miss a UPARAM option. But there is a UFUNCTION meta option: AutoCreateRefTerm=“XXX,YYY, …”.
It’s a little silly for this particular case, but is much more useful with containers where you want a reference but you also want to allow it to be an optional parameter.
in my experience, passing FNames by reference can create real issues, where the fname changes under your feet. even when passing it as const FName.
this tend to happen if you have the fname in an array or map, but also on other cases.
the fname is meant to be a hashed index to a string table, hence the small size.
it gave me enough headaches to decide to pass it by value instead.
passing it as value is somewhat acceptable, disappointing, but acceptable.
i haven’t had the time to decipher why that happens, but since i can’t trust it, i can’t really use it. unless im 100% sure it’s ok.
also i’ve checked the source for fname recently, and i’m not sure it’s 3 uint32, i think it’s only 2 but one of them is selected depending on some macros.
If you’re seeing that sort of behavior then it’s more likely due to your own use of the reference (or the source of the reference) then the actual pass-by-reference behavior. The behavior will be no different than if you make a reference as a local variable and then do something to the source variable. And it would also be true for any reference type, not just references to FNames.
You absolutely have to be careful about using references to instances of things in containers. Any operation that modifies the container (especially adds, resizes or reserves) may cause the container to reallocate and move the contents to other memory. This will invalidate any references you have to things stored in the container.
thanks, but ive spent a long time debugging that code.
While theres always the possibility its a bug in my code, and i got it wrong, im quite confident it was an issue with the containers, since i didnt had it in other places. And i had the same issue in different parts of the code dealing with containers.
Yes, but as I said already it would be your uses of the container that would cause the references to be invalidated. The only time I’ve ever seen issues like you describe is when attempting to use a reference to an element of a container that’s been modified. I work with references to all sorts of types (including FNames) in containers and have never encountered a problem (10 years, 2 AAA releases, which is only to say they weren’t trivial game projects).
i see what you mean and that’s something i would have been aware of.
i don’t recall modifying the container itself. except maybe adding an object to it. but not moving or removing or modifying an item itself since i would have expected.
one could assume that was my code that ultimately generated the issue, but even in that case, my point is/was that the issue was not intuitive or predictable for that situation, hence the caveat to keep eyes open for such cases.
thanks for your time. i don’t want to hijack this thread, i’ll keep that in mind if i revisit my code someday.