LuaJIT and the FFI

35.1 About LuaJIT

LuaJIT (by Mike Pall) is a tracing JIT compiler for Lua 5.1 (with some 5.2 extensions). It's extremely fast and often comparable to C for numeric code.

It's used by OpenResty, Kong, Neovim, LÖVE, Snabb, and game engines.

Compatibility: Lua 5.1 syntax/semantics (no integers, goto supported, // and native bitwise operators not supported; use the bit library). Development continues on the v2.1 rolling branch (there are no formal releases).

35.2 The FFI: Call C Directly from Lua

local ffi = require("ffi")

ffi.cdef[[
    int printf(const char *fmt, ...);
    size_t strlen(const char *s);
    typedef struct { double x, y; } point_t;
    double sqrt(double x);
]]

ffi.C.printf("Hello %s from %d\n", "FFI", 2025)
print(ffi.C.strlen("LuaJIT"))

-- C structs and arrays (fast, compact)
local p = ffi.new("point_t", 3, 4)
print(ffi.C.sqrt(p.x * p.x + p.y * p.y))       --> 5
local arr = ffi.new("double[?]", 1000000)       -- 8 MB native array, much faster than tables for numeric work
for i = 0, 999999 do arr[i] = i * 0.5 end       -- 0-based!

-- Metatypes: methods on C types
local Point = ffi.metatype("point_t", {
    __index = { length = function(self) return math.sqrt(self.x ^ 2 + self.y ^ 2) end },
    __add = function(a, b) return ffi.new("point_t", a.x + b.x, a.y + b.y) end,
    __tostring = function(self) return ("(%g, %g)"):format(self.x, self.y) end,
})
local q = Point(1, 2) + Point(3, 4)
print(tostring(q), q:length())

-- Load other shared libraries
local zlib = ffi.load(ffi.os == "Windows" and "zlib1" or "z")
ffi.cdef[[ unsigned long compressBound(unsigned long sourceLen); ]]
print(zlib.compressBound(1000))

-- Memory management
local buf = ffi.gc(ffi.C.m
…

35.3 LuaJIT Performance Tips

Write JIT-friendly code: avoid NYI ("not yet implemented") operations in hot loops (some string functions, pairs in older versions, varargs-heavy code). Check with -jv / -jdump: luajit -jv script.lua.

Use ffi arrays/structs for numeric data, cache globals and module functions in locals, and avoid creating closures in hot loops.

Extras: bit.* (bitwise operations), jit.* (control the JIT), table.new/table.clear (via require "table.new"), string.buffer (2.1).