TObjectIterators the speedy-er way

So as best I understand, in general using
for (TObjectIterator<UStaticMeshComponent> It; It; ++It)
for (TObjectIterator<UStaticMeshComponent> It; It; ++It)
is bad - it's not a good idea to iterate all the UStaticMeshComponents (in a fresh save there's about 10k of them) or whatever other component you want to iterate over, but sometimes it seems somewhat unavoidable for whatever reason. I just wanted to share the results of my profiling in case it helps This simple approach takes between 5-7ms to run on my PC.
for (TObjectIterator<UStaticMeshComponent> It; It; ++It)
{
UStaticMeshComponent* StaticMeshComponent = *It;
if (StaticMeshComponent && StaticMeshComponent->GetWorld() == World && !StaticMeshComponent->ComponentTags.
Contains(ResourceRouletteTag))
{
if (const UStaticMesh* StaticMesh = StaticMeshComponent->GetStaticMesh())
{
FName MeshPath = FName(*StaticMesh->GetPathName());
if (MeshesToDestroy.Contains(MeshPath))
{
StaticMeshComponent->SetActive(false);
StaticMeshComponent->SetVisibility(false);
StaticMeshComponent->DestroyComponent();
}
}
}
}
for (TObjectIterator<UStaticMeshComponent> It; It; ++It)
{
UStaticMeshComponent* StaticMeshComponent = *It;
if (StaticMeshComponent && StaticMeshComponent->GetWorld() == World && !StaticMeshComponent->ComponentTags.
Contains(ResourceRouletteTag))
{
if (const UStaticMesh* StaticMesh = StaticMeshComponent->GetStaticMesh())
{
FName MeshPath = FName(*StaticMesh->GetPathName());
if (MeshesToDestroy.Contains(MeshPath))
{
StaticMeshComponent->SetActive(false);
StaticMeshComponent->SetVisibility(false);
StaticMeshComponent->DestroyComponent();
}
}
}
}
1 Reply
Beef
BeefOP3w ago
Comparatively, separating out the iteration and the performing some of the operations in parallel, this takes <1ms to perform the same task on a CPU with 6 cores.
TArray<UStaticMeshComponent*> WorldMeshComponents;
for (TObjectIterator<UStaticMeshComponent> It; It; ++It)
{
UStaticMeshComponent* StaticMeshComponent = *It;
if (StaticMeshComponent && StaticMeshComponent->GetWorld() == World)
{
WorldMeshComponents.Add(StaticMeshComponent);
}
}

TArray<UStaticMeshComponent*> ComponentsToDestroy;

ParallelFor(WorldMeshComponents.Num(), [&](int32 Index)
{
UStaticMeshComponent* StaticMeshComponent = WorldMeshComponents[Index];
if (StaticMeshComponent && !StaticMeshComponent->ComponentTags.Contains(ResourceRouletteTag))
{
if (const UStaticMesh* StaticMesh = StaticMeshComponent->GetStaticMesh())
{
FName MeshPath = FName(*StaticMesh->GetPathName());
if (MeshesToDestroy.Contains(MeshPath))
{
FScopeLock Lock(&CriticalSection);
ComponentsToDestroy.Add(StaticMeshComponent);
}
}
}
});

for (UStaticMeshComponent* StaticMeshComponent : ComponentsToDestroy)
{
StaticMeshComponent->SetActive(false);
StaticMeshComponent->SetVisibility(false);
StaticMeshComponent->DestroyComponent();
}
TArray<UStaticMeshComponent*> WorldMeshComponents;
for (TObjectIterator<UStaticMeshComponent> It; It; ++It)
{
UStaticMeshComponent* StaticMeshComponent = *It;
if (StaticMeshComponent && StaticMeshComponent->GetWorld() == World)
{
WorldMeshComponents.Add(StaticMeshComponent);
}
}

TArray<UStaticMeshComponent*> ComponentsToDestroy;

ParallelFor(WorldMeshComponents.Num(), [&](int32 Index)
{
UStaticMeshComponent* StaticMeshComponent = WorldMeshComponents[Index];
if (StaticMeshComponent && !StaticMeshComponent->ComponentTags.Contains(ResourceRouletteTag))
{
if (const UStaticMesh* StaticMesh = StaticMeshComponent->GetStaticMesh())
{
FName MeshPath = FName(*StaticMesh->GetPathName());
if (MeshesToDestroy.Contains(MeshPath))
{
FScopeLock Lock(&CriticalSection);
ComponentsToDestroy.Add(StaticMeshComponent);
}
}
}
});

for (UStaticMeshComponent* StaticMeshComponent : ComponentsToDestroy)
{
StaticMeshComponent->SetActive(false);
StaticMeshComponent->SetVisibility(false);
StaticMeshComponent->DestroyComponent();
}
If you have an even larger list to work through, rather than using a mutex in the loop you can make thread-local variables to store the results of each parallel operation in and just batch write/append the results at the end of processing to your other variable. There's quite a bit of information of all its capabilities in the ParallelFor.h header and examples online for more information. There's likely other things that can be done to further improve this - this is the result of my reading and experimentation so far
Want results from more Discord servers?
Add your server