Multithreading Path Modifiers

Hello,

My game has ~300 moving units that frequently repath through changing mazes on a grid graph. Everything works quite well, but an optimization I’d like to take care of is that path modifiers run on the main thread and cause a bit of CPU load whenever the grid is updated.

I found this thread from 2015 of someone asking something similar: Assigning a modifier in advance for MultiTargetPath

I modified Seeker.cs to run the post process modifiers from immediateCallback instead of OnPathComplete as recommended in the post, but results were unstable and it’s not clear what the fix is without having a greater understanding of astarpathfinding’s internals. I also assume the codebase has changed quite a bit since 2015, so maybe some assumptions have changed.

I do want to note that I’m currently still on version 4.2.18 of the package. Unfortunately 5.x is not compatible with Unity 2021.3 due to relying on some newer Unity features and upgrading the project to LTS 2022 introduces a 10% runtime performance drop so upgrading is not an option for me at the moment.

Do you have any recommendations for how to best accomplish parallelizing the path modifiers?

Thank you

Hi

What modifiers are you interested in?

Hey,

I’m currently using RaycastModifier. Funnel could probably also work, especially if I backport the GridStringPulling implementation out of 5.x.

As long as you don’t use the physics raycasting, then I think it should work.
Though you’ll need to tweak it so that it does not run the modifier again when the path is normally post-processed.

Thank you for the prompt reply. I tried it again, and double checked that the modifiers aren’t being called twice and that the modifier is not using physics (tried both the raycast modifier with graph raycasting and the funnel modifier) and am getting the same results. The paths appear to be getting tangled with other paths or something. I also tried adding a Claim and Release for the entire lifetime of the path in case it was maybe getting pooled early and shared, but that did not have any impact.

The pathing typically works fine without the immediateCallback usage. Here is a diff of the changes made to Seeker.cs

Can you check what happens if you disable multithreading? (A* Inspector → Settings → Threads).

The pathing behaves with multithreading disabled (but obviously tanks performance).

image

Hmm…
I believe some modifiers may use cached buffers for temporary work, which will cause issues since this is not thread-safe.
You could try wrapping your simplification logic in lock(seeker).
Though I’m not quite sure why a single seeker would process multiple paths. Maybe there are some modifiers that use global cached buffers? (Can’t recall).

Took a quick look through the implementation for RaycastModifier and FunnelModifier. RaycastModifier certainly uses shared static buffers, so it would make sense that RaycastModifier is not safe, but I didn’t see anything obviously problematic with FunnelModifier at face value. It’s curious that both have the same aberrant behavior. I’ll poke around a little more to see if I can identify where the root cause might be.

Is it fair to say that at the moment path modifiers are not officially supported for multi-threading on the version I’m on and the recommendation to use immediateCallback for them is unplanned territory (just trying to estimate effort/risk for getting this working)?

Definitely.
I’m moving away from modifiers as a concept anyway. Instead, the movement scripts do their own internal smoothing instead.

Ok, good to know. Thank you for your support!