Testing and Profiling some of the many different get all actor methods in Unreal Engine 5 with C++
There are many ways to get all actors in Unreal, they're all great, but going off the recent myth-debunk video I wanted to have some fun testing them out.
Software Versions: Unreal Engine 5.5.1 | Rider 2024.3.2
Project Name: MyProject
The Unreal Engine YouTube account recently posted a video titled "Myth-Busting Best Practices in Unreal Engine | Unreal Fest 2024" and it's a wonderful video that I highly recommend. Mentioned in the video is that GetAllActorsOfClass
is faster than GetAllActorsWithInterface
and GetAllActorsWithTag
so I wanted to see if I could test it in Unreal. I'm not great at testing or profiling so I took it as an opportunity to practice those skills. Below is the video, the discussion around GetAllActorsOfClass
starts around 36 minute mark.
Take all results with cation and always profile for your own specific project. As the video mentions throughout, profiling is the best way to understand what works best for you and your game. This post is really only for practice and fun, all the functions that I'll talk about are all great and for my games they're all very performant. Use whatever works best for you and your project.
Always profile for you project. Results may vary.
So is GetAllActorsOfClass
faster than GetAllActorsWithInterface
, and in my rudimentary testing, yep, absolutely. In all my testing GetAllActorsOfClass
typically reigned supreme over all get all methods. Results may vary depending on the circumstance, but GetAllActorsOfClass
or using TActorRange
in a loop where the functions that were usually the fastest.
I took this testing opportunity to try out QUICK_SCOPE_CYCLE_COUNTER
and SCOPE_SECONDS_COUNTER
, two super great functions we can use for testing. Refer to Epic's official stat overview documentation for complete details, but breifly QUICK_SCOPE
will return the time in the Unreal Insights profiler and SCOPE_SECONDS
will set a double/float variable for logging, while both being scoped to their respective sections.
For testing I created an actor that spawned thousands of actors on BeginPlay
and then each second ran a different technique of getting all actors. The only actors spawned were very simple TestActor
and TestActorInt
classes that I crated for this test. The only difference is that TestActorInt
inherits an interface.
void AMyGetAllActors::TestGetAll()
{
QUICK_SCOPE_CYCLE_COUNTER(MyStat_GetAllActorsOfClass);
double ThisTime = 0;
TArray<AActor*> FoundActors;
if (MyWorld != nullptr)
{
SCOPE_SECONDS_COUNTER(ThisTime);
UGameplayStatics::GetAllActorsOfClass(MyWorld, AActor::StaticClass(), FoundActors);
}
UE_LOG(LogTemp, Log, TEXT("Time: %.8f, TotalActors: %d"), ThisTime, FoundActors.Num());
}
By all accounts my testing isn't perfect, but I used QUICK_SCOPE_CYCLE_COUNTER
at the beginning of the function to profile the function in insights and then added SCOPE_SECONDS_COUNTER
to set the time right before the GetAll
function for logging.
For a visual representation I created list view widget that listened for a delegate broadcast with a sorted TArray
derived from a TMap
to provide all the values. In most situations GetAllActorsOfClass
was always faster than GetAllActorsWithInterface
and GetAllActorsWithTag
.
A lot of my testing happened inside the editor, but the results were similar in a packaged build. The results are sorted by time every second so the rows will occasionally change places as the test runs.
To enable Unreal Insights click the trace button at the bottom of the editor, enable Stat Named Events
for QUICK_SCOPE
results and then select the Unreal Insights (Session Browser) option.
With the session browser open you start and stop recordings using the circular graph button next to Trace menu highlighted in the image below.
Once done you can double click to open the results. The quick scoped results aligned with the scoped seconds counter results, GetAllActorsOfClass
was usually much faster than GetAllActorsWithInterface
and GetAllActorsWithTag
.
The Unreal Myth Busting video is a lot of fun with a lot of great information. The video prompted a good opportunity to start getting more comfortable with testing and profiling.
If you cloned the GD Tactics Repo you can run the my example by directly dragging in the BP_Add_GetALL_Widget and MyGetAllActors actors into the OpenWorld level. You can adjust the amount of actors spawned via the details section for the MyGetAllActors
actor.
I defer to Epic's Unreal Engine developers or other C++ UE experts for more factual results. A lot of times it might not be the function that's the problem, but rather how the function is being used that might be effecting performance. Each project is different, each game has specific use cases, so testing and profiling may differ per project.
For my example I had an isolated open world with six timers running every second, so my example might not be the best for another project. However, I was happy to confirm what the video mentioned, that yes indeed, GetAllActorsOfClass
is faster than GetAllActorsWithInterface
and GetAllActorsWithTag
.
All the GetAll
actor functions are great that I'll likely use throughout development without affecting performance, however, I'll likely always keep performance in mind never trying to overuse something or use one of the functions in Tick
.
Harrison McGuire