Page 1 of 1

Does Line 'B' run faster than 'Line A' for anyone?

Posted: Wed Feb 02, 2022 9:04 pm
by kicknbritt

Code: Select all

local socket = require("socket")

local s = socket.gettime()

t = {}


local function g(t)
	t = math.random(1, 100)*math.random(1, 100)*math.random(1, 100)
end

for i=1, 8000000 do
	-- Line A
	--t[1] = math.random(1, 100)*math.random(1, 100)*math.random(1, 100)
	
	-- Line B
	g(t[1])
end


print(math.abs(s-socket.gettime()))
I'm trying to figure out why the 'Line B' runs faster on my machine than 'Line A"
Shouldn't the function overhead make 'Line B' slower?

Also, how do you feel about using functions inside performance critical code?
Do you tend to avoid them? Or does it not really matter to you?

Re: Does Line 'B' run faster than 'Line A' for anyone?

Posted: Wed Feb 02, 2022 9:33 pm
by GVovkiv

Code: Select all

local function g(t)
	t = math.random(1, 100)*math.random(1, 100)*math.random(1, 100)
end

for i=1, 8000000 do
	-- Line A
	--t[1] = math.random(1, 100)*math.random(1, 100)*math.random(1, 100)
	
	-- Line B
	g(t[1])
end
probably because in "Line B" you call g(), g() gets "nil" as argument, after that you perform math.random on that local "t" argument
on "Line A" you write data to global table "t", which takes more time then changing local "t" in "Line A"
if you change code to:

Code: Select all

local socket = require("socket")

local s = socket.gettime()

t = {}

local function g(t)
	t[1] = math.random(1, 100)*math.random(1, 100)*math.random(1, 100)
end

for i=1, 8000000 do
	-- Line A slower
	--t[1] = math.random(1, 100)*math.random(1, 100)*math.random(1, 100)
	
	--Line B faster
	g(t)
end

print(math.abs(s-socket.gettime()))
then you see that they do the same thing: write data to "t[1]"
and you also see that they perform in +- equal time

Re: Does Line 'B' run faster than 'Line A' for anyone?

Posted: Wed Feb 02, 2022 10:06 pm
by kicknbritt
Oh duh. I'm passing a value not a reference. I wasn't thinking about that.
So I guess we can see here that function call overhead isn't really an issue

Re: Does Line 'B' run faster than 'Line A' for anyone?

Posted: Wed Feb 02, 2022 10:22 pm
by darkfrei
Is it possible to use multiple threads for such cycles?

Re: Does Line 'B' run faster than 'Line A' for anyone?

Posted: Thu Feb 03, 2022 12:17 am
by pgimeno
LuaJIT is a tracing JIT. This means that while interpreting, when it finds a hot loop, it follows the code execution and compiles stuff as it finds it (adding guards for the case where a different path to the one compiled is taken). A consequence is that function calls are flattened, that is, they are eliminated from the compiled code and treated as if the code within the function was inlined.

Re: Does Line 'B' run faster than 'Line A' for anyone?

Posted: Sat Feb 05, 2022 12:23 pm
by kicknbritt
I was under the impression that only certain c functions were inlined.

https://stackoverflow.com/questions/524 ... the-luajit

Is this false then?

Re: Does Line 'B' run faster than 'Line A' for anyone?

Posted: Sat Feb 05, 2022 12:53 pm
by pgimeno
It used to be true; what I said applies to the case where the compiler can JIT-compile, which isn't always. But math.random is in the list of implemented functions. http://wiki.luajit.org/NYI

So, the loop above seems quite clearly compilable.

And anyway, LuaJIT 2.1 no longer aborts traces for non-compilable functions; what it does is pause the tracing, switch to interpreter mode, execute the function in interpreted mode, then switch back to tracing mode and resume, and then it may end up compiled with an on-the-fly temporary switch to interpreted mode. That's called trace stitching. As the author says (paraphrased), it's not particularly fast, but at least the trace isn't aborted. So you could say that it's no longer true in LJ 2.1.

Re: Does Line 'B' run faster than 'Line A' for anyone?

Posted: Sun Feb 06, 2022 6:34 am
by kicknbritt
Wow. The jit really is amazing.
Thanks for the update, I will be putting this to good use in my current project :)