Which is faster? :GetPartBoundsInRadius() or Magnitude checking?

I am coding an interactive grass system for grass to move arround when a player walks into it, I am trying to optimize it as much as I can.

One of the optimizations involves checking how far all of the grass is from the camera, this is so it stops being simulated and grass very far away is destroyed or replaced with imposters.

In order to check this I determined that most efficient methods are using :GetPartsBoundsInRadius() without overlap params to be faster, and comparing the distance of all existing grass against the camera’s position using .Magnitude.

The issue is that I don’t know which one is faster, all I know is that technically the more items we need to loop through and check magnitude, the slower it will get, but I also don’t know how slower :GetPartBoundsInRadius() will be compared to.

3 Likes

I have widely seen Magnitude used over anything else. But honestly its more opinion than anything.

3 Likes

They are fundamentally different, so it doesn’t make sense to compare them. :GetPartBoundsInRadius() checks if the part’s bounding box is touching the imaginary sphere defined by the radius. But a magnitude check will only consider the center point of the part, i.e. its position.

4 Likes

Yeah they are different, but you can accomplish the same thing with both if you know how to. I think more so it is a usecase and most people just use magnitude…

1 Like

Magnitude is definitely faster because it’s only dealing with math. Using GetPartBoundsInRadius is a spatial query operation that does a lot of 3D stuff with special rules like OverlapParams, so it’s slower.

Also, you get the benefit of native codegen and algebra trickery for using only math (you can avoid using squareroots which is usually slow to calculate)

if pos:Dot(pos) <= radius^2 then
--is the same as
if pos.Magnitude <= radius then
--but avoids using squareroots
1 Like

I am aware they both work diferently, but keep in mind performing individual checks inside lua, can actually end up being slower than a single function that runs internally, its the reason why bulkMoveTo() can be more efficient.

Also It does makes sense to compare them because internally I doubt :GetPartBoundsInRadius() is grabbing every single bounding box’s vertices and then comparing them, or something like that, so it could end up being faster than magnitude.

2 Likes

I am unsure if thats faster due to magnitude getting an update a long time ago.

[PSA] .Magnitude Now Obliterates Squared Distance Checks in Luau (With benchmarks!) - Development Discussion - Developer Forum | Roblox

1 Like

You seem to have dodged what I said about native codegen. You can mark your function to be compiled to assembly and have it run faster than any C API. Also, a function that runs internally can still be running tons of loops that you just can’t see.

If you think about it, that’s the only way for it to determine if a box is inside a radius. It needs to check the vertices to see if they’re within range.

That post is quite a while ago and the Luau language has evolved dramatically since. Here’s my test results as of today:

If you’re still not convinced, you can run the same exact code they provided for the benchmark:
image

And about the native codegen I mentioned: all of them gets faster. I’m using the new native attribute.

3 Likes

wait wait, can you benchmark :GetPartBoundsInRadius() i am just curious as to the difference in performance now.

Also is there a way to get the distance value using the square root, or nah?

2 Likes

Nuh if you want to get the magnitude you actually… have to get the magnitude. Which means using Pythagorean theorem which involves squareroots. You can only avoid squareroots if you’re just comparing distances.

You can definitely test it yourself but if it means convincing you then

@native local function partsInRadius(parts: {BasePart}, r: number): {BasePart}
	local t: {BasePart} = {}
	local r2: number = r^2
	
	for _, p in parts do
		local pos: Vector3 = p.Position
		if pos:Dot(pos) <= r2 then
			table.insert(t, p)
		end
	end
	
	return t
end

local parts: {BasePart} = workspace.Folder:GetChildren()
local start: number = os.clock()
print(#partsInRadius(parts, 12))
print(os.clock() - start)

local start: number = os.clock()
print(#workspace:GetPartBoundsInRadius(Vector3.zero, 12))
print(os.clock() - start)
3 Likes

Thanks, you answered a question for me and the others who might stumble upon this post in the future.