FVector can be optimised out to return garbage

The following function return garbage (-2756727551235339700000000.000, 0.000, 0.000) unless one of the commented Black Magic Fixes is included, in which case it will correctly return (0,0,0).

(The game world has been set up, but no racers have been added to it)

I believe this has to do with the compiler optimising out the for loop when the RacerManager is NULL, or the RacerManager->Racers.Num() == 0, even though I exit early in such cases.

The .Sort() function works by calling the Racer class’ < operator, like so:

FORCEINLINE bool ASplineRacer::operator <(const ASplineRacer& Right) const
{
	return CalculatePercentageComplete() < Right.CalculatePercentageComplete();
}

Note, that the CalculatePercentageComplete() function is a CONST as required by the < operator.

FVector URaceRunningManager::GetFocalPoint()
{
	// BLACK MAGIC FIX FOR COMPILER OPTIMISING OUT THE FOR LOOP WHEN NO RACERS EXIST
	// YOU MUST INITIALISE THE FVECTOR
	// OR IT RETURNS A REALLY WRONG VALUE
	// DO NOT DO: FVector Result;
// ALSO, THIS DOESN'T WORK: FVector Result = FVector();
	FVector Result = FVector(0, 0, 0); 

	if (!RacerManager || RacerManager->Racers.Num() == 0)
	{
		return Result;
	}

	TArray<ASplineRacer*> SortedArray(RacerManager->Racers);
	SortedArray.Sort(); // we use our overloaded SplineRacer::operator <

	float TotalInfluence = 0.0;

	for (uint8 i = 0; i < SortedArray.Num(); i++)
	{
		ASplineRacer* Racer = SortedArray[i];
		float Influence;

		// Lower Third
		if (i < SortedArray.Num() / 3)
		{
			Influence = 1.0;
		}
		// Middle Third
		else if (i < SortedArray.Num() * 2 / 3)
		{
			Influence = 2.0;
		}
		// Upper Third
		else
		{
			Influence = 4.0;
		}

		// For some unknown reason the following line of code is ESSENTIAL to making this function work
		// Seriously! Some weird side effects!?!
		// Don't freaking touch this!
		// Literally freaking black magic from the 7th circle of hell.
		// (Verified by Nick)

		// Any of the following options work:
		//FString PrintDebug = FString::SanitizeFloat(Influence) + " Influence to percent:" + FString::SanitizeFloat(Racer->CalculatePercentageComplete());
		//FString Percent = "a" + FString::SanitizeFloat(Racer->CalculatePercentageComplete());
		//FString SanFloat = "b" + FString::SanitizeFloat(Influence);
		//FString FixBlackMagic = "a" + FString("b");

		// None of these options work as a substitute...
		//FString Percent = FString::SanitizeFloat(Racer->CalculatePercentageComplete());
		//FString SanFloat = FString::SanitizeFloat(Influence);
		//float OtherFixMaybe = 1.f + 1.f;
		//float OtherFixMaybeAgain = 1.f + float(1);

		// And you don't even need to use the constructed string
		//print(SanFloat);
		//print(PrintDebug);

		Result += (Racer->GetActorLocation() * Influence);
		TotalInfluence += Influence;
	}

	Result /= TotalInfluence;

	// This doesn't make it work
	//print(Result.ToCompactString());

	return Result;
}

You should use :

FVector Result(ForceInit);

if you want to zero FVector components.

look on the default constructor:

FORCEINLINE FVector::FVector()
{}

It may contain garbage in Non-Debug configurations.

Oh in debug conf:

FVector Result;
Result = {X=-107374176. Y=-107374176. Z=-107374176. }

:slight_smile:

This still doesn’t explain why, instead, in the for loop I can put:

FString FixBlackMagic = "a" + FString("b");

and it works…? (Even with FVector Result;)

Would you isolate a working(with error :slight_smile: ) sample without custom logic(ASplineRacer/CalculatePercentageComplete etc)?

ASplineRacer is merely an extension of APawn and CalculatePercentageComplete is a const function that returns a value 0.f-1.f (tested to always be true, usually returns 0.f until the race starts)

Anything that fills those requirements and then calls the function I’ve given you (obviously give your APawn extender class the < as I’ve listed) should have exactly the same problem and black magic fix.

As I am doing this for work I don’t really have time to build a test bed considering the problem has been solved, but if you just do exactly as I’ve listed in the initial question you should have the same problem.

Hey vikhik-

I have a few questions to help me reproduce this on my machine. Are the operator override and GetFocalPoint functions you posted inside the pawn class you created? Additionally where is the CalculatePercentageComplete() function located and what does the function do?

