Optimization help regarding a custom pathfinding script thing

You can give “tasks” to workers in actors that calculate the values, again it’s another rather niche scripting feature that requires extensive technical knowledge though.

Could you try some more basic things like reducing the number of iterations, and adding task.wait to prevent prolonged hanging?

i could reduce the number of iterations but when i use wait the lag disapears but the path generation becomes snails paced :confused:
Also ive tried parralel luau but never fully understood how to properly do it but im fine to learn it!

You can reduce how often it’s throttling, and sorry I can’t really help a whole lot, since I haven’t really dived into custom pathfinding before.

1 Like

heres my basic test:

-- Manual caching benchmark
local sqrt = math.rad
local t1 = os.clock()
for i = 1, 1e7 do
	local x = sqrt(i)
end
local t2 = os.clock()
print("Manual caching time:", t2 - t1)

-- Global access benchmark
local t3 = os.clock()
for i = 1, 1e7 do
	local x = math.rad(i)
end
local t4 = os.clock()
print("Global access time:", t4 - t3)

--[[ Without optimize 2

	math.clamp:
	
	Manual caching time: 0.10730649996548891 (0.1073) ✅
	Global access time: 0.10778090002713725 (0.1077) ❌
	
	##############################

	math.sqrt:
	
	Manual caching time: 0.06010770000284538 (0.0601) ✅
	Global access time: 0.06233300000894815 (0.0623) ❌

	##############################
	
	math.deg:

	Manual caching time: 0.058390900027006865 (0.0583) ❌
	Global access time: 0.05750799999805167 (0.0575) ✅
	
	##############################
	
	math.rad:

	Manual caching time: 0.05750240001361817 (0.0575) ❌
	Global access time: 0.056873699999414384 (0.0568) ✅
]]

--[[ With optimize 2

	math.clamp:
	
	Manual caching time: 0.1071595000103116 (0.1071) ✅
	Global access time: 0.10978739999700338 (0.1097) ❌
	
	##############################

	math.sqrt:
	
	Manual caching time: 0.05766210000729188 (0.0576) ✅
	Global access time: 0.05807460000505671 (0.0580) ❌

	##############################
	
	math.deg:

	Manual caching time: 0.05880890000844374 (0.0588) ❌
	Global access time: 0.058404099952895194 (0.0584) ✅
	
	##############################
	
	math.rad:

	Manual caching time: 0.05668149999110028 (0.0566) ❌
	Global access time: 0.056579000025521964 (0.0565) ✅
]]

Seems like some do better than others, I’m lazy and only grabbed the average from 3 runs for each so keep that in mind.

I included the benchmark part itself so you or anyone else can run it with more samples, who knows the time may change

2 Likes

I think all the results are pretty much in the margin of error of each other.

1 Like

The pathfinding algorithm im using JPS is the most optimized one i chould find sense it maximizes bassicaly everything that a* is slow at plus i only use the cost from node to goal sense it makes the algorithm go straight for the goal and ignores accuracy

There’s a lot to unpack, but here are the two places I think might be hotspots for performance.

local RAY = workspace:Raycast(prev, D, Params)-- ray
if pcall(function() local a = RAY.Instance end) then

This could be replaced with a simple if RAY then. The closure being created here is probably a major bottleneck.

local function GCNgp(From:Vector3, PF_children:table)-- returns Grid_Location of a node from given world position
	local temp, index, max = {}, 0, 9999
	for _, child in ipairs(PF_children) do
		local dist = (child.Position - From).Magnitude
		if dist < max then
			max = dist
			index += 1
			temp[index] = child
		end
	end
	return temp[index]:GetAttribute("Grid_Location")
end

The temp table is useless here since you’ll always return the last value. Just keep a result variable and set it to next largest child in the loop.

local result, max = Children[1], 9999

for _, child in ipairs(PF_children) do
	local dist = (child.Position - From).Magnitude
	
	if dist > max then
		max, result = dist, child
	end
end

return result:GetAttribute("Grid_Location")

should dist be less then max?
Also ima go to sleep goodnight!

And im back now and i didnt think of just using a global and not an array nice!

You should look into using the built in MicroProfiler! It allows you to see what exactly is happening every frame.

I dont completely understand your code from reading it since im not familiar with the algorithm, but I theres a few glaring faults:

  • Using instances is slow in bulk because of the LuaBridge! You should cache instance properties where applicable. This is the best topic I could find but I suggest starching further Roblox LuaBridge - Where the magic lies within when talking to the DataModel!

  • Attributes are especially slow from my experience. Look for alternatives, like a purely data based alternative

  • Using .Magnitude is slow because of sqrt. If you dont need the exact distance, there are alternatives that should still give mostly good results. This is something you should only be mindful of if its being done in bulk

The results are only so close to one-another because they compile to identical code. In the absence of an optimize flag, scripts ran in Studio are compiled with optimization level 1. If you ran them with 0 optimization, manually caching the function WOULD be significantly faster because the compiler wouldn’t be caching it for you.

Again, write clean code - not clever code.

1 Like

Huh ive never thought of using non part based data for that.
I will try that out!

Hang on should i be using object orientated stuff for the node class or is what im using fine?

You dont need OOP and i wouldnt recommend it for this. If anything its just slower (by a negligible amount mind you)

Oh, okay, also
Holy moly, what a performance save!
Previously it needed 17 seconds of brutal lag to generate a path. But now
It does it in only 3 seconds of slight lag—nice!


Also, that unintentionally made the separate grid generator work 10 times faster, lol.

If you are using A-star pathfinding, you should be able to recall it from finish to start instead of start to finish, that the target is the player and the player is the target. Also the longer the path is the lower performance you get

im using Jump Point Search but yes i will recall from finish after the player moves some distance from where it was originally standing