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 } }
- This will reserve memory for the array in advance. Do this before modifying an array to prevent allocations in a loop etc.
- 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(orPush) will copy (or move) an instance of the element type into the array.Emplacewill 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 }
- By using range-based for loops you can drop an allocation from the
- 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.
Type Kind Min/Max Values Size Exposed to BP int8/uint8 8 bit signed/unsigned integer -128 to 128 - 0 to 255 1 byte false/true int16/uint16 16 bit signed/unsigned integer -32,768 to 32,768 / 0 to 65,535 2 bytes false/false int32 32 bit signed integer -2,147,483,648 to 2,147,483,647 4 bytes true uint32 32 bit unsigned integer 0 to 4,294,967,295 4 bytes false int64 64 bit signed integer -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 8 bytes true uint64 64 bit unsigned integer 0 to 18,446,744,073,709,551,615 8 bytes false
- 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.

- 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