Page 1 of 2

math.random > love.math.random

Posted: Thu Oct 26, 2017 4:36 pm
by KayleMaster
I did some test while optimizing a problematic loop and these are my findings:
2621440 tests
math.random ~25ms
love.math.random ~35 ms
Is this because of the extra access table (love.) ?
Just something interesting I found.

Re: math.random > love.math.random

Posted: Thu Oct 26, 2017 5:00 pm
by Nixola
It may be, although a 40% increase in time looks excessive. Can you try making both functions local when testing?

Re: math.random > love.math.random

Posted: Thu Oct 26, 2017 6:54 pm
by KayleMaster
They are both local, in fact, it's just one function, I just change math.random to love.math.random in the function and compile again. Did this 10 times and got consistent results.
Anyways, it's not that noticeable, and no, It wasn't the problem of my loop ( I wish it was ).

Re: math.random > love.math.random

Posted: Thu Oct 26, 2017 6:54 pm
by 0x25a0
As far as I know the pseudo-random number generator in LOVE is not based on Lua's PRNG. So, the performance difference is not because of an extra table access, it's because they are inherently different PRNGs :)

Re: math.random > love.math.random

Posted: Thu Oct 26, 2017 7:00 pm
by Nixola
Yeah, LÖVE's is indeed slower. I just thought it was faster for some reason.

Re: math.random > love.math.random

Posted: Thu Oct 26, 2017 8:40 pm
by zorg
It's better though, Löve's implementation, i mean.

Re: math.random > love.math.random

Posted: Thu Oct 26, 2017 9:03 pm
by Nixola
Yeah, that's a given; it's also consistent across all platforms and Lua interpreters (while LuaJIT packs a consistent PRNG, LÖVE can also use standard Lua).

Re: math.random > love.math.random

Posted: Fri Oct 27, 2017 7:47 am
by erasio
Plus a larger period and actual randomness without staying by setting a seed and calling random 3 times.

I mean math.random is just weird. Functional when you follow the docs. But I wouldn't recommend using it.

Plus love has random generator objects allowing you to have random generators assigned to parts of your game for actually consistent seed based randomness.

Instead of just having one which may play into combat, map generation and more meaning the order of random calls will affect the result and make debugging a pain.

Re: math.random > love.math.random

Posted: Fri Oct 27, 2017 7:52 am
by KayleMaster
Yeah but I just need random tile gen (different shades) so it'd do fine for me.

Re: math.random > love.math.random

Posted: Sat Oct 28, 2017 8:56 am
by vrld
I was curious, so I did my own test. I also computed the variation in run time to get a sense if the difference is (statistically) significant. This is the code I used:

Code: Select all

-- compute mean and standard deviation from sample
local function mean_std(t)
  assert(#t >= 2)
  local mean, scatter = 0, 0
  for n,x in ipairs(t) do
    local d = x - mean
    mean = mean + d/n
    scatter = scatter + d * (x - mean)
  end
  return mean, math.sqrt(scatter / (#t - 1))
end

-- take N samples of K runs of f().
local function profile(f, N, K)
  local t = {}
  for n = 1,N or 10000 do
    local t0 = love.timer.getTime()
    for k = 1,K or 100 do
      f()
    end
    t[#t+1] = (love.timer.getTime() - t0) / (K or 100)
  end
  return t
end

-- first run to warm up the JIT compiler
mean_std(profile(math.random))
mean_std(profile(love.math.random))

-- actual measurement run
m1,s1 = mean_std(profile(math.random), 1000000, 10000)
m2,s2 = mean_std(profile(love.math.random), 1000000, 10000)

local ns = 1e9 -- one second is 1000000000 nanoseconds
print("libc:", ("%.3f ns ± %.3f ns"):format(m1*ns, s1*ns))
print("love:", ("%.3f ns ± %.3f ns"):format(m2*ns, s2*ns))

love.event.quit()
And this is one result (you may get other numbers, even across runs):

Code: Select all

libc:	8.958 ns ± 2.154 ns
love:	14.195 ns ± 2.300 ns
What does that tell us? Well, your numbers are most probably correct: 2621440 runs take, on average, 25ms for math.random() or 35ms for love.math.random(). The difference also seems to be significant.
But: One evaluation of either function takes less than 20 nanoseconds. Nanoseconds! Given the benefits of love's PRNG (that is: consistency over OSes and better statistical properties), it seems as if this is not the right place to start optimizing your code.