Good Practices

Codes good practices, pitfalls and tips

LUA

Gain performance: Early Return

You're probably used to coding like this:

local function myMethod()
  if condition then 
    -- do a little dance, make a little love…
  end
end

In LUA, you can skim a few processing cycles by using the early return style:

local function myMethod()
  if not condition then return end
  -- do a little dance, make a little love…
end

You can gain a significant amount of performance this way, especially when doing this in a loop.

Fixing/preventing nil access

LUA throws an exception if it encounters nil in unexpected places. The corresponding error will look like this:

attempt to access local '<variable name>' (a nil value)
stack traceback: 
  my_example.lua:1234 in function 'MyFunctionName

Open the corresponding file, find the correct line, and check what is being accessed there. It will look like variable.property, or perhaps something will be concatenated to a string ("something something text" .. variable).

You can assign a default value to the property in question:

myString = <original string assignment> or ""
myNumber = <original number assignment> or 0
myEverythingElse = <original object assignment> or {}

While that won't solve any other problems, it will at least make the error go away.

Switch in LUA: Lookup Tables

Who doesn't know the problem? You want to know if your string is A, B, or C, but not D — and LUA doesn't have a switch statement.

Fortunately, there is a built-in and performant way to

Performance killers: strings

String concatenation and comparison can be the difference between a brief stutter and a complete freeze or even crash to desktop. This is not a joke — see here for more detail.

Comparing/Searching

Lua internalizes strings. That means these two strings will share a single representation in memory:

local string1 = "This is the same object!"
local string2 = "This is the same object!"

The comparison between those two strings will be almost-instant.

This becomes a problem when comparing strings in a loop (see Scopes):

for (_, mystring) in ipairs(mytable) do
    if mystring == "This is the same object!" then
        -- do something
    end
end

Every single pass of the loop will create a memory representation of "This is the same object!" and then discard it again.

local myCompareString = "This is the same object!"
for (_, mystring) in ipairs(mytable) do
    if mystring == myCompareString then
        -- do something
    end
end

Takeaway:

If at all possible, define things outside the scope of loops!

Finding in strings

Lua's regex implementation is very limited. There is a limitation for pipes. For example, the following example will actually iterate twice after creating internal string representations:

if string.find("catastrophe",  "dog|cat") then 
  -- do something
end

It is faster to just do this:

if string.find("catastrophe",  "dog") or string.find("catastrophe",  "cat") then 
  -- do something
end

On top of that, string.match will return the entire string if no match is found:

local match = string.match("catastrophe",  "dog")
if match ~= "catastrophe" then
  -- do something
end

The alternative:

if string.find("catastrophe",  "dog")
  -- do something
end

Takeaway:

  • Avoid regex

  • prefer String.find() over String.match()

Concatenation

For a performance analysis of different kinds of string concatenation, check here.

Scopes

Don't do this (30% less performant):

function foo(x)
    for i = 1, 1000000 do
        x = x + math.sin(i)
    end
    return x
end

Do this instead:

local sin = math.sin
function foo(x)
    for i = 1, 1000000 do
        x = x + sin(i)
    end
    return x
end

Last updated