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.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 }
- 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