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

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
kicknbritt
Citizen
Posts: 98
Joined: Sat May 30, 2015 2:15 am
Location: Chicago, IL/Anchorage,AK

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

Post 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?
"I AM THE ARBITER!!!" *Pulls out Energy sword and kills everything*
User avatar
GVovkiv
Party member
Posts: 686
Joined: Fri Jan 15, 2021 7:29 am

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

Post 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
User avatar
kicknbritt
Citizen
Posts: 98
Joined: Sat May 30, 2015 2:15 am
Location: Chicago, IL/Anchorage,AK

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

Post 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
"I AM THE ARBITER!!!" *Pulls out Energy sword and kills everything*
User avatar
darkfrei
Party member
Posts: 1204
Joined: Sat Feb 08, 2020 11:09 pm

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

Post by darkfrei »

Is it possible to use multiple threads for such cycles?
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
pgimeno
Party member
Posts: 3672
Joined: Sun Oct 18, 2015 2:58 pm

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

Post 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.
User avatar
kicknbritt
Citizen
Posts: 98
Joined: Sat May 30, 2015 2:15 am
Location: Chicago, IL/Anchorage,AK

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

Post 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?
"I AM THE ARBITER!!!" *Pulls out Energy sword and kills everything*
User avatar
pgimeno
Party member
Posts: 3672
Joined: Sun Oct 18, 2015 2:58 pm

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

Post 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.
User avatar
kicknbritt
Citizen
Posts: 98
Joined: Sat May 30, 2015 2:15 am
Location: Chicago, IL/Anchorage,AK

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

Post by kicknbritt »

Wow. The jit really is amazing.
Thanks for the update, I will be putting this to good use in my current project :)
"I AM THE ARBITER!!!" *Pulls out Energy sword and kills everything*
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 11 guests