Some Simple Optimizations in Unreal Engine

When it boils down to it you should profile things before optimizing however, these things can be added without the need to profile.

 

  • TArray.Reserve(size)
    • This will reserve memory for the array in advance. Do this before modifying an array to prevent allocations in a loop etc. When adding elements to a container in a loop, performances can be impacted because of multiple heap allocations to resize the container.
    • Also, do this on temporary allocations. (if you allocate an array in a method and then return that array later or need to do something specific to the results)
// Adds N copies of a character to the end of an array.
void AppendCharacterCopies(char CharToCopy, int32 N, TArray<char>& Result)
{
    if (N > 0)
    {
        Result.Reserve(Result.Num() + N);
        for (int32 Index=0; Index < N; Index++)
        {
            Result.Add(CharToCopy);
        }
    }
}

void AppendCharacterCopies(char CharToCopy, int32 N)
{
    if (N > 0)
    {
        TArray<char> Result;
        Result.Reserve(N);
        for (int32 Index = 0; Index < N; Index++)
        {
            Result.Add(CharToCopy);
        }

        // do something with Result
    }
}
  • TArray.Emplace(type)

    • By using Emplace over Add you don’t create a copy of the type. Really only use emplace on types larger than 8 bytes or rather don’t use it on things like ints.
    • Add (or Push) will copy (or move) an instance of the element type into the array. 
    • Emplace will use the arguments you give it to construct a new instance of the element type. 
  • Range-based for loops

    • By using range-based for loops you can drop an allocation from the int32 i = 0; ... and it also makes your code a bit cleaner.
for (const auto& Elem : MyContainer)
{
    // TArray - Elem
    // TMap - Elem.Key/Elem.Value
}
  • Pass by reference

    • Passing by reference you don’t copy said member which doesn’t do another allocation of the type.
    • Don’t pass normal types (int/float/bool) by reference as that actually can slow down performance.
  • Smaller types

    • Use smaller types like uint8 or uint16 when possible. Sure, uint16 isn’t exposed to Blueprint, so that really limits you however, you may not need to expose that to BP.
    • Keep in mind that FString is 16 bytes, FName is 10 bytes, FText is 40 bytes, and uint8 is 1 byte.
    • I only pass by value if the type is > 8 bytes. TypeKindMin/Max ValuesSizeExposed to BPint8/uint88 bit signed/unsigned integer-128 to 128 - 0 to 2551 bytefalse/trueint16/uint1616 bit signed/unsigned integer-32,768 to 32,768 / 0 to 65,5352 bytesfalse/falseint3232 bit signed integer-2,147,483,648 to 2,147,483,6474 bytestrueuint3232 bit unsigned integer 0 to 4,294,967,2954 bytesfalseint6464 bit signed integer-9,223,372,036,854,775,808 to 9,223,372,036,854,775,8078 bytestrueuint6464 bit unsigned integer0 to 18,446,744,073,709,551,6158 bytesfalse
  • Tick

    • Disable tick on actors that don’t need ticking.
    • Add PrimaryActorTick.bCanEverTick = false; to the constructor of the Actor class.
    • Tick is much more performance in C++ than in BP, but you still shouldn’t tick actors that don’t need ticking.
  • UMG

    • Avoid using the bindings as they’re essentially Tick with a pretty name. Instead, use delegates to update and maintain your UI.
  •  

 

 

until next time