Additionally, just so I’m sure I understand correctly, the FVector Result that is being returned at the end of the function has an incorrect value after the for loop, correct? If you print the value of the variable does it appear correctly or incorrectly before/after line 63 and before/after line 67?

Cheers

Doug Wilson

The structure (in C+±ish pseudocode) is:

ASplineRacer : APawn
 operator overload for < // Allows us to .Sort()
 float CalculatePercentageComplete() const; // always returns 0.f - 1.f

URaceRunningManager : UActorComponent (placed on a manager blueprint in the game world)
 FVector GetFocalPoint() // Returns the most interesting point in the race
 URacerManager RacerManager; // For access to the racers

URacerManager : UActorComponent (on the same manager blueprint)
 TArray<ASplineRacer> Racers; // the racers

If you print the variable (just print Result, not print one of those debug strings/black magic fixes) then it prints garbage. From memory, they look something like “1.#p” or something?

If the return is garbage then it is garbage the whole way through. If the return is not garbage then it is not garbage the whole way through.

My main curiosity at this stage is still why, instead of initialising the vector properly, in the for loop I can put:

FString FixBlackMagic = "a" + FString("b");

and it works…? Even with no racers spawned (array size 0) or all racers returning 0.f from CalculatePercentageComplete()

I’m still not able to reproduce the garbage when printing the FVector. Can you reproduce this in a blank project without custom code? If so please list your setup that caused the FVector to print incorrectly.

I guess the first thing to say is that the FVector needs to be initialized somehow just like any variable. You cannot do this with the default constructor as Epic have chosen to make a non-initialising default constructor, most likely for performance reasons. You can initialize it using the way you have or by using FVector::ZeroVector which may be slightly more readable.

Obviously without initializing the FVector you are in the realm of C++ undefined behaviour. Having the most basic login in this function shows this up as the compiler will probably optimize the code to share with other functions. Adding something very unique to this function makes the compiler not optimize the code in the same way and you seem to be lucky that it magically makes the FVector initialized to something sensible.

This will be correct. Added default constructors to FVector would be a severe performance hit. By changing the code you’re changing the stack layout and the FVector’s luckily getting to some 0’d memory.

You don’t actually need result until later - you could change this to:

if (!RacerManager || RacerManager->Racers.Num() == 0)
     {
         return FVector::ZeroVector;
     }

Though honestly I’m not sure what the initialization cost on the stack vs accessing static memory trade off is.

If any Epic devs are reading this a platform specific optimization might be some typed constructors using enums to identify: FVector someVar(eMakeThisZero); This allows us to avoid the static memory access in favor of fast asm instructions to init the vectors, assuming someone does the legwork and implements the functions :slight_smile:

Thats exactly what the FVector(EForceInit) version does :slight_smile:

	/**
	 * Constructor which initializes all components to zero.
	 *
	 * @param EForceInit Force init enum
	 */
	explicit FORCEINLINE FVector(EForceInit);

Hey vikhik-

Where are you printing the Result variable that it is giving you garbage values? Are you seeing this in the output log of the editor or while playing the game in PIE/standalone? Can you post the code where Result is being used? So far in my testing creating a FVector and printing it has not shown any garbage values.

Hi Doug,

I am actually at a conference with Mieszko at the moment, I’m not at my desk to reproduce for another week or so.

I was exposing this function to blueprint, calling it in the level blueprint and then printing the vector out (without assigning it to a variable first, as that caused other issues when using uninitialised values).

If possible I’ll try and get some repro up when I’m back, as it’s a really curious bug.

Well shiver me timbers :wink: though I don’t see fast platform specific implementations I also didn’t look that hard.

In that case can I have implementations for EForceUnitX, Y and Z also :slight_smile:

Give once and they will never stop asking… NEVER!!!

Hey vikhik-

I just wanted to touch base with you and see if you’ve been able to get any repro for this bug? I tried creating a function and calling it in my level blueprint however it still printed out (0, 0, 0).

Don’t think this is a bug is it? Stack contents will depend on compiler settings, function contents and optimization levels unless explicitly initialized - which FVector explicitly isn’t for performance reasons.

Therefore an uninitialized FVector will sometimes contain garbage, and sometimes not!

Hey vikhik-

We’ve not heard from you in a few days and have not been able to reproduce the same issues you described originally. I will be marking this post as resolved for tracking purposes but if you are able to provide any additional information you can post a comment to reopen the post and we will investigate further.

Cheers

Doug Wilson