Since optimization in FtD Lua can be very different from optimization in other languages and environments such as C or Java, I thought it might be useful to collect information about FtD Lua specific optimization. (but I also include some more general optimization-related stuff)

The main goal would be to find the fastest alternative when there are multiple ways to do something. The results below are based on benchmarks executed in FtD. The specific quantitative differences may depend on the machine used, but the qualitative differences should be mostly universal. Especially the items listed under 'Major FtD specific' are worth keeping in mind as they are not obvious but can have a major impact on performance.

The list below is by no means complete; I just wrote down the first things that came to mind. Any suggestions for additions, changes or clarifications are always welcome.

Major FtD specific:

the math library seems to be much faster than the Mathf library. This means that whenever possible, use function in the math library such as math.sqrt instead of the equivalent functions in the Mathf library such as Mathf.Sqrt.

When working with a Vector3 instance v, accessing its members with v[1], v[2], v[3] is orders of magnitude faster than accessing them with v.x, v.y, v.z. The same is not true for normal Lua tables.

Creating a Vector3 instance takes roughly twice as long as creating an array (table) with three entries.

For a Vector3 instance v, avoid using v.sqrMagnitude and v.magnitude as they are much slower than the respectively equivalent Vector3.Dot(v,v) and math.sqrt(Vector3.Dot(v,v)).

The (deprecated) I:GetMissileInfo is incredibly slow compared to I:GetLuaControlledMissileInfo, so only use the former if you can't do the same with the latter.

Major general:

Always use the local keyword when defining functions and variables, even if they are used as a global function or variable. The only exception is the Update function which must be global. Performance comparison of 'local global' variables and truly global variables:

runs in 26ms as the baseline for the next two benchmarks,

runs in 54ms while

runs in only 28ms. Adding the local keyword can not change the functionality as everything we do is within its scope, but it improves performance from 28ms over the baseline to only 2ms over the baseline. Interestingly, it only seems to matter when writing to the variable, not when reading it.

Avoid goniometric and inverse goniometric functions whenever possible. From a benchmark, it seems that math.sin and math.cos take at least 4 times longer than math.sqrt and math.tan, math.acos, math.asin and math.atan take at least 5 times longer. In many cases it is possible to replace some goniometric functions with square roots such as

instead of

Minor FtD specific:

A call to math.abs takes slightly longer than call to math.sqrt (which is weird). Replacing math.abs with an inline if-else is faster, creating your own abs function to use is slower.

Minor general:

When using global functions more than once within the same local scope, making a local reference can have a significant impact on performance. That is,

will have faster execution of bar than

This extends to build in libraries such as math and Vector3. Making a local reference to the library or to specific functions within the library will improve performance if they are used more than once.

You can go even further with this: when using a self-made function that, for example, uses some global function, it is executed faster if the function is created inside a closure that contains a local reference to the global function that's used. An example for the case of calculating the magnitude of a vector:

The performance gain by doing this is small but measurable. Readability does suffer badly though. The function in this example is faster than both v.magnitude and math.sqrt(Vector3.Dot(v,v)).

The main goal would be to find the fastest alternative when there are multiple ways to do something. The results below are based on benchmarks executed in FtD. The specific quantitative differences may depend on the machine used, but the qualitative differences should be mostly universal. Especially the items listed under 'Major FtD specific' are worth keeping in mind as they are not obvious but can have a major impact on performance.

The list below is by no means complete; I just wrote down the first things that came to mind. Any suggestions for additions, changes or clarifications are always welcome.

Major FtD specific:

the math library seems to be much faster than the Mathf library. This means that whenever possible, use function in the math library such as math.sqrt instead of the equivalent functions in the Mathf library such as Mathf.Sqrt.

When working with a Vector3 instance v, accessing its members with v[1], v[2], v[3] is orders of magnitude faster than accessing them with v.x, v.y, v.z. The same is not true for normal Lua tables.

Creating a Vector3 instance takes roughly twice as long as creating an array (table) with three entries.

For a Vector3 instance v, avoid using v.sqrMagnitude and v.magnitude as they are much slower than the respectively equivalent Vector3.Dot(v,v) and math.sqrt(Vector3.Dot(v,v)).

The (deprecated) I:GetMissileInfo is incredibly slow compared to I:GetLuaControlledMissileInfo, so only use the former if you can't do the same with the latter.

Major general:

Always use the local keyword when defining functions and variables, even if they are used as a global function or variable. The only exception is the Update function which must be global. Performance comparison of 'local global' variables and truly global variables:

Code:

`function Update(I)`

for i = 1, 10000000 do

end

end

Code:

`b = nil --truly global variable. Don't ever do this.`

function Update(I)

for i = 1, 10000000 do

b = 0

end

end

Code:

`local b = nil --note the local keyword here`

function Update(I)

for i = 1, 10000000 do

b = 0

end

end

Avoid goniometric and inverse goniometric functions whenever possible. From a benchmark, it seems that math.sin and math.cos take at least 4 times longer than math.sqrt and math.tan, math.acos, math.asin and math.atan take at least 5 times longer. In many cases it is possible to replace some goniometric functions with square roots such as

Code:

`local cosa = math.cos(a);`

local sina = math.sqrt(1 - cosa * cosa);

Code:

`local cosa = math.cos(a);`

local sina = math.sin(a);

Minor FtD specific:

A call to math.abs takes slightly longer than call to math.sqrt (which is weird). Replacing math.abs with an inline if-else is faster, creating your own abs function to use is slower.

Minor general:

When using global functions more than once within the same local scope, making a local reference can have a significant impact on performance. That is,

Code:

`function bar()`

local foo = foo;

foo();

foo();

end

function foo()

end

Code:

`function bar()`

foo();

foo();

end

function foo()

end

You can go even further with this: when using a self-made function that, for example, uses some global function, it is executed faster if the function is created inside a closure that contains a local reference to the global function that's used. An example for the case of calculating the magnitude of a vector:

Code:

`function createMagnitudeFunction() --this function is just here to create a closure`

local sqrt = math.sqrt; --create a local reference to the square root function

function magnitudeFunction(x)

local x1 = x[1];

local x2 = x[2];

local x3 = x[3];

return sqrt(x1 * x1 + x2 * x2 + x3 * x3); --we use the external local reference sqrt instead of using math.sqrt

end

return magnitudeFunction; --we return the function

end

magnitudeFunction = createMagnitudeFunction(); --create the actual function and assign it to a variable;