Blueprint Performance

How to make good Ramen

=== Performence

Blueprint i slower then C++ but it is now slow

The most commong reason for moving over to C++ tend to be workflow and colloboration related rather then Performence.

Nonetheless BP can have a signigicant performence impact

BP is ~10x slower then C++ but depends on each case.

What cost performence varies withing each project-hardware for BP

---Performence Loss reasons---

Performence loss could come from executing large networks of nodes

It are the connections between the nodes that are expansive

Anything related to complex logic or math is therefore expensive in BP

Anything relateded to quick loops is also relative expansive in BP. (whileloop, DO N, ForEachLoop)

Anything that requires iteration of large number of actors/classes can be expensive.

Ticking is slightly more expensive in BP then in C++

In general the abuse of ticking is the most common cause of performence loss with BP.

For everyday usage of items above you are not likley to experience any slow downs

If you use these alot then eventurelly the tiny bits of performence loss will start to add upp.

Do not use ticking at all if performence is very important.

Blueprints always execute single threaded. By default it has a 1M (250000) instruction limit per tick and for all BP.

Limit only hit if things build in a suboptiomal way.

A BP in itself poses no signigicant overhead.

Meerly embeeding content in a BP (prefab system style) will have no measurable impact on performence.

1 Play in Editor (PIE) Is much slower.

2 Dev Play

3 Shipping Play

--- Ticking ---

The number one reason for perfroemce loss is typically abuse of ticking

Ticking is per frame updating - it should be used with care

The tick event itself is not the only thing ticking.

Timeline, mousex, animation blueprint, umg

You can disable ticking on BP by default (Project settings)

Use timers and instead

Use events instead

For non ticking blueprints unceck start with tick enabled to clean up the profiler lists.

--- Memory and Loading ---

BP can majorly impact memory consumption and loading times

BP differs from C++ when it comes to memory and loading. It is regarded as content

C++ is loaded right away on boot, BP is only loaded when needed.

Right click the object and use reference viewer to see what use what of the content.

Any form of reference between a BP and another BP or content will load it

Casts are also references

Right click object and use sizemap to see what is loaded

Plan ahead in how to organize and manage the references

Prevent BPs that reference a huge number of (espacially large) assets. Split those of into child BP classes.

Best technique is to create a clear hierarchy with rules in place for where assets references live.

1: Every key class is defined in C++ (makes it easy to move things to c++ later if needed)

2: A blueprint then inheriting from that C++ class. (functionality)

3: Another bp is then inheriting from the parent BP in turn. (onlky content loaded)

Content is ideally referenced primarliy by that third steps

Plus a clear set of rules regarding what is allowed to cast to what.

Function libraries

- care should also be given to function libraries

- If a single function is used then it is loaed in it's entirerty

- Any refererence to an asset, or a cast to another class, anywhere within the library will result in that content also getting loaded in.

- For that reason, function libraries should focus soley on functionality

GameMode is a way to seperate things to have a GameMode_Game and a GameMode_Menu

To complemnt all this you can use dynamic asset loading.

Soft references can control precisly when an asset is loaded.

AsyncLoadAsset instead of SetStaticMesh.

Will show up as a purple line in reference viewer.

Soft references that use a map to async load content is good when having large number of configurable parts.

--- Garbage collection

- Process of removing objects no longer required

If something is destroyed in game it is marked as such.

At regular intervals, the engine iterate all the objects that exist and retrive the ones marked as destroyed

It then fully remove these by clearing up menory space they are occupying

GC is somewhat expensive as it needs to iterate through all objects.

GC is automatic and only need to care about it if creating and destryoing large number of actors.

stat gc : cmd to see garbace collection Stats

=== Compilation

Compile time on blueprint no impace on runtime.

Negative effect on workflow.

Can be a sign of a memory/loading impact do to interwined references

Compile time most affected by

--- Number of BP nodes present in graph

BP has a 16 bit compiler limit wich limit result in maximum 64kb compiled BP.

Limit not visible, compile fails if hit. If hit you use BP wrong.

Macros are inserted into the graph does not much to improve compile time.

Functions makes a huge difference as they compile much faster and reduce overhead for the blueprint limit.

Splitting more functionality into different BP child actors/comonents would make a big difference

Or moving logic heavy steps to C++, in larger projects this is definently the recommended approach.

- Casting and other kinds of references

A cast result in another class being referenced. A BP cannot compile without also compiling any BP's it has a reference to.

Can create long chain reactions of compilations this way.

Even if the cast is invalid/fails, this is still the case.

In fact anything that reference a class, liuke casting does, have the same effect of compiling the other class.

Casting can also impace memory and load times.

The impact of memory is more serious then the impact it has on compile speed.

Don't cast more then you have to

Cast from some BP to the pawn class.

* Usually it's fine. The player class is already loaded att all times anyway.

* Casting to other key framework classes usally find to for the same reason

Cast from one small BP to another

* Usually its fine. If the BP's are small enough in functionality and content they contain the cast impact is minimal.

* Especally if both BP's are expected to exist in the same enviornment all the timne. Both will be loaded anyway. Like a button beside a door.

Cast from any BP to a central BP containing nothing but variables.

* Thats fine. The central BP will add no memory or loading overhead and will compile superfast as only variables.

Cast from a common BP to a BP rearly used.

* Not fine. THe rearly used one will add signigicant overhead, particly if its content or functionality heavy.

* Cast from the rearly used one to the common one would problay be onlky

Casting back and forth between all the framework classes

* From memory point of view it's ok but compile time may still suffer since it has to check all each time one is changed.

Design a system when and where to cast.

Designate a few classes as being ok to cast to.

Use C++ to brige classes.

C++ can provide project wide accessible variables.

A node can get the values from the class without having the class directly.

For example instead of Vehicle()->GetNumPassangers one has GetNumPassangeOfPlayerVehicle

Project Wide available functions that triggers the target (as a event) in the approriate target class.

Cast to a parent class and use it if possible.

A C++ parent class provides no overhead.

The parent class can contain nothing more then the variables and functions the children need.

For a BP parent class use inheritance and events to have a minimal parent class that gives as small overhead as possible.

Interfaces is a powerful tool to cut down on casting and references.

Generic functionality across many different classes without having to hardcode references to those classes.

Does Implement Interface node.

Use gameplay tags to avoid referencing a class if you wish to check if a actor is of a certain class.

It also prevents having to cast for checking the value of it's variables.

Actor has tag node.

A good casting strategy + native + interfaces + inheritence

And optionally gameplay tags.

--- Race condition / Circular references

Blueprint that depend on each order.

IsVailid to ensure they do exist and are loaded.