Download

Can't use SetActorLocation() in a thread

So I’ve been working on this flocking simulation and it’s working well, but it needs to be optimized. Recently i’ve tried to implement multithreading to it so that the calculations for the next position and setting the boid actor in the next calculated position is done in threads instead of sequentially, hoping that this would improve the performance by a large margin and allow me to spawn more boids for the flock.

There is one problem though, when using SetActorLocation() in the thread, UE4 triggers a breakpoint immidiately on game start on the line where SetActorLocation() was used.

When i noticed that i tried to remove SetActorLocation() from the thread and just put it in the boid actors Tick() function so that the calculations for the next position are done in threads but SetActorLocation() is done sequentially. This resulted in very little improvement of the simulation performance which I’m assuming is because we’re doing the SetActorLocaton() sequentially and there’s too many boids to go through every tick? Or maybe because the SetActorLocation() is expensive to execute?

Here’s the thread class that does the work for each individual boid in the .h file



class CalculateFlockTask : public FNonAbandonableTask
{
public:
    CalculateFlockTask(ABoid* boid);

    ~CalculateFlockTask();

    //Requierd by UE4
    FORCEINLINE TStatId GetStatId() const
    {
        RETURN_QUICK_DECLARE_CYCLE_STAT(CalculateFlockTask, STATGROUP_ThreadPoolAsyncTasks);
    }


    ABoid* boid;


    TArray<AActor*> DetectOthers(ABoid* boid);
    FVector Align(ABoid* boid, TArray<AActor*> nearbyBoids);
    FVector Cohesion(ABoid* boid, TArray<AActor*> nearbyBoids);
    FVector Separation(ABoid* boid, TArray<AActor*> nearbyBoids);

    void Move(ABoid* boid, float DeltaTime);
    void Flock(ABoid* boid);
    void RotateMesh(ABoid* boid, FVector oldPosition, FVector newPosition);


    void DoWork();
};



This thread class is defined in the same .h file as an actor class called ParallelCalculator. This actor is spawned once upon game start and is given an array of all the boids that are spawned. In it’s Tick() function, it runs a for each loop on the array and creates a thread for each boid in the list to perform the calculations on to update their positions. SetActorLocation() is executed in the Move function.

This way of creating the thread is inspired by this example where someone creates a thread to calculate prime numbers in the background: Unreal 4 Threading - Running a Task on a Worker Thread - YouTube

Also, here’s the output from “Autos”, apparently the boid that the thread received is set to null, which is weird because when SetActorLocation() is removed from the thread and put into the boids Tick() function, the thread it is able to recognize the boid it received and do calculations for the next position and set it’s variables to different values.

I doubt that SetActorLocaton() is thread safe. Have you considered using particles instead of Actors?

AFAIK firing anything into the UE4 Game Thread from outside threads isn’t supported.

How would using particles instead help out? Does it work the same where i define a bunch of behavior functions in a single particles C++ files but it draws them in the game in a more efficient way?
​​​​

Well that sucks :confused:
Does anyone have a suggestion of how i could parallelise this simulation in some other way? Is there maybe a way for UE4 to do SetActorLocation() on an array of boids/actors all in one swoop instead of going through each one sequentially?

I am no expert on Particles but I was thinking if the functions are simple but the amount of Actors is extreme then the particle system and a Shader (GPU) could do it faster if possible.

Maybe you could give Boid a FVector member and store the target location there while running your algorithm then after it runs you go through all your boids in the game thread and call SetActorLocation with that cached value?

This is assuming it’s your flocking algorithm that’s bottlenecking and not calling SetActorLocation too many times.

The main way to optimize boids or other AI algorithms is to not run the algorithm when you don’t need to.

  • Figure out which flocks are near the player and only run boids on those.
  • Do something simpler, or nothing, on far away flocks.
  • Tick far away flocks less freqently, so they update only every few frames.
  • “Alternate ticks” for your flocks, so flocks take turns on which update

Many ways to do it. Google around there is a ton of available research on it. :cool:

You can also just do all your computations in threads, write it all to some buffer, and then apply those locations next frame or whenever in the main thread. Just treat the ABoids as read only when not on the main thread.

This is pretty much what I’m doing right now, calculating the next position for each individual boid by creating a thread for each one and running the flocking algorithm in the threads which also updates their position vectors. Then all ABoid actors have to do in their Tick() functions is run SetActorLocation() on themselves with their updated position vectors. But all i can get up to is 800 actors and FPS drops to like 20-30 FPS. So i’m pretty sure it’s SetActorLocation() that’s causing this framerate drop. Because I’m assuming UE4 goes through each ABoid actors Tick() function sequentially.

These are all valid ideas, but the algorithm isn’t really the problem right now since it’s being done in parallel for all boids and it works. The main issue is having too many actors in the game that are having their positions updated in every frame, which apparently can’t be done in parallel threads.

So does that mean i have to wait until this type of operation is supported in UE4 (if it ever will be)? Are you guys sure there isn’t some kind of workaround :/? Or rather, has anyone ever come across a UE4 game project where someone has 3000-8000+ actors in the game and they’re all having their positions updated every frame with a steady 60FPS?

Ah, I see your issue. Yea, 800 ticking actors is going to be a problem. Ideally you’d want one manager object that maintains a light weight version of each of these actors and can apply the data / updates via one tick call. This likely means writing a custom entity renderer and such, but it would be do-able.

I’d suggest reading up on some articles on multithreading and optimization - in general, not UE4 specific. The one recurring thing you’ll read is that just throwing more threads at a problem will more often than not give you worse performance, not better. That is definitely going to be the case with your approach of a thread per boid per tick; in fact the only reason your project hasn’t ground to a halt is that you’re using UE4’s task graph, which is not creating a new thread for each boid.

There are a bunch of things more important than taking the work off the game thread:

  • Don’t use an actor per boid. Use one per flock, and an instanced static mesh for rendering them.
  • Put all state/simulation data related to your boid into a simple struct, and store them all in an array, so all the data is adjacent in memory.
    ​- Don’t pass around arrays by value.
  • Update all your boids in a loop, from a single function.

Then, if you want to take it off the game thread, it’s easy to do. Your update function operates on the raw simulation data only so can run on any thread. You just need to have it output an array of transforms, and push/pull that to the game thread each tick where you update your components (ISM if possible) with the transform data.

​​​​​​​Add in Jocko Jonson’s suggestions too and you can get tens of thousands of them, no problem.