Page 1 of 1

How to implement something like C struct in LuaJIT

Posted: Tue Jan 31, 2023 1:19 pm
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 ?

Re: How to implement something like C struct in LuaJIT

Posted: Tue Jan 31, 2023 2:15 pm
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.

Re: How to implement something like C struct in LuaJIT

Posted: Tue Jan 31, 2023 2:34 pm
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.

Re: How to implement something like C struct in LuaJIT

Posted: Tue Jan 31, 2023 4:59 pm
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.

Re: How to implement something like C struct in LuaJIT

Posted: Fri Feb 03, 2023 12:50 pm
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.

Re: How to implement something like C struct in LuaJIT

Posted: Sat Feb 04, 2023 8:11 am
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.

Re: How to implement something like C struct in LuaJIT

Posted: Sat Feb 04, 2023 9:51 am
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).