A short and straight-forward question.
How would you calculate the magnitude of a vector when nano-seconds matter?
Normally using Vector3.Magnitude is sufficient for most things in a game.
The magnitude of a vector is calculated like this.
function get_distance(vec)
local distance = (vec.X * vec.X) + (vec.Y * vec.Y) + (vec.Z * vec.Z) -- Okay, but gives you a huge number.
return math.sqrt(distance) -- This is the slowest part.
end
The local distannce line is where we calculate the distance of a vector.
But this of course will give you a HUUUGE number that can skyrocket into the thousands.
For things like a zombie AI that compares distances between players/humans, you can just discard the math.sqrt() since we don’t need that to compare which distance is closest.
Good optimization generally.
But what if we DO need math.sqrt()?
That’s the problem, this is Lua, not C++.
Most approximation algorithms and things like Quake’s “Fast Inverse Square Root” don’t work in Lua.
Now I CAN accept some losses, I do not mind if the approximate distance is a few decimals off.
I even accept single-decimal precision or whole integer precision
(since we can also just multiply vectors before getting distance and then divide to get more decimal places).
I just want to find a way to get approximate distances, a distance that allows you to draw a line between two points.
Maybe we don’t need to look for a better square root algorithm, perhaps we just need a whole different magnitude function.
when a vector3 is created or modified its magnitude is already calculated and stored as .Magnitude, unless you are storing the vector3s X, Y and Z values in a table.
if you only need to check if something is within a range, all you have to do is compare the square distance to the square magnitude.
but like i said above, the magnitude is automatically calculated when a vector is created/changed, so doing X^2 + Y^2 + Z^2 < distance^2 is slower than doing .Magnitude < distance, because you are doing more operations which is squaring and adding numbers and then comparing them, as opposed to just comparing them
Vector3.Magnitude is actually only calculated when you read from it if I remember correctly.
Otherwise it would simply do a lot of needless computations if you never use the .Magnitude property.
I already know I can just take out the square root equation for comparing distances.
I already use that, mainly for AI-related stuff.
But I need a distance that I can draw a line between.
I’m looking through the internet just to see if there is some kind of magical equation or ultra-fast algorithm that can give approximate distance.
Vector3.Magnitude is actually only calculated when you read from it
thats stupid. that is like saying a variable such as a = 2 + 2 is being calculated every single time you access it, in reality it is calculated once and the result is stored before the code runs. if it was a :GetMagnitude() function then yes, it would run and be calculated each time, but its not a function, its a table entry. Vector3s are nothing but tables with locked metatables.
Are you actually being impacted by this? I have never heard of anyone having an issue with square root performance on any modern computers. Fun fact: Loading data from RAM can have a 10x higher perf hit than square root calc.
I am actually going to check this, that seems weird.
From a game engine design standpoint it seems silly to calculate properties that aren’t being used.
Yes, generally if you type a = 2 + 2 that WILL get calculated because the program actually expects you to use the variable later.
I’ve never heard of Vector.Magnitude being calculated before being read though.
If Vector.Magnitude was calculated even though it is never read, simply adding a few vectors together like this…
local vec = Vector3.new(10, 50, -28) + Vector3.new(5, -80, 3)
Would run THIS each time you added/subtracted/multiplied vectors.
function Magnitude(vec)
return math.sqrt( (vec.X ^ 2) + (vec.Y ^ 2) + (vec.Z ^ 2) )
end
Of course this would be written in C++ in the game engine’s source code likely but that’s still a lot of math and instructions for a property that doesn’t even always get read.
Just setting a object’s position would be several times slower.
But good, I think this can be checked by seeing if code takes longer to execute if the magnitude property is accessed.
If magnitude is calculated even while it’s unused or never read/accessed by any scripts, we shouldn’t see a difference in speed.
I intend to write a library with re-usable and optimized math functions, it forms a foundation for multiple projects.
And I’m generally the kind of person who like to simulate 1000 NPCs at once and have a few bizzillion projectiles simulated that ricochet around and have long life times.
No, technically I’m not impacted by it directly since I run quite high-end hardware.
However, I like to optimize my games for low-end hardware with only 2 CPU cores.
I hold myself to a high standard of optimization and try to make it an essential part of development so I don’t miss a critical point.
Yes, I’m well aware of the dangers and risks of optimizations, but I generally know what I want to achieve and keep code organized in re-usable modules for efficiency and to make things less confusing later.
now that i think about it, it does actually make more sense if it was calculated the first time it was accessed and then cached, but it shouldnt recalculate it every single time it is accessed
Trying to optimize around a vector’s magnitude isn’t really worth it. IMO your real performance bottlenecks will probably always be elsewhere. I do believe vec.Magnitude caches after first access. As always, use the profiler to figure out what parts of your code are hotspots and focus on those. Then you’ll know what you need to benchmark.
I’m running a billiards simulation at 6000 hz (e.g. 6k physics steps per second of playback), utilizing Vector3s everywhere. I’m able to calculate the whole simulation quite fast. There are multiple magnitude checks being done in this process. The optimizations I’ve made to ensure the speed of this process: (1) Utilize --!native directive at the top of the script (only works server-side), (2) optimize for CPU cache hits; memory locality, (3) Utilize the microprofiler often to identify hotspots. In all of this, magnitude operations have never bubbled up as performance issues.
Testing that out, the vector library IS actually faster - about 2.5x!
Code to test
local t0 = tick()
for i = 1, 100000000 do -- A
local m = vector.magnitude(Vector3.new(5,5,5))
end
local t1 = tick()
for i = 1, 100000000 do -- B
local m = Vector3.new(5,5,5).Magnitude
end
local t2 = tick()
local a = t1-t0
local b = t2-t1
print("Time for A:", a)
print("Time for B:", b)
warn(`A is {b/a}x times faster that B`)
(Note that both are quite fast - I had to have the code run 100 million times in order for it to give a good reading)
When comparing relative distances, the actual benefit of disregarding the sqrt, compared to using Vector3.Magnitude, is that it is about 1.3x faster.
Code to test
local t0 = tick()
for i = 1, 100000000 do -- A
local v = Vector3.new(i, i, i)
local s = v.X^2 + v.Y^2 + v.Z^2
end
local t1 = tick()
for i = 1, 100000000 do -- B
local v = Vector3.new(i, i, i)
local s = v.Magnitude
end
local t2 = tick()
local a = t1-t0
local b = t2-t1
print("Time for A:", a)
print("Time for B:", b)
warn(`A is {b/a}x times faster that B`)
However, if you instead do vector.magnitude(VECTOR), then it’s actually faster to just use the vector library instead of squaring all the values and adding them.
Code to test
local t0 = tick()
for i = 1, 100000000 do -- A
local v = Vector3.new(i, i, i)
local s = v.X^2 + v.Y^2 + v.Z^2
end
local t1 = tick()
for i = 1, 100000000 do -- B
local v = Vector3.new(i, i, i)
local s = vector.magnitude(v)
end
local t2 = tick()
local a = t1-t0
local b = t2-t1
print("Time for A:", a)
print("Time for B:", b)
warn(`B is {a/b}x times faster that A`)
The Luau source code is a typical indication of how Roblox handles things. There could be subtle differences in the engine Vs. the VM, but it’s safe to assume otherwise. Vector magnitude is calculated as you would, with no overall fancy algorithm to shorthand the calculation. Since the vector library invokes C++ code, its not implausible for it to yield better runtime results.
Modern CPUs have long since overcome the overhead of square-rooting, so this isn’t something you should think too hard about
This is a thing you can do in Luau?
Quite curious how that works.
I thought it was mostly only possible to do high-level optimization in Luau.
But seeing all the posts made here, it could very well be that Roblox already uses fast magnitude functions.
Modern machines don’t struggle with performance much nowadays, although I still try to find ways to just make something run as fast as possible for players using older machines.
I don’t know whether Roblox uses some kind of optimization in calculating the magnitude of a vector, but I’m pretty sure that both Vector3 and Vector2 use single precision floating point numbers (32 bits per number). I have seen this mentioned about Vector3s somewhere and I think it applies to Vector2 as well. Here’s an example that shows that when you store a number that can be represented exactly as a 64-bit float in a Vector2 and then fetch it back from the vector, you don’t necessarily get the same number back which indicates that Vector2 doesn’t store the number as a 64-bit float.
As far as I can tell, the magnitude function is basically a variable. It is like calling the position of a part or checking the boolean of the CanCollide property. This means that the calculation for this function is run whenever the value changes (you can check in the properties of a part during a live test to fact check me, I may or not be wrong). This would mean that running additional calculations to find the same result as pre-existing calculations already being run would basically double the computing power already being used. While this is not a significant amount of computing power being used, not making your own function to find the magnitude would be better for low-end devices as you are not doubling the amount of math the device is already doing.
In summary, Roblox runs the Vector.Magnitude function whether you want it to or not, which means running your own Vector.Magnitude function makes a computer have to do two calculations instead of one. This would be less optimized than just using Roblox’s pre-existing feature.
As far as I know, some data properties are only calculated when read.
It would already be a waste of computing power to compute the magnitude of a vector if not a single script in-game even reads it.
It just wouldn’t make sense from a game engine’s design stand-point.
But it could be that Roblox already uses some kind of fast-square-root alfgorithm, so far as I know there’s no Lua-based algorithm that offers a faster square root or anything.
Sleitnick already pointed this out, but I think that, unless you already fully exhausted everything else there is to optimize, optimizing the calculation of the magnitude of a vector is something you shouldn’t put as the first priority. Most likely other things would be holding you back in terms of performance.
I have no benchmarks to back it up, but I’m pretty sure something as simple as creating an instance and setting some of its properties would be many times slower than simply calculating a vector magnitude, albeit still in the realm of nanoseconds.