It would be interesting to know how many projects are using regular Lua vs LuaJIT, as the two implementations have diverged.
When both were on version 5.1, there didn't seem to be any downsides to LuaJIT. Many Lua projects could benefit from it as long as they weren't trying to run on a platform that disallows JIT compilation (e.g. iOS or a game console).
However, now that LuaJIT is 2-3 language versions out-of-date, and has different best-practices (e.g. around FFI), it's almost like the two are different languages.
We chose to go with Lua on our embedded project ~2 years ago, and chose lua 5.3 with the following rational:
1. Lua embeds well on a small system and gives us a higher level language than C. lua or luajit is ok
2. Lua isn't being used for performant code. If we have performance issues use C.
3. LuaJit is looking for a maintainer and is stagnant. This isn't desirable.
Not sure if this is "best practice" but it summarized how we worked through it.
Lua 5.4 has some appealing benefits for us, we'll give it a few patch releases, but I look forward to moving to it.
Re Performance: We're actually running from 50Hz code in Lua on an old embedded CPU. We haven't re-written that in C yet since it runs just fine. We're been constantly surprised at how well it runs and works for us.
Performance-wise Lua is not too far behind LuaJIT that you should just "never" use Lua. I used Lua for a long time without problems, and in the cases where you need that extra performance you are just one "system call" away from native performance for that specific operation. I used to benchmark these things (for my use cases) for a long time, and Lua was always somewhere between 50-100% slower than LuaJIT, which is NOT a lot. It's a mistake to think that people implement Mandelbrot as a script callback. :)
It's unfortunate that LuaJIT fell behind so much, but as far as game engines who need a scripting language goes: You are either safe with Lua, or you have other alternatives: JavaScript, WASM, dynamic libraries and so on.
Nice work, man. I was doing microbenchmarks where I among other things measure the 90ns (JIT) and 120ns (Regular) function call overhead of Lua. My benchmarks was compared to my own scripting backend where the call overhead is 4ns, and it always feels a little bit like cheating when you have a gigantic headstart with each test.
I'm not surprised the JIT isn't that good, but this is crazy almost! Did you compare against the latest version of Lua?
In my microbenchmarks LuaJIT was always faster than Lua, but not so much that I wouldn't stop using Lua. In my mind once you stop developing a language it stops being interesting, because you are always looking for ways to simplify and remove whole classes of bugs.
EDIT: I just built lua5.4 from sources and ran my benchmarks again, and it's markedly worse than LuaJIT still, but it's better than before for sure. My conclusion is that Lua5.4 is the fastest PUC-Lua yet.
Some of these benchmarks are maybe not how one would do things if they were trying to go fast. An unfortunate idiom I wrote a thousand times at a company that uses Lua and tries to go fast is
local n = 0
local t = {}
-- to do an insert
n = n + 1
t[n] = blorp
-- to delegate some inserts to another function
n = insert_some_stuff(t,n)
A less messy thing you could do is
local table_insert = table.insert
but this is not as great, because lua will still do a binary search to find the end of the array each time you insert something.
Lua's weak point is around doing things with arrays, IME. It's just not very fast when you actually need something simple.
I actually started poking around at a language design over the past month that tries to leverage Lua as the compiler for something that can run in its own separate bytecode interpreter, with more of an emphasis on low level primitives. It started off as a simple adaptation of Forth ideas, but yesterday I started playing with a revision that shifts the data structure from plain old stack towards growable arrays containing three cursors(thus, "tricursor array") which can be purposed to describe bounds, destinations, insertion points, read and write, top of stack, etc. In Forth the top three values of the stack tend to get dedicated words for their manipulation because they are used quite often; this model runs with that idea plus methods I've often used for array data(text edits, sound sample loops, blotting sprites) and tries to extrapolate that into a language that can ease certain forms of array programming, while falling back on using the array as a data stack. Still just sketching it now.
That might be, but the C++ array append is bounds-checked which makes it half as fast as it could be too. It would be 16ns if there is no checking. So, you are right but I am trying to do a balanced approach here. These benchmarks are made for me, and I want to write (more or less) normal code.
I don't want to write local table_insert = ...
I'd rather drop Lua for something else then. That said, it's cool that there are things you can do to speed things up if you really have to.
Fair enough! I think t[#t+1] = x is not as offensive and will get you a few nanos less than the way with two table lookups, but if that's also not to your taste then that's fine.
PUC-Rio[1] which is the university where the original authors and current maintainers work. See "Where does Lua come from?" here: https://www.lua.org/about.html
If you use LuaJIT, you can generate bindings to C libraries, versus handwriting bindings for PUC Lua. The productivity difference is staggering. Additions to Lua since 5.1.5 have not helped me write more or better software, as much as I love Lua.
Every major release of Lua has always been a different language, and embedding in general encourages not upgrading. If you ship something based on Lua 5.2, how to handle all existing user scripts if you upgrade Lua?
I think we do have to call this a legitimate language fork at this point. Prosody (XMPP server) runs on 5.2 and does not require LuaJIT. The LuaJIT thing is more of a symptom than a cause.
When both were on version 5.1, there didn't seem to be any downsides to LuaJIT. Many Lua projects could benefit from it as long as they weren't trying to run on a platform that disallows JIT compilation (e.g. iOS or a game console).
However, now that LuaJIT is 2-3 language versions out-of-date, and has different best-practices (e.g. around FFI), it's almost like the two are different languages.