Can I make AActors tick in parallel?

Is there a way to make two Actors tick in parallel.
I am profiling my scene using Unreal Insights and my Tick takes about 60ms.
In the breakdown it shows the two actor ticks (both around 30ms) laid out serially in the timeline.
The actors do not interact with each other, so I am wondering if it is possible to tick them at the same time so that I can half my frame Tick time.
Does this make sense?

Thanks so much in advance.

1 Like

The CPU can only do one thing at a time. No matter what program you’re using… :wink:

if i get you right, you want to run gameplay code in parallel in several diffirent threads. Afaik UE doesn’t support it for GameThread.

Unfortunately not. Afaik gameplay code running on a single thread on purpose to simplify it’s logic without diving into synchronization and other multithreading hell things: gameplay by its own is already complex enough.
While it’s probably technically possible, i guess it won’t be an easy task for epics to move engine to gameplay multithreading and would make things even harder for us, ue users.

Thanks @ClockworkOcean and @IrSoil
The reason why I am asking is because of this sentence:

…many actors can be updated in parallel if they’re in the same group

(from UE documentation: Actor Ticking)

So it seems possible, right? I am just wondering how.

It just means ‘in the same group, in the same tick’. They still get updated one at a time.

As others pointed out, not possible on the blueprint side of things.

30ms is massive, you don’t want that on the gamethread. Even if you could parallelize the two, you’d still be running at 30 fps which is awful.

I won’t even recommend moving away from Tick into a Timer, because that would generate stutters on timer basis, which sucks.

At this point, you need to either optimize/rework your stuff to smooth it up overtime, or run it in a separate dedicated thread so that it doesn’t impact game thread. In both cases, you’ll want to move to C++, and it probably won’t be cycling as fast as every Tick.

Here are some common optimization techniques :

  • Move from blueprints to C++. If you are doing some algorithm with lots of calculations, lots of nodes especially functions calls, then simply moving to C++ can improve performance by a tenfold.

  • Split your calculations over several ticks. If you are calling several functions which are heavy, reorganize your code so you can call FunctionA in a tick then FunctionB in the next tick then process results in the next tick. Cache results in class variables. Here’s a simplified example :

// class member variables
    int32 CalculationStep;
    float ResultA;
    float ResultB;

void AMyActor::Tick(float dt)
{
    switch (CalculationStep)
    {
        case 0:
            ResultA = SomeHeavyFunctionA();
            break;
        case 1:
            ResultB = SomeHeavyFunctionB();
            break;
        case 2:
            ProcessResults();
            break;
    }
    CalculationStep = (CalculationStep + 1) % 3;
}
  • If you are using for loops, you can achieve something similar, by “globalizing” the index and the variables you need in loop body. For example :
//OLD CODE:
void AMyActor::Tick(float dt)
{
    for (auto& Element : SomeArray)
        DoSomeStuff(Element);
}

//NEW CODE:
// class member vars
    int32 LoopIndex;

void AMyActor::Tick(float dt)
{
    // limit to 100 iterations per frame
    for (int32 NumIt = 0; NumIt < 100 && LoopIndex < SomeArray.Num(); ++NumIt, ++LoopIndex)
    {
        auto& Element = SomeArray[LoopIndex];
        DoSomeStuff(Element);
    }
    if (LoopIndex == SomeArray.Num())
        LoopIndex = 0;
}
  • Alternatively, if your code doesn’t interact too much with the gameplay and doesn’t rely on engine functions that require main thread, then you can create a thread to do the heavy calculations. Initialize it with all the data required to calculate, then let it report back on main thread once it’s done. This approach is not efficient if you need to constantly communicate between threads, so the algorithm/calculations should be able to run in isolation from the initial data alone and until the calculation is done. Of course you can update the data inbetween each “cycle”, hopefully you get the point.
3 Likes

So all Actors in the same Tick Group will be processed serially?
What is the parallel referring to then?

So, actually, I am using C++.
I am not looking to achieve 120fps or anything like that.
This is not for a game.
What is taking so long is updating millions of vertex positions and normals every frame.
I have a custom UDynamicMeshActor with about 1.5 million triangles.
I have played with all the different functions from FMeshNormals, etc, and found a good setup. But between the recalculation and then updating the UDynamicMeshComponent with FastNotifyPositionsUpdated( true );, it just takes a while :slight_smile:

In my scene I have two of those actors, each takes 30ms per tick. So if I could get them to tick in parallel, in theory that would bring it down to 30ms for the total tick (instead of the 60ms I am getting right now).

Does this make sense?

Ahh ok. Was wondering the use case.

Some things I’d look at - are all vertices all in view at once and do the vertices have collision? If not chunk them up and don’t update the chunks that are not in view. Or do some kind of LOD thing where distant chunks only get updated less or at a different granularity.

Hmm maybe you can set up your own sort of pipeline to calculate and update the meshes in parallel of the game’s ticking system. I’m thinking something like this :

  • bind a Tick to TG_PrePhysics, where you start an async task to do calculations (and updating if possible) in a separate thread, for each actor

  • bind a second Tick to TG_PostPhysics or TG_PostUpdateWork, and do a hard wait for each task to finish, retrieve result and finish updating

Or you could just start both tasks and wait both of them in a single Tick function, but it’d be significantly better to run it in parallel of all other ticks & physics (& possibly rendering). Maybe start with one function first and then try to upgrade it.

1 Like

No collision at all. Unfortunately I have to assume all will be in view at once due to the nature of the output (full mesh view and translucent materials are common use cases).

Thanks, great suggestion.
I was thinking about doing something like this as well and your idea sounds like the only real option.
I’ll give it a go over the next few days / week and will let you know if this worked for me.

@Chatouille I had a look at the Async API and did something along the lines of what you suggested.
Since my tick consists basically of two parts:

  1. Calculate new positions + normals
  2. Update dynamic mesh to be drawn

I am running part 1) in Async.
So on frame 1, I start 1) and do nothing else.
On all consecutive frames, I wait for the Async from previous frame to be done (if not done yet) and then use the result for 2). I then immediately start Async 1) for the next frame.
Overall, this obviously means I am running 1 frame delay on my Actor, but since I am in control, I just make sure that all other actors that need to be synchronized for rendering are also getting a 1 frame delay.
Seems to be working quite well and 1), which has heavy calculations in it is no longer slowing me down much.

Thanks again for your help.
Much appreciated.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.