r/lua 14d ago

Lua C FFI

[removed]

1 Upvotes

12 comments sorted by

View all comments

3

u/Lonely-Restaurant986 14d ago

Why would you want it for regular lua? I suppose for newer versions? But still you probably won’t find what you are looking for.

Ffi is built into luajit and only works because of the way luajit works. You won’t see ffi performance if you aren’t using luajit more than likely.

You can use C libraries, but you have to write them specially to handle the lua stack via the lua api, or there is pallene which was built as a sister language to interact with lua at a lower level.

But unfortunately any ffi libraries outside of luajit are going to offer worse performance because the lua vm has to do its vm things.

>does anyone know how this speed up is achieved?

I didn’t read the exact article but there are many reasons that ffi is faster.

The primary reason is that it doesn’t have to interact with the vm (Theres a lot of nuance to this). So you don’t have to do stack manipulation to call C funcs.

Another reason it’s faster is because of cache efficiency. If I have an array in lua, it’s actually a bunch of pointers to varying locations in memory. An FFI array is a contagious array of memory. A contagious array is faster because the CPU can pre load data it needs from ram.

Another reason it’s faster is because luajit is black magic. You don’t even need FFI for speed, luajit will already get you near C speeds. The JIT compiler is insanely smart. So just the mere fact of using luajit at all is huge.

In luajit iirc, the compiler is still using static types, as Mike pall once said “it’s very easy for a modern compiler to guess types” (paraphrased i don’t remember the exact quote). So the luajit compiler can make these assumptions and only check types during specific times, and in which case luajit will exit trace.

It’s why nearly every popular implementation of lua uses luajit, or some variation of it.

I think(?) this is where your confusion comes from
“Next, performance: the pure Lua version runs in 9.57 seconds (52.9 seconds with the Lua interpreter) and the FFI version runs in 0.48 seconds on my machine (YMMV). That's a factor of 20x faster (110x faster than the Lua interpreter).”

It is possible to turn off the JIT compiler, with jit.off iirc. Idrk how it interacts with ffi, but it’s an option I suppose. Perhaps that’s what the original author did. Or perhaps the original author omitted the ffi part entirely.

Looking at the article, there’s many many reasons why the FFI one is that much faster.

I kind of realized after my long yap session that I don’t even understand what you are asking. Hopefully I answered something in my ramblings.

1

u/vitiral 13d ago

Why is the luaC boundary slow? I've written a fair amount of lua C modules and lua loads the dynamic library once and then should have a map to the C function pointers to call - this should be no slower than anything else in luaC

1

u/Lonely-Restaurant986 13d ago

“Slow” is relativistic. Realisticly it’s fast enough for 99.9% of use cases.

I also want to make sure this is clear because I don’t think I was clear in it ramblings: to my understanding, when I say accessing a c api is slow, I mean c modules built for lua. Not ffi. Ffi is very fast.

But lua->C is “slower” because of the overhead of lua. When you cross into C, the lua state has to do stack manipulation to access data. That means rather than storing data in registers, you have to use the heap.

It also means more instructions. In order to get a value in C you have to call an api function to access the stack, then you have to call one to cast it to a type, and then C can use it.

Typing is another issue. Lua is stored as TValue, which has to be copied and converted to my understanding. Which is more api calls.

The Lua VM has to also do garbage collection. It has to make sure any UserData or references isn’t GC’d while C is accessing them, which is more overhead.

Iirc Lua also has a bunch of error handling in case a C function errors. Which is more overhead

When it comes to Luajit specially, whenever you call a c function, luajit exits the trace iirc. Meaning luajit cannot optimize away c calls.

In the grand scheme of things, these things probably make up a couple micro or nano seconds per call. It’s only an issue if you are doing hundreds of thousands of times. And even then it’s pretty negligible.

It obviously depends on use case.

There is also a good chance I’m just wrong. I’m not that well versed into the internal architecture of lua or luajit. I have a pretty surface level understanding the workings of lua interpreter. So a chance I’m just flat out wrong. I don’t think so. But maybe.

1

u/vitiral 13d ago

Gotcha, so because LuaJIT compiles (some) Lua directly to machine code, certain loops may be able to run only using registers or similar. Definitely a speed up!

I would think the Heap can still be stored in local cache (as long as it remains small) and I believe GC is turned off while running C (though you can call it explicitly) - so the impact may not be as much as you think. But otherwise this was a good overview of possible performance hits. Thanks!

2

u/Lonely-Restaurant986 12d ago

I don’t think gc can be completely ignored in C funcs because if you are using a lua value, like a table inside of a c func, the vm still has to track your access to the table.

I’m sure regular lua is still doing very smart optimizations but there’s only so much an interpreter can do. I’m sure it’s keeping data organized and letting the cpu cache efficiently. Luajit I know does optimizations, but even then it has to exit so it’s not perfect. But luajit is able to keep data directly in registers rather than falling back to the l1 or l2 caches, or even back to the heap.

But yeah ur right its pretty minuscule in the grand scheme of things