How do I send tasks to be completed on different threads?(Multi threading implementation)

Hello, I’ve followed Rama’s guide of implementing multi threading and sending them to tasks in C++, available from here A new, community-hosted Unreal Engine Wiki - Announcements - Epic Developer Community Forums , but what I noticed is that there is either no real different or it the game may perform worse than before.

This is my code:
static FGraphEventRef MultiThreadTestTask;

class FMultiThreadingTest
{
//what is needed in order to initiate the vision process in this class
UVisionSystemComponent* _VisionSystem;
UWorld* _WorldObject;
FVector _WorldEyePosition;
FRotator _WorldEyeRotation;
AActor* _Actor;
float _deltaTime;

public:
//Constructor
FMultiThreadingTest(UVisionSystemComponent* VisionSystem, UWorld* WorldObject, FVector WorldEyePosition, FRotator WorldEyeRotation, AActor* Actor, float deltaTime)
{
_VisionSystem = VisionSystem;
_WorldObject = WorldObject;
_WorldEyePosition = WorldEyePosition;
_WorldEyeRotation = WorldEyeRotation;
_Actor = Actor;
_deltaTime = deltaTime;
}

//task name
static const TCHAR* GetTaskName()
{
	return TEXT("FMultiThreadingTest");
}

FORCEINLINE static TStatId GetStatId()
{
	RETURN_QUICK_DECLARE_CYCLE_STAT(FMultiThreadingTest, STATGROUP_TaskGraphTasks);
}

//return thread for the task
static ENamedThreads::Type GetDesiredThread()
{
	return ENamedThreads::AnyThread;
}

static ESubsequentsMode::Type GetSubsequentsMode()
{
	return ESubsequentsMode::TrackSubsequents;
}

//Main Function
void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent)
{
	_VisionSystem->Process(_WorldObject, _WorldEyePosition, _WorldEyeRotation, _Actor, _deltaTime);
}

};

and I create the thread in the following way:

FGraphEventArray Prereq;
MultiThreadTestTask = TGraphTask::CreateTask(&Prereq,ENamedThreads::AnyThread).ConstructAndDispatchWhenReady(VisionSystem, GetWorld(), WorldEyePosition, WorldEyeRotation, this, deltaTime);

and the Vision System is:

void UVisionSystemComponent::Process(UWorld* world, FVector position, FRotator rotation, AActor* playerActor, float timeDelta)
{
SCOPE_CYCLE_COUNTER(STAT_TA_VisionProcess);

// if the vision system is enabled
if (ActiveSystem)
{
	AInteractiveObject* IObject = FindBestObject(world, position, rotation, playerActor, FocusPoint);

	FGraphEventArray Prereq;
	VisionGameThreadTask = TGraphTask<FVisionGameThread>::CreateTask(&Prereq, ENamedThreads::AnyThread).ConstructAndDispatchWhenReady(ActiveSystem, world, &SpawnedReticle, position, rotation, ReticleMaximumDistanceVR, ReticleMaximumDistance2D, ReticleFloatDistance, ReticleFadeTimer, ReticleFadeTime, ReticleVisible, playerActor, Debug, DisableFocusReticle, IObject, ReticleMaterial, timeDelta, ReticleBlueprint, &HighlightedObject, &FocusedObject, HighlightTimer, UnFocusTimer, PSMoveTriggerDown, FocusTime, FocusPoint);
}

AuditNPCProximityList(world, position, rotation, playerActor);

}

with this being the thread setup:

static FGraphEventRef VisionGameThreadTask;

