DistToSqr vs Distance

We all know that Vector.DistToSqr is faster than Vector.Distance right? It’s even noted on the wiki.

Honestly I haven’t liked using DistToSqr when doing distance checks because the numbers I have to work with are somewhat annoying,
not a very good reason to not use it especially in a piece of code that was for example, used for rendering or in a loop that is called often.

I did some testing and found that DistToSqr is only very slightly faster. Using the following code:
[lua]
local function profileAverage(taskItr, task, averageItr)
local mean = 0;

for j = 1, averageItr do
	local start = SysTime();
	for i = 1, taskItr  do
		task(i);
	end
	mean = mean + (SysTime() - start);
end

return mean / averageItr;

end

concommand.Add(“profile”, function(ply, cmd, args)
local itr = 1000 * 100;
local avg = 10;

print("disttosqr: " .. profileAverage(itr, function(i)
	-- assign global so that this can't be ignored as a useless operation to perform
	-- maybe jit does this i'm not sure
	dist = Vector(i, i, i):Distance(Vector(-i, -i, -i))
end, avg));

print("distance: " .. profileAverage(itr, function(i)
	dist = Vector(i, i, i):DistToSqr(Vector(-i, -i, -i))
end, avg));

print("sqrdist custom: " .. profileAverage(itr, function(i)
	local a = Vector(i, i, i);
	local b = Vector(-i, -i, -i);
	dist = (a.x - b.x) + (a.y - b.y) + (a.z - b.z);
end, avg));

end);
[/lua]

I got these results:



disttosqr: 0.11867651725609
distance: 0.11770560799648
sqrdist custom: 0.23249257654688


Which means a single call to DistToSqr over a call to Distance is (on average) 100ns (0.0001milliseconds) faster. Is this a relevant number anywhere?

To put the negligibility of this number into perspective, I wanted to use a function like this to simplify my life:
[lua]
local meta = FindMetaTable(“Vector”);
function meta:DistLessThan(b, dist)
return self:DistToSqr(b) < (dist ^ 2);
end
[/lua]

But the difference between using this and not using a function (as a calling a function will obviously impact things slightly) is 11 times worse than
calling Distance over DistToSqr.

It might be a micro-optimization but I guess it could add up for things like distance checking all entities on the map to a player. Now granted even with like 500 entities on a map that would come out to less than a millisecond.

One thing I’ve never thought of is the [in]efficiency of calling Lua functions. Have you done any benchmarking of your DistLessThan metamethod versus just inlining the body? I always hear how inefficient Lua functions are to call but I’ve never seen any statistics. I presume since everything is JIT compiled it wouldn’t be any slower than calling a function in C. And I don’t see everyone defining their C functions as inline to ‘optimize’.

Still slower than using the metatable:



disttosqr: 0.11709539255319
disttosqr local: 0.12383633548877
distlessthan meta: 0.16619708595851
distlessthan local: 0.12311359309911
distlessthan double local: 0.12113224030436


[lua]
local meta = FindMetaTable(“Vector”);
function meta:DistLessThan(b, dist)
return self:DistToSqr(b) < (dist ^ 2);
end

local function distlessthan(a, b, dist)
return a:DistToSqr(b) < (dist ^ 2);
end

local disttosqr = meta.DistToSqr;
local function distlessthan2(a, b, dist)
return disttosqr(a, b) < (dist ^ 2);
end

concommand.Add(“profile”, function(ply, cmd, args)
local itr = 1000 * 100;
local avg = 10;

print("disttosqr: " .. profileAverage(itr, function(i)
	-- assign global so that this can't be ignored as a useless operation to perform
	-- maybe jit does this i'm not sure
	dist = Vector(i, i, i):DistToSqr(Vector(-i, -i, -i)) &lt; 50;
end, avg));

print("disttosqr local: " .. profileAverage(itr, function(i)
	-- assign global so that this can't be ignored as a useless operation to perform
	-- maybe jit does this i'm not sure
	dist = disttosqr(Vector(i, i, i), Vector(-i, -i, -i)) &lt; 50;
end, avg));

print("distlessthan meta: " .. profileAverage(itr, function(i)
	dist = Vector(i, i, i):DistLessThan (Vector(-i, -i, -i), 50)
end, avg));

print("distlessthan local: " .. profileAverage(itr, function(i)
	dist = distlessthan(Vector(i, i, i), Vector(-i, -i, -i), 50);
end, avg));

print("distlessthan double local: " .. profileAverage(itr, function(i)
	dist = distlessthan2(Vector(i, i, i), Vector(-i, -i, -i), 50);
end, avg));

end);
[/lua]

Actually all of the math functions in source are inline. Inline functions is almost always faster than a normal call, but it will result in larger binary sizes - so in that case there’s a trade-off.