Recommended FName comparison method

There are several ways to compare FNames. What is the best way regarding performance and if the TCHAR array “CompareToMe” is used temporary just for this comparison?

existingFName.Compare(TEXT("CompareToMe"))
existingFName.Compare(FName(TEXT("CompareToMe")))
existingFName.Compare(FName(TEXT("CompareToMe"), FNAME_Find))
existingFName == TEXT("CompareToMe")
existingFName == FName(TEXT("CompareToMe"))
existingFName == FName(TEXT("CompareToMe"), FNAME_Find)

So is there a difference regarding performance between

TEXT("CompareToMe") // *
FName(TEXT("CompareToMe")) // Like above (same constructor called)?
FName(TEXT("CompareToMe"), FNAME_Find) // Will return NAME_None, if not existent?
  • Works because FName(const [WIDECHAR/ANSICHAR]* Name, EFindName FindType=FNAME_Add) is called → this adds “CompareToMe” to stringTable (if not present)?

and

FName::Compare()
operator==

Looking into FName::Compare(), there is done a wide to narrow convert in case of an initial mismatch which I’d like to avoid. Unfortunately I didn’t found operator== for FName to try to answer the question for myself.

Thanks!

operator == is more performant than Compare if you’re only testing for equality, because that’s all it does. From the source:

== is in NameTypes.h:

FORCEINLINE bool operator==(const FName& Other) const
{
	#if WITH_CASE_PRESERVING_NAME
		return GetComparisonIndexFast() == Other.GetComparisonIndexFast() && GetNumber() == Other.GetNumber();
	#else
		static_assert(sizeof(CompositeComparisonValue) == sizeof(*this), "ComparisonValue does not cover the entire FName state");
		return CompositeComparisonValue == Other.CompositeComparisonValue;
	#endif
}

Compare is here:

int32 FName::Compare( const FName& Other ) const
{
	// Names match, check whether numbers match.
	if( GetComparisonIndexFast() == Other.GetComparisonIndexFast() )
	{
		return GetNumber() - Other.GetNumber();
	}
	// Names don't match. This means we don't even need to check numbers.
	else
	{
		TNameEntryArray& Names = GetNames();
		const FNameEntry* const ThisEntry = GetComparisonNameEntry();
		const FNameEntry* const OtherEntry = Other.GetComparisonNameEntry();

		// Ansi/Wide mismatch, convert to wide
		if( ThisEntry->IsWide() != OtherEntry->IsWide() )
		{
			return FCStringWide::Stricmp(	ThisEntry->IsWide() ? ThisEntry->GetWideName() : StringCast<WIDECHAR>(ThisEntry->GetAnsiName()).Get(),
								OtherEntry->IsWide() ? OtherEntry->GetWideName() : StringCast<WIDECHAR>(OtherEntry->GetAnsiName()).Get() );
		}
		// Both are wide.
		else if( ThisEntry->IsWide() )
		{
			return FCStringWide::Stricmp( ThisEntry->GetWideName(), OtherEntry->GetWideName() );
		}
		// Both are ansi.
		else
		{
			return FCStringAnsi::Stricmp( ThisEntry->GetAnsiName(), OtherEntry->GetAnsiName() );
		}		
	}
}

Note that both do precisely the same thing to test for equality, but compare goes on to test for ordering if they aren’t equal, whereas == does not. So in the case of equality their performance is identical, but == outperforms compare when the strings aren’t equal because it does no further processing.

Use compare when you need ordering, == when you only need equality.

As for your choice of construction, it does not matter. == FName(TEXT(...)) is identical to == TEXT(...) (same with compare), the latter case simply implicitly calls the appropriate FName constructor according to the usual C++ conversion rules, given the lack of specific == and Compare overloads. I would go with == TEXT(...) simply because it is less verbose, but also because you’ll benefit from any specific potentially-optimized overloads they may choose to add in the future.

1 Like