Just a thought: Shouldn’t be a pure cast even faster?
True, but then I wouldn’t have read about awesome news haha! Good stuff to know!
No. thread must be always bumped so that more people familiarise themselves with research.
I don’t understand atittude against “old” threads. Its is just a number.
The answer is already in the page.
You’ve found it from Google.
Meaningless bumping is, well, meaningless.
test is very limited, and kind of gives a poor impression of the true cost of casting. The casting function in source code is very simple, it reads the properties and function definitions of the Class and compares them to the properties and function definitions of the object passed in. If all of these things match, we have a successful cast. Depending on what you’re casting to could be very little, like the sample project here is doing as little as possible in the cast to minimize the apparent cost of it, or it could be a rather deep check up the V-Table with lots of data to sort through (it verifies the ENTIRE class, not just the last child, of course, that’s why a Character will cast to Actor or Pawn as well as Character). In a real project if you’re casting can be quite expensive, for example casting to your player character you’re most likely checking UObject/Actor/Pawn/Character/MyCharacterBase/MyHeroBase/MyHeroType. These classes are often quite large, everything you make on top of Character is not optimized for casting like the engine classes are, so they can get very expensive to cast.
The true advantage to using an interface is that you only have to find the function definitions in that interface in your object to know it implements it, so while in your test here the interface and class are so similar in size they might as well be the same, is likely never true in a real scenario.
I’ve done basic Fizz-Buzz testing for release builds of shipped games, and I can tell you with absolute certainty the smaller the interface and larger the class, the larger the discrepancy between using them becomes. So should you avoid class casting entirely? Well no, getting around casts entirely is a lot of systems design work and that production cost has to weigh in at some point in the discussion, and making a billion interfaces isn’t going to make your life any easier long term, so cast today and come back to redesign tomorrow when it’s a core or you do it very frequently, and build ecosystems that don’t require casting to function.
When you cast, you will get the correct class, or nothing at all. Looking at the properties and functions is not needed at all; all it needs to do is walk the chain of parent classes, and compare to the class you cast to. If one of them matches, return that. I can’t think of any reason why a class cast would have to look at properties at all, and, in fact, I can’t even think of an implementation that let you get the right class out of an object that was cast that way.
It is my understanding that, if ClassA and ClassB both have the same properties/functions, but don’t share a base class, you cannot cast an instance of ClassA to ClassB. If you have seen otherwise in the engine source, could you point at a file/line number?
Or are you describing the behavior of interface resolution, not casting? Interfaces can possibly be resolved by matching function names. But there’s still the “implements interfaces” roll-out panel in the class properties, to explicitly list what interfaces you implement.
I have a doubt: Does casting creates a hard reference of that class to compare?
I assume by “doubt” you mean “question!”
Yes, when you cast, you get an instance of the particular class (or one of its subclasses.)
If you cast to UWhatever, and then call GetClass() on the object, then you will get a class object that is UWhatever or some subclass of UWhatever.
I’m describing how it functions on an engine level to handle the casting behavior when you use the Cast function. You wouldn’t cast to anything besides the class you want, and failing returns a nonsense pointer. On an engine level that function is comparing the presence of properties, that the properties present within an object match the properties in the definition of the class. isn’t a value comparison.
At the engine level, casting to another class does not compare any property presence, it compares the unreal-level type info for the class.
You can have two classes that have exactly the same properties, and both derive straight from AActor, yet when you create an instance of class A, you cannot cast it to an instance of class B.
In C++, casting is fast, because it can be done with compiler support (although in debug mode it can run checks, depending on how you do it.)
In blueprint, is slightly slower, because of the blueprint/c++ marshaling needed, but it’s not terrible. What it can do is walk the chain of UClass in the object, to find the class you want, or not. I haven’t actually timed , but it’s never been a problem – if a blueprint-level cast is a performance problem, then that code shouldn’t be in blueprint to begin with.
You can read the code in Engine\Source\Editor\BlueprintGraph\Private\K2Node_DynamicCast.cpp
for more details (including the determination when you’re trying to do an impossible or tautological cast, based entirely on the UClass of the castee.)
And just to hammer home, is the implementation of the blueprint VM opcode EX_DynamicCast
:
// check to see if the Castee is a castable class
else if( Castee->IsA(ClassPtr) )
{
*(UObject**)RESULT_PARAM = Castee;
}
Which is the good-old Unreal C++ level cast. (There some additional code above to check for UInterface and NULL cases.)
There are actually two different implementations of UClass::IsChildOf
(which is used by IsA
).
See Engine Source on GitHub
https://github.com/EpicGames/UnrealEngine/blob/d94b38ae3446da52224bedd2568c078f828b4039/Engine/Source/Runtime/CoreUObject/Public/UObject/Class.h#L480
https://github.com/EpicGames/UnrealEngine/blob/99b6e203a15d04fc7bbbf554c421a985c1ccb8f1/Engine/Source/Runtime/CoreUObject/Private/UObject/Class.cpp#L2197
The implementation is chosen like
#if UE_EDITOR || HACK_HEADER_GENERATOR
// On editor, we use the outerwalk implementation because BP reinstancing and hot reload
// mess up the struct array
#define USTRUCT_FAST_ISCHILDOF_IMPL USTRUCT_ISCHILDOF_OUTERWALK
#else
#define USTRUCT_FAST_ISCHILDOF_IMPL USTRUCT_ISCHILDOF_STRUCTARRAY
#endif
The USTRUCT_ISCHILDOF_STRUCTARRAY implementation caches an array of superstructs and the number of bases in the constructor of UStruct
. Then the actual IsChildOfUsingStructArray
implementation needs to compare only the number of bases as well as whether `` has the given Parent
at the corresponding index in its array of superstructs.
See Engine Source on GitHub
https://github.com/EpicGames/UnrealEngine/blob/99b6e203a15d04fc7bbbf554c421a985c1ccb8f1/Engine/Source/Runtime/CoreUObject/Private/UObject/Class.cpp#L4347
https://github.com/EpicGames/UnrealEngine/blob/d94b38ae3446da52224bedd2568c078f828b4039/Engine/Source/Runtime/CoreUObject/Public/UObject/Class.h#L271
I’m still a newbie to UE, but I hear that hard references created by casting cause referenced blueprints to load when the casting blueprint is loaded. can form a chain reaction that hurts memory usage and loading times.