Lua and Luajit Best Practices?

The following thread is intended to collect all the good ways to use Lua/Luajit within Defold.
Some of the information will include:

  • General Lua performance with respect to language use
  • How to tweak luajit to perform that little bit better
  • FFI specific performance improvements (for those targeting platform external libs)
  • Interop performance notes for native libraries.
  • Defold specific optimizations

Hopefully this could be a useful reference of common links and information that people can add to over time.

Initial References
Defold Optimisation Page

Lua Classic Tips:

Luajit Performance Tips:

Luajit Performance Tips (from Mike Pall):


Luajit projects useful for learning interop:
UFO Excellent examples of luajit ffi

Lua for Windows:
A huge array of interop libraries for windows. You can make almost any application using these.

These projects provide a huge array of samples and libraries for people to learn Lua and Luajit with. Highly recommended to new Lua users.


Some quick tips for lua performance:
Always create local references to methods within tables if they are going to be called many times.
The example here is using tables. Put this at the top of your lua file, and this will improve perf if you are operating on tables many times in a frame.

local tinsert = table.insert
local tremove = table.remove 
local tconcat = table.concat

You can do this with any module or lua script table you are using.


Another classic perf tip:
Always try to use ipairs if you intend to do a large amount of iteration within the main game frame at runtime.

-- Do this
for i,v in ipairs(mytablelist) do ... something .. end 
-- Try not to do this
for k,v in pairs(mytablelist) do ... something .. end 

If you need to use key tables, try to use them for lookups and not for iteration.


Another lua handy tip. This is not performance related, but it is a look into the wonderful world of metatables and metamethods :wink:
Often you want to get a size of a table. You can iterate the table, and if you use insert and remove then table.getn will work most of the time. But it can be frustrating because using a table[key] = value can break getn.
What to do? Metatables!!! This is a little OO like but heres an example of a table with some metamethods that help solve the above problem.

local myobject = {}
local mt = {
  __newindex = function (tbl, key, value) 
       if(key == "count") then return end -- dont allow count modification!
       if(tbl[key] == nil) then tbl.count = (tbl.count or 0) + 1 end
       if(value == nil and tbl[key]) then tbl.count = tbl.count -1 end
       tbl[key] = value 
setmetatable(myobject, mt) 

Now when you use myobject[key]=value the myobject will have a count properties added that shows how many indexes are added and removed using the newindex ([]) method.
When combined with other functions and metamethods you can do some really nice things to make managing tables much easier and more intuitive to the developer.


FFI is cool :slight_smile: and it is very fast - C fast. But it is also has a bunch of baggage. Here’s some quick tips and code snippets using ffi, and how you can leverage it.

The Bad
FFI bypasses the normal C calling convention in Lua to call C methods. This means when you call an FFI C method you can jump into handling pointers and addresses directly. This means crashing your application (and even Defold) is a real possibility. Beware!

FFI is platform specific. When you call into the lower levels, you are calling that platforms specific methods that are compiled for that platform (OS/Hardware). This means if you need cross platform you will need to make FFI mappings for each.

Note: FFI will not work on html5 (or I dont think it will - wasm might blow up). FFI should work on the other target platforms though.

The Good
Because FFI lets you call native C methods it is insanely quick. And the Luajit system treats it like directly calling a C function. This means you get the great benefits of the jit prediction systems and the speed of running something at maximum perf on a machine.

It is horribly easy to use. One of the best ways to be able to interface with Lua and very easy to write for - just write a C dll/so and call it :slight_smile:

This all sounds interesting Dave, what is it, and how can I use it.
Heres a quick example. We want to call the OS level malloc to make a huge amount of memory (which we cant always do in Lua), and we want to put stuff in it.

local ffi = package.preload.ffi()  -- In Defold this is a little different. Normaly you use: local ffi = require("ffi")
-- Define the methods you want to use (these are OS methods)
void * malloc( size_t bytes );
void free(void *ptr);

-- Thats it. We are done! Now we can call malloc and free directly!!
local mymem = ffi.C.malloc( 1e8 )  -- alloc 100MB - you can make this over 2GB which is lua's own internal limit

-- Put something in it. FFI lets you use 0 based array assignment!
mymem[0] = 10
mymem[100000] = 20
-- Get the values
pprint(mymem[0], mymem[100000])
-- Let the memory free! Do not forget to do this, or you may end up in a bit of a mess

Some things to note.
When using C library methods like malloc and free, ffi maps them into the ffi.C object. This is why you call them with a leading ffi.C.
When loading external libraries you need to call ffi.load on the library, which will make the methods in for you . More details here: FFI Library