How to implement something like C struct in LuaJIT

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
be-thomas
Prole
Posts: 9
Joined: Fri Apr 29, 2022 8:22 am

How to implement something like C struct in LuaJIT

Post by be-thomas »

I need something like a C struct with the following features :-
- memory aligned for fast access
- no memory wastage

I am into making a simulation which will require a LOT of Vectors.
I know lua tables does a lot of memory wastage.

So, I tried benchmarking the three effective ways of implementing such a structure :-
- lua table
- closure
- ffi

Astonishingly, I found ffi to be much slower than the rest. I heard it supposed to be drastically faster?

Here's the code I tried:

Code: Select all

local ffi = require("ffi")

function vek(a, b)
    return function(x)
        if x == 1 then return a else return b end
    end
end

function vef(a, b)
    ffi.cdef[[
        typedef struct { int64_t x, y; } vec2;
    ]]
    local vec2 = ffi.new("vec2")
    vec2.x = a
    vec2.y = b
    return vec2
end

function vec(a, b)
    return {a, b}
end


local a = vec(1, 2)
local b = vek(1, 2)
local c = vef(1, 2)

print(a[1], a[2])
print(b(1), b(2))
print(c.x, c.y)

-- benchmark tables
local s = os.clock()
local sum = 0
for i = 1, 999999999 do
    sum = sum + (a[1] * a[1] + a[2] * a[2])
end
print(sum, ", lua tables: ", os.clock() - s)

-- benchmark closures
local s1 = os.clock()
local sum1 = 0
for i = 1, 999999999 do
    sum1 = sum1 + (b(1) * b(1) + b(2) * b(2))
end
print(sum1, ", closures: ", os.clock() - s1)

-- benchmark ffi
local sum2 = ffi.cast("int64_t", 0)
local s2 = os.clock()
for i = 1, 999999999 do
    sum2 = sum2 + (c.x * c.x + c.y * c.y)
end
print(sum2, ", ffi struct : ", os.clock() - s1)
Here's the output I get :-

Code: Select all

1       2
1       2
1LL     2LL
4999999995      , lua tables:   0.707674
4999999995      , closures:     0.708508
4999999995LL    , ffi struct :  0.946042
Can someone explain me the reason for such a drastic slowness for FFI ?
Andlac028
Party member
Posts: 174
Joined: Fri Dec 14, 2018 2:27 pm
Location: Slovakia

Re: How to implement something like C struct in LuaJIT

Post by Andlac028 »

be-thomas wrote: Tue Jan 31, 2023 1:19 pm

Code: Select all

local ffi = require("ffi")

function vek(a, b)
    return function(x)
        if x == 1 then return a else return b end
    end
end

function vef(a, b)
    ffi.cdef[[
        typedef struct { int64_t x, y; } vec2;
    ]]
    local vec2 = ffi.new("vec2")
    vec2.x = a
    vec2.y = b
    return vec2
end

function vec(a, b)
    return {a, b}
end


local a = vec(1, 2)
local b = vek(1, 2)
local c = vef(1, 2)

print(a[1], a[2])
print(b(1), b(2))
print(c.x, c.y)

-- benchmark tables
local s = os.clock()
local sum = 0
for i = 1, 999999999 do
    sum = sum + (a[1] * a[1] + a[2] * a[2])
end
print(sum, ", lua tables: ", os.clock() - s)

-- benchmark closures
local s1 = os.clock()
local sum1 = 0
for i = 1, 999999999 do
    sum1 = sum1 + (b(1) * b(1) + b(2) * b(2))
end
print(sum1, ", closures: ", os.clock() - s1)

-- benchmark ffi
local sum2 = ffi.cast("int64_t", 0)
local s2 = os.clock()
for i = 1, 999999999 do
    sum2 = sum2 + (c.x * c.x + c.y * c.y)
end
print(sum2, ", ffi struct : ", os.clock() - s1)
Actually, there is typo in your code in measuring performance - in ffi, you compare current time with time at the start of closures benchmark, so it take closures time + some time (you use s1 instead of s2), so ffi is the fastest (~0.237534 if substracted ffi from closure time).

