Skip to content

Performance

Performance in Logic Driver is largely up to your implementation. Understanding the basics of how Logic Driver works can help you optimize accordingly.

With proper setup, it's possible to design a state machine in the blueprint editor, but at run-time executes almost entirely in C++1 and does not tick.

Profiling

Logic Driver supports stat profiling for performance sensitive operations.

To enable, type the following from your console:

stat LogicDriver

Profile

Tracking Editor Performance

For editor specific performance tracking, use:

stat LogicDriverEditor

This will track editor intensive operations, like running editor construction scripts.

See Unreal Engine Stat Commands for more information.

Stats to Watch

Tick and Update

  • These usually go hand in hand, as tick will call Update. The more OnStateUpdate logic you have in each state and the complexity of your transitions will impact this number.

GetValidTransition

  • When a state is finding a transition to take.

CanTransition

  • When a transition is evaluating.

GraphEvaluation

  • Whenever a local blueprint graph in a state machine blueprint is evaluating. Fast path indicators in a state machine graph will prevent GraphEvaluation.

EvaluateAndTakeTransitionChain

  • This can occur on autobound event transitions or if you are manually calling EvaluateAndTakeTransition from an SMInstance.

Initialization

When state machines initialize they map out all available states, instantiate node instances, and run construction scripts. This process can be very expensive for larger state machines. Try to limit the number of times initialization is called and consider when you call it. Once a state machine is initialized it usually doesn't need to be initialized again.

Guid Calculation

Guid calculation is one of the most expensive operations that can occur during initialization. Because of this, later versions of the plugin will instead calculate Guids during blueprint compilation, saving substantially on initialization time.

This setting can be changed under Project Settings -> Logic Driver Editor -> Calculate Guids on Compile, but defaults to true and should remain enabled.(1)

  1. This setting exists primarily for troubleshooting, as caching Guids for state machine references has proven to be a complex scenario.

Dynamic State Machine References

Using dynamic references will always calculate the reference's Guids at run-time. There are no plans to change this, and is a trade off for using dynamic references. The rest of the state machine, and non-dynamic references, will still be optimized.

Node Instances

Custom node instances will always be instantiated for each node, but default node instances (no custom class) will not be instantiated unless they are programmatically accessed. This is to reduce memory overhead and initialization times. If you have a state machine that constantly accesses its default node instances, it might be beneficial to call PreloadAllNodeInstances, but this should not be necessary in most situations.

Async Initialization

In Pro you can initialize instances async off of the game thread. This can reduce blocking operations and may be helpful if you have many state machines you need to initialize at once during game play.

  • UObject instances can be created async by calling CreateStateMachineInstanceAsync.
  • Components have an InitializeAsync method.
  • Components can be initialized async automatically on BeginPlay by setting the Begin Play Initialization Mode as long as Initialize on Begin Play is checked.

InitializeAsync

Thread Safety

Async initialization is a multi-threaded operation, so be careful of any code that may not be thread safe. An operation that occurs during initialization which may not be safe is node class instantiation. Check your C++ constructors on custom node classes for thread safe code and verify any properties are also thread safe.

Construction scripts will always run on the game thread, after node instantiation finishes.

Editor Thread Safety

Sometimes node classes may only be unsafe initializing async in the editor. An example would be Logic Driver's Text Graph Properties. They contain slate style structs that can access static information in their constructors in a non-thread safe way. These structs only exist in the editor so run-time use is safe.

Under the node class containing the unsafe properties, you can uncheck Is Editor Thread Safe preventing the node class from instantiating off of the game thread.

ThreadSafe

The node compiler will always disable Is Editor Thread Safe if it detects Text Graph Properties.

Ticking

The default behavior is for state machines to tick every frame. Each tick will update the state machine, evaluating transitions and running state update logic.

Each transition has a Conditional Result which evaluates each update by default. The more complex the logic here, the more expensive the operation.

Lower the tickrate or disable tick

It is possible to lower the tick interval of the state machine or of the component. Some users have found success in dynamically adjusting the tickrate based on proximity to the player.


You can turn off tick or prevent it from ever being registered.

Disabling tick is best used with event based transitions.

When tick is disabled OnStateUpdate logic won't run unless Update() is called manually.

Event Based Transitions

One of the best performance optimizations is to use event based transitions. This will limit transition evaluation only to when the event is called. With this approach tick evaluation is not required and tick can even be turned off all together.

Blueprint Graph Evaluation

Evaluating blueprint graphs can have overhead. Logic Driver has optimizations to help limit going through the local blueprint graph if possible.

Fast Path

It's possible to avoid calls to the local graph execution points and instead execute natively in the following situations:

  • There is no logic connected to the node.
  • The node is a Conditional Result node and is either defaulted to false or to true.
  • The node is directly connected to an Instance version of the node (a custom node class is assigned) and not connected to anything else.
    • In this case the function is executed directly on the custom node class.
    • This primarily benefits C++ node classes, but still avoids the local graph for blueprint node classes.

When one of these situations is true and no BP graph will be executed at all, a lightning symbol will show up indicating that node will avoid the blueprint graph.(1) This is the same symbol that animation state machine fast path uses and represents the same behavior.

  1. Exposed variable graphs may still execute in blueprints.

If all execution points are fast path then the owning node will be considered fast path and an icon will show up above it.

FastPath

Transition Fast Path

Transitions won't display the fast path icon, but the tooltip will show it.

Public Variable Evaluation

Exposed variables on a state generally require blueprint graph evaluation which occurs when the state is entered.

In many cases, the value entered into a public property will just be a default value, without any variable connected. In this case Logic Driver sets the actual default value of that node instance based on the blueprint pin's entered value. However, the behavior during run-time is to evaluate the graph anyway.

That behavior can be changed under the node class by unchecking Eval Default Properties. This will avoid evaluation of default values. This setting will have no impact if a variable is connected to the public property in the state machine graph. The downside to this is if you modify the public property in the node class during run-time, it will not reset to the default value each time the state is entered like it would normally.

PublicProperties EvalDefaults

This is not a default optimization so behavior is consistent with variable evaluation and with previous versions.

Memory Usage

When a state machine is initialized, the entire state machine, and all state machine references, are loaded into memory. If a custom node class is assigned to a state or transition, a UObject will be instantiated. Each state stack or transition stack node will also instantiate a UObject. This can result in a high UObject count for very large state machines used on multiple contexts, and is something to be aware of.

To reduce memory usage, avoid assigning a custom node class when not required, such as if you just need a blank end state or an always true transition. A node without a custom class is inexpensive and is effectively just an embedded struct in the blueprint.

Avoiding the transition stack and using a single transition class for complicated transition logic is the preferred solution when performance is critical.

You may want to separate out large state machines and only load them when needed. Such as if you have various dialogue state machines for a character, only load in and initialize what is required. Then shutdown the state machine when it is no longer used.

Future Optimizations

Improving the UObject count for node classes is a potential enhancement that may be considered in a future update. Keep an eye on the roadmap for updates and more details.

One reason node memory operates the way it does currently, is because the node may listen to events that require it to be instantiated, such as OnRootStateMachineStart. It's also possible the instance is utilized from other nodes, or maintains information when it is not active. These cases have to be considered with any future optimizations.


  1. To define node logic in C++ requires custom node classes found in the Pro version.