class FVisionGameThread
{
//what is needed in order to initiate the vision process in this class
bool _ActiveSystem;
UWorld* _WorldObject;
AActor*& _SpawnedReticle;
FVector _Position;
FRotator _Rotation;
float _ReticleMaxDistanceVR;
float _ReticleMaxDistance2D;
AActor* _PlayerActor;
float _ReticleFloatDistance;
float _ReticleFadeTimer;
float _ReticleFadeTime;
bool _Debug;
bool _ReticleVisible;
bool _DisableFocusReticle;
AInteractiveObject* _IObject;
UMaterialInstanceDynamic* _ReticleMaterial;
float _DeltaTime;
TSubclassOf _ReticleBlueprint;
AInteractiveObject*& _HighlightedObject;
AInteractiveObject*& _FocusedObject;
float _HighlightTimer;
float _UnFocusTimer;
bool _PSMoveTriggerDown;
float _FocusTime;
FVector _FocusPoint;

public:
//Constructor
FVisionGameThread(bool ActiveSystem, UWorld* WorldObject, AActor SpawnedReticle, FVector Position, FRotator Rotation, float ReticleMaxDistanceVR, float ReticleMaxDistance2D, float ReticleFloatDistance,float& ReticleFadeTimer, float ReticleFadeTime, bool& ReticleVisible, AActor PlayerActor, bool Debug, bool DisableFocusReticle, AInteractiveObject IObject, UMaterialInstanceDynamic* ReticleMaterial, float DeltaTime, TSubclassOf ReticleBlueprint, AInteractiveObject **HighlightedObject, AInteractiveObject **FocusedObject, float& HighlightTimer, float& UnFocusTimer, bool PSMoveTriggerDown, float FocusTime, FVector FocusPoint)
: _SpawnedReticle(*SpawnedReticle), _HighlightedObject(*HighlightedObject), _FocusedObject(*FocusedObject)

{
	_ActiveSystem = ActiveSystem;
	_WorldObject = WorldObject;
	_Position = Position;
	_Rotation = Rotation;
	_ReticleMaxDistanceVR = ReticleMaxDistanceVR;
	_ReticleMaxDistance2D = ReticleMaxDistance2D;
	_PlayerActor = PlayerActor;
	_ReticleFloatDistance = ReticleFloatDistance;
	_ReticleFadeTimer = ReticleFadeTimer;
	_ReticleFadeTimer = ReticleFadeTime;
	_Debug = Debug;
	_ReticleVisible = ReticleVisible;
	_DisableFocusReticle = DisableFocusReticle;
	_IObject = IObject;
	_ReticleMaterial = ReticleMaterial;
	_DeltaTime = DeltaTime;
	_ReticleBlueprint = ReticleBlueprint;
	_HighlightTimer = HighlightTimer;
	_UnFocusTimer = UnFocusTimer;
	_PSMoveTriggerDown = PSMoveTriggerDown;
	_FocusTime = FocusTime;
	_FocusPoint = FocusPoint;
}

//task name
static const TCHAR* GetTaskName()
{
	return TEXT("FVisionGameThread");
}

FORCEINLINE static TStatId GetStatId()
{
	RETURN_QUICK_DECLARE_CYCLE_STAT(FMultiThreadingTest, STATGROUP_TaskGraphTasks);
}

//return thread for the task
static ENamedThreads::Type GetDesiredThread()
{
	return ENamedThreads::GameThread;
}

static ESubsequentsMode::Type GetSubsequentsMode()
{
	return ESubsequentsMode::TrackSubsequents;
}

//Main Function
void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent)
{
	if (_ActiveSystem)
	{
		// make sure current highlighted object is still in active objects list
		//  this happens when an object is focused on when it unregisters itself
		if (_HighlightedObject != NULL && (!AInteractiveObject::InteractiveObjects.Contains(_HighlightedObject) || !_HighlightedObject->IsInteractable()))
		{
			// look away from object and unset
			if (_HighlightedObject->VirtualHandEngaged)
			{
				if (_FocusedObject != NULL)
				{
					_FocusedObject->PSMoveDisengage();
				}
			}
			_HighlightedObject->OnLookAway();
			_HighlightedObject = NULL;
		}

		// same as above but for focused object
		if (_FocusedObject != NULL && (!AInteractiveObject::InteractiveObjects.Contains(_FocusedObject) || !_FocusedObject->IsInteractable()))
		{
			// look away from object and unset
			if (_FocusedObject->VirtualHandEngaged)
			{
				if (_FocusedObject != NULL)
				{
					_FocusedObject->PSMoveDisengage();
				}
			}
			_FocusedObject->OnLookAway();
			_FocusedObject = NULL;
		}

		/** Sort out highlighting/focusing on objects */
		if (_IObject == NULL)
		{
			// not looking at any objects, sort out any objects we were looking at

			// un-highlight object immediately if not looking at anything
			if (_HighlightedObject != NULL)
			{
				if (_HighlightedObject->VirtualHandEngaged)
				{
					if (_FocusedObject != NULL)
					{
						_FocusedObject->PSMoveDisengage();
					}
				}
				_HighlightedObject->OnLookAway();
				_HighlightedObject = NULL;
			}

			// un-focus on focused item if we're not looking at anything
			if (_FocusedObject != NULL)
			{
				_UnFocusTimer += _DeltaTime;
				if (_UnFocusTimer >= 0)
				{
					if (_FocusedObject->VirtualHandEngaged)
					{
						if (_FocusedObject != NULL)
						{
							_FocusedObject->PSMoveDisengage();
						}
					}
					_FocusedObject->OnLookAway();
					_FocusedObject = NULL;
				}
			}
		}
		else 
		{

			// reset un-focus timer if we're looking at something
			_UnFocusTimer = 0;

			// still looking at a new object
			if (_HighlightedObject == _IObject)
			{
				_HighlightTimer += _DeltaTime;
				if (_HighlightTimer > _FocusTime)
				{
					// new focused item!
					if (_FocusedObject != NULL) 
					{
						// unset existing item first
						if (_FocusedObject->VirtualHandEngaged)
						{
							if (_FocusedObject != NULL)
							{
								_FocusedObject->PSMoveDisengage();
							}
						}
						_FocusedObject->OnLookAway();
					}

					// update new focused object and unset highlighted pointer
					_FocusedObject = _IObject;
					_HighlightedObject = NULL;

					// send onfocus event
					if (_PSMoveTriggerDown)
					{
						if (_FocusedObject != NULL)
						{
							_FocusedObject->PSMoveEngage();
						}
					}
					_FocusedObject->OnFocus();
				}
			}
			else
			{
				// looking at something that isn't the current focused or highlighted object
				// unset existing Highlighted Object if exists
				if (_HighlightedObject != NULL) 
				{
					if (_HighlightedObject->VirtualHandEngaged)
					{
						if (_FocusedObject != NULL)
						{
							_FocusedObject->PSMoveDisengage();
						}
					}
					_HighlightedObject->OnLookAway();
					_HighlightedObject = NULL;
				}

				// set new highlighted object (if it's not the currently focused object)
				if (_IObject != _FocusedObject)
				{
					_HighlightedObject = _IObject;
					_HighlightedObject->OnHighlight();
					if (_FocusedObject != NULL)
					{
						if (_FocusedObject->VirtualHandEngaged) 
							if (_FocusedObject != NULL) 
							{
								_FocusedObject->PSMoveDisengage();
							}
						_FocusedObject->OnLookAway();
						_FocusedObject = NULL;
					}
				}
				_HighlightTimer = 0;
			}
		}

		bool check = false;
		// return true if reticle already present! (and not disabled)
		if (_SpawnedReticle != NULL)
		{
			check = true;
			if (_DisableFocusReticle)
			{
				// hide already existing reticle
				_SpawnedReticle->SetActorHiddenInGame(true);

				// return false to stop processing the reticle
				check = false;
			}
		}
		else
		{
			// early out if the reticle isn't meant to be enabled anyway
			if (_DisableFocusReticle) check=false;
			else
			{

				// return false if reticle not set
				if (_ReticleBlueprint == NULL) check=false;
				else
				{
					// spawn our reticle Blueprint!
					_SpawnedReticle = _WorldObject->SpawnActor(_ReticleBlueprint);
					if (_SpawnedReticle)
					{
						_SpawnedReticle->SetActorHiddenInGame(true);
						_ReticleVisible = false;

						// find player camera
						ACharacter* Player = UGameplayStatics::GetPlayerCharacter(_WorldObject, 0);
						if (Player)
						{
							// cast to our character class
							ATheAssemblyCharacter* PlayerCharacter = Cast<ATheAssemblyCharacter>(Player);
							if (PlayerCharacter)
							{
								// attach reticle to camera so it gets updated with head rotation/position
								_SpawnedReticle->AttachRootComponentTo(PlayerCharacter->FirstPersonCameraComponent);
							}
						}


						// make an instance of it's material
						TArray<UActorComponent*> Meshes = _SpawnedReticle->GetComponentsByClass(UMeshComponent::StaticClass());
						for (int meshIdx = 0; meshIdx < Meshes.Num(); meshIdx++)
						{
							UMeshComponent* Mesh = Cast<UMeshComponent>(Meshes[meshIdx]);
							if (Mesh)
							{
								// find a suitable material on the reticle
								for (int matIdx = 0; matIdx < Mesh->GetNumMaterials(); matIdx++)
								{
									// check that the material contains a definition for FocusEffectIntensity before making a dynamic version
									UMaterialInterface* Mat = Mesh->GetMaterial(matIdx);
									float tmp;
									if (Mat && Mat->GetScalarParameterValue("Alpha", tmp))
									{
										// create new dynamic material from existing one
										_ReticleMaterial = Mesh->CreateAndSetMaterialInstanceDynamic(matIdx);
										_ReticleMaterial->SetScalarParameterValue("Alpha", .0f);

										// return early now we have one valid material!
										check = true;
									}
								}
							}
						}
						check = true;
					}
					else
					{
						check=false;
					}
				}
			}
		}
		if (check)
		{
			// setup trace parameters
			FCollisionQueryParams TraceParams(FName(TEXT("VisionSystem Trace")), true);
			TraceParams.bTraceComplex = true;
			// ignore the player when tracing
			TraceParams.AddIgnoredActor(_PlayerActor);

			bool VREnabled = GEngine && GEngine->IsStereoscopic3D();

			// Hit result object
			FHitResult HitData(ForceInit);
			if (_WorldObject->LineTraceSingleByChannel(
				HitData,
				_Position,
				_Position + (_Rotation.Vector() * (VREnabled ? _ReticleMaxDistanceVR : _ReticleMaxDistance2D)),
				// trace by visibility
				ECC_Visibility,
				TraceParams
				))
			{
				// move reticle to slightly in front of hit point
				FVector Direction = _Position - HitData.ImpactPoint;
				Direction.Normalize();
				// float ReticleFloatDistance cm in front of target point
				FVector Position = HitData.ImpactPoint + (_ReticleFloatDistance * Direction);
				_SpawnedReticle->SetActorLocation(Position);
			}
			else
			{

#if WITH_EDITOR
if (_Debug && HitData.Actor != NULL)
{
//hit something else! Debug.
//DrawDebugString(_WorldObject, HitData.Location, FString::Printf(TEXT(“Wanted to hit %s → got %s”), *_IObject->GetHumanReadableName(), *HitData.GetActor()->GetHumanReadableName()), NULL, FColor::Magenta, 0.1f);
}
#endif

				// just place reticle as far away as we allow (so it isn't in the way of the player's depth perception)
				_SpawnedReticle->SetActorLocation(_Position + (_Rotation.Vector() * (VREnabled ? _ReticleMaxDistanceVR : _ReticleMaxDistance2D)));
			}

			// make sure reticle is visible
			if (_DisableFocusReticle)
			{
				_SpawnedReticle->SetActorHiddenInGame(true);
				_ReticleVisible = true;
			}
			else
			{
				_SpawnedReticle->SetActorHiddenInGame(false);
				_ReticleVisible = false;
			}
		}
	}
	else
	{
		// make sure reticle is hidden if vision system is disabled
		if (_SpawnedReticle != NULL)
		{
			_SpawnedReticle->SetActorHiddenInGame(true);
			_ReticleFadeTimer = .0f;
			_ReticleVisible = false;
		}
	}// end: if (ActiveSystem) {	

	if (_SpawnedReticle != NULL && _ReticleVisible)
	{
		if (_ReticleMaterial != NULL)
		{
			// if fade time is zero, immediately make the reticle visible
			if (_ReticleFadeTime == 0)
			{
				_ReticleMaterial->SetScalarParameterValue("Alpha", 1.0f);
			}
			else
			{
				// process time passing
				_ReticleFadeTimer += _DeltaTime;

				if (_ReticleFadeTimer >= _ReticleFadeTime)
				{
					_ReticleFadeTimer = _ReticleFadeTime;
				}

				_ReticleMaterial->SetScalarParameterValue("Alpha", _ReticleFadeTimer / _ReticleFadeTime);
			}
		}
	}
	else
	{
		// reset timer
		_ReticleFadeTimer = .0f;
	}
}

};

The vision system has been setup up like that because the threaded bit needs to run on the game thread.
One thing I can notice by myself is the fact I use prereq as I can just pass NULL and be done with it.

Another thing I want to add is the fact that I can find the thread in the stats of the game.