Also you may want to use double instead of int64_t, if you want to work on real numbers, not only on integers.
RNavega
Party member
Posts: 385
Joined: Sun Aug 16, 2020 1:28 pm

Re: How to implement something like C struct in LuaJIT

Post by RNavega »

Besides the nice catch from Andlac028, does it make a difference if you replace the FFI struct with an array? Using it like in the table benchmark, by indexing it.
be-thomas
Prole
Posts: 9
Joined: Fri Apr 29, 2022 8:22 am

Re: How to implement something like C struct in LuaJIT

Post by be-thomas »

Thanks @Andlac028 FFI seems to be really fast.
I tried two more scenarios.

I tried with arrays & the performance difference was pretty much on-par with struct, here's the arrays output :-

Code: Select all

1       2
1       2
1       2
4999999995      , lua tables:   0.714686
4999999995      , closures:     0.714767
4999999995LL    , ffi array :   0.239937
One interesting thing I found, is turning jit off makes the FFI significantly slower.
Here's my benchmark code with jit off :-

Code: Select all

jit.off()
local ffi = require("ffi")

function vek(a, b)
    return function(x)
        if x == 1 then return a else return b end
    end
end

function vef(a, b)
    return ffi.new("double[2]", a, b)
end

function vec(a, b)
    return {a, b}
end

local a = vec(1, 2)
local b = vek(1, 2)
local c = vef(1, 2)

print(a[1], a[2])
print(b(1), b(2))
print(c[0], c[1])

-- benchmark tables
local s = os.clock()
local sum = 0
for i = 1, 9999999 do
    sum = sum + (a[1] * a[1] + a[2] * a[2])
end
print(sum, ", lua tables: ", os.clock() - s)

-- benchmark closures
local s1 = os.clock()
local sum1 = 0
for i = 1, 9999999 do
    sum1 = sum1 + (b(1) * b(1) + b(2) * b(2))
end
print(sum1, ", closures: ", os.clock() - s1)

-- benchmark ffi
local sum2 = ffi.cast("double", 0)
local s2 = os.clock()
for i = 1, 9999999 do
    sum2 = sum2 + (c[0] * c[0] + c[1] * c[1])
end
print(sum2, ", ffi array : ", os.clock() - s2)


Here's the output of the same thing with jit off (and for loop goes upto 9999999 only, as interpreter is slow) :-

Code: Select all

1       2
1       2
1       2
49999995        , lua tables:   0.121602
49999995        , closures:     0.399438
49999995LL      , ffi array :   2.422952
Well, if someone was building games for iOS too, then they better have alternative implementation which don't rely on FFI.
As we know JIT is not allowed in iOS apps.

Btw, thanks for helping me out with this benchmark.
User avatar
zorg
Party member
Posts: 3468
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: How to implement something like C struct in LuaJIT

Post by zorg »

JIT's also off on Android afaik due to bugs instead of it not being allowed, so for any mobile platform you probably don't want to use FFI due to the performance decrease.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
be-thomas
Prole
Posts: 9
Joined: Fri Apr 29, 2022 8:22 am

Re: How to implement something like C struct in LuaJIT

Post by be-thomas »

The speed & simplicity had been the main selling point for Love2D.
Now if nothing is done about it, there is Raylib as a very competitive alternative. Now it looks all the more lucrative especially with JIT off.
RNavega
Party member
Posts: 385
Joined: Sun Aug 16, 2020 1:28 pm

Re: How to implement something like C struct in LuaJIT

Post by RNavega »

I think few will be able to match Löve's high productivity, maybe Godot or Unity.

I don't know the extent of these LuaJIT bugs being mentioned. In that case maybe having Löve as a static library that you can use with your Lua code, after it's transpiled to like C, to build native apps (edit: AFAIK none of these things exist, it's just wishful thinking).
Post Reply

Who is online

Users browsing this forum: No registered users and 4 guests