Skip to main content
GameDev.net gamedev.net

PRO Tired of ads? Read GameDev.net ad-free and help keep the community independent with GameDev Pro โ€” $3/month.

Unreal Engine - The "EngineMisc" in the low level memory tracker could be tricky for you.

Unreal Engine - The "EngineMisc" in the low level memory tracker could be tricky for you.

SquallLiu
The Graphic Guy Squall ยท ยท 2 min read
473 0

A quick share about my finding for the EngineMisc category under Unreal's LLM (Low level memory tracker, not the AI LLM).

If you ever wonder why this category always shows a high amount of memory, then this knowledge might help ๐Ÿ™‚.

First of first, what does this EngineMisc mean exactly? From the UE documentation:

Any low-level memory that is not tracked in any other category.

We can agree that the description is too broad and couldn't really help us.

After some codebase digging, I find the EngineMisc is actually measuring the scope of FEngineLoop::Tick():

void FEngineLoop::Tick()
{
	SCOPED_NAMED_EVENT_TEXT("FEngineLoop::Tick", FrameColor);
	SCOPE_STALL_COUNTER(FEngineLoop::Tick, 2.0);

#if !UE_BUILD_SHIPPING && !UE_BUILD_TEST && MALLOC_GT_HOOKS
	FScopedSampleMallocChurn ChurnTracker;
#endif
	// let the low level mem tracker pump once a frame to update states
	LLM(FLowLevelMemTracker::Get().UpdateStatsPerFrame());

	LLM_SCOPE(ELLMTag::EngineMisc);
	
	// ..... continue FEngineLoop::Tick() logic ..... //
}

Yikes! That is a huge range to investigate if you're doing memory optimizations.

Fortunately, the โ€œTickโ€ function suggested that EngineMisc is tracking per-frame memory allocations. Especially those tickable objects.


Let me use the Lyra sample here, with the following test code:

void ULyraWeaponStateComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{

	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	if (APawn* Pawn = GetPawn<APawn>())
	{
		if (ULyraEquipmentManagerComponent* EquipmentManager = Pawn->FindComponentByClass<ULyraEquipmentManagerComponent>())
		{
			if (ULyraRangedWeaponInstance* CurrentWeapon = Cast<ULyraRangedWeaponInstance>(EquipmentManager->GetFirstInstanceOfType(ULyraRangedWeaponInstance::StaticClass())))
			{
				CurrentWeapon->Tick(DeltaTime);
			}
		}
	}

	MyLargeBuffer.Empty();
	MyLargeBuffer.SetNum(9999999);
	for (int32 i = 0; i < 100; i++)
	{
		MyLargeBuffer[i] = FMatrix();
	}
}

Scary! Anything you added in your customized components would eventually be counted as EngineMisc.


To fix this, adding your own LLM tag/scope:

#include "HAL/LowLevelMemTracker.h"
#include "HAL/LowLevelMemStats.h"
DECLARE_LLM_MEMORY_STAT(TEXT("ULyraWeaponStateComponent::Tick"), STAT_LyraWeaponStateLLM, STATGROUP_LLMFULL);
DECLARE_LLM_MEMORY_STAT(TEXT("ULyraWeaponStateComponent::Tick"), STAT_LyraWeaponStateSummaryLLM, STATGROUP_LLM);
LLM_DEFINE_TAG(LyraWeaponLLM, NAME_None, NAME_None, GET_STATFNAME(STAT_LyraWeaponStateLLM), GET_STATFNAME(STAT_LyraWeaponStateSummaryLLM));

void ULyraWeaponStateComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	LLM_SCOPE_BYTAG(LyraWeaponLLM);
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	if (APawn* Pawn = GetPawn<APawn>())
	{
		if (ULyraEquipmentManagerComponent* EquipmentManager = Pawn->FindComponentByClass<ULyraEquipmentManagerComponent>())
		{
			if (ULyraRangedWeaponInstance* CurrentWeapon = Cast<ULyraRangedWeaponInstance>(EquipmentManager->GetFirstInstanceOfType(ULyraRangedWeaponInstance::StaticClass())))
			{
				CurrentWeapon->Tick(DeltaTime);
			}
		}
	}

	MyLargeBuffer.Empty();
	MyLargeBuffer.SetNum(9999999);
	for (int32 i = 0; i < 100; i++)
	{
		MyLargeBuffer[i] = FMatrix();
	}
}

Ta, your memory usage won't just roughly show in EngineMisc.

In the fixed screenshot, you can see the EngineMisc dropped to 157.76MB after I added the custom tag.

Which means the LLM can also trace parent-child tags properly. And that is a good thing.


Summary

If you ever wonder why the โ€˜EngineMiscโ€™ shows a high amount memory usage.

The reason is that category mainly traces the memory usage from tick functions.

But the official document is not clear enough.

If you join or take over a project that has a similar issue. Try to narrow down the LLM usage in the following areas:

UActorComponent::TickComponent()

AActor::TickActor()

Discussion

Loading comments...