Professional Documents
Culture Documents
Make it fast.
Optimisation is everything when running lots of instances, with low delays. However, there is such thing as
premature optimisation. Also, avoid excessive cleverness.
"Excessive cleverness is doing something in a really clever way when actually you could have done it in a much
more straightforward but slightly less optimal manner. You've probably seen examples of people who construct
amazing chains of macros (in C) or bizarre overloading patterns (in C++) which work fine but which you look at
and go "wtf"? EC is a variation of premature-optimisation. It's also an act of hubris - programmers doing things
because they want to show how clever they are rather than getting the job done." - sbsmac
Written it twice? Put it in a function
Pre-compilation by the game engine can save up 20x the amount of time processing, even if the initial time is
slightly lengthened. If you've written it twice, or if there is a kind of loop consistently being compiled (perhaps a
script run by execVM), make it into a function (FUNCVAR =compile preprocessfilelinenumbers "filename.sqf")
Preprocessfilelinenumbers
The preprocessFileLineNumbers command remembers what it has done, so loading a file once will load it into
memory, therefore if wanted to refrain from using global variables for example, but wanted a function
precompiled, but not saved, you could simply use: call compile preprocessfilelinenumbers "file"
Remembering the only loss of performance will be the compile time of the string returned and then the call of the
code itself.
Length
If any script or function is longer than around 200-300 lines, then perhaps (not true in all cases by all means) you
may need to rethink the structure of the script itself, and whether it is all within scope of the functionality required,
and if you could do something cleaner, faster and better.
Fewer statements => faster code
This may sound too obvious, but... optimise the code by removing redundant statements. The following code
examples do the same thing, but the latter is 1.5 times faster:
_arr = [1,2];
_one = _arr select 0;
_two = _arr select 1;
_three = _one + _two;
_arr = [1,2];
Variable Names
Scripts and functions that use long variable names will run more slowly than those with short names. Using
cryptically short variable names is not recommended without explanatory comments. _pN = "John Smith";
//this line executes in half the time of the line below
_playerNameBecauseThePlayerIsImportantAndWeNeedToKnowWhoTheyAreAllTheTimeEspeciallyInsideThis
ImpressiveFunction = "John Smith";
Conditions
if (_group knowsAbout vehicle _object > 0 && alive _object && canMove _object && count magazines
_object > 0) then {
//custom code
};
You may expect the engine to stop reading the condition after the group has no knowledge about the object but
that's false. The engine will continue evaluating the condition until the end even if any of the previous conditions
evaluated false.
if (_group knowsAbout vehicle _object > 0) then {
if (alive _object && canMove _object && count magazines _object > 0) then {
//custom code
};
};
Now the engine will only continue reading the condition after the group has some knowledge about the object.
Alternatively you can use lazy evaluation syntax. If normal evaluation syntax is (bool1 .. bool2 .. bool3 .. ...), lazy
evaluation syntax is (bool1 .. {bool2} .. {bool3} .. ...). Now let's look at the above example using lazy evaluation:
if (_group knowsAbout _vehicle object > 0 && {alive _object} && {canMove _object} && {count
magazines _object > 0}) then {
//custom code
};
Using lazy evaluation is not always the best way as it could speed up the code as well as slow it down,
depending on the current condition being evaluated:
['true || {false} || {false}'] call BIS_fnc_codePerformance; //fastest
isNil
isNil String is quite a bit faster than isNil Code
var = 123;
isNil "var";
// is faster than
isNil {var};
Make it pretty.
Documentation, readability, and all that jazz. Clean code is good code.
If Else If Else If Else ...
If you can't escape this using a switch control structure, then try and rethink the functionality. Especially if only
one option is needed to match.
On the other hand switch is slower than if then else. To keep tidiness of the switch and speed of if,
use if exitWith combined with call: call {
if (cond1) exitWith {//code 1};
if (cond2) exitWith {//code 2};
if (cond3) exitWith {//code 3};
//default code
};
if () then {}
is faster than
if () exitWith {}
is faster than
if () then {} else {}
or
if () then [{},{}]
However there is no noticeable difference in speed in the following:
_a = 0; if (true) then {_a = 1};
Constants
Using a hard coded constant more than once? Use preprocessor directives rather than storing it in memory or
cluttering your code with numbers. Such as: a = _x + 1.053;
b = _y + 1.053;
And
_buffer = 1.053;
a = _x + _buffer;
b = _y + _buffer;
_a = _x + BUFFER;
_b = _y + BUFFER;
This also allows quick modifying of code; with the obvious loss of dynamics, but in that case it isn't a constant is
it.
Loops
These first two loop types are identical in speed (+/- 10%), and are more than 3x as fast the proceeding two loop
types.
Where as these two loops are much slower, and for maximum performance, avoided.
Waituntil can be used when you want something to only run once per frame, which can be handy for limiting
scripts that may be resource heavy.
waitUntil {expression};
As requested, the method to gain this information was via the CBA_fnc_benchmarkFunction, using around
10,000 iterations. It was not tested across different stations, and *may* be subject to change between them
(ArmA2 is special remember :P):
fA = {
private "_i";
_i = 0;
while {_i < 1000} do {
_i = _i + 1;
private "_t";
_t = "0";
};
};
fB = {
for "_i" from 0 to 1000 do {
private "_t";
_t = "0";
};
};
This code then performs 10,000 tests and returns average time taken for the function, measured via
diag_ticktime.
[fA,[],10000] call CBA_fnc_benchmarkFunction;
[fB,[],10000] call CBA_fnc_benchmarkFunction;
Threads
The game runs in a scheduled environment, and there are two ways you can run your code. Scheduled and non
scheduled.
Depending on where the scope originates, determines how the code is executed. Scheduled code is subject to
delays between reading the script across the engine, and execution times can depend on the load on the system
at the time.
Some basic examples:
Avoid O(n^2)!!
Commonly you may set up foreach foreach's. 'For' example:
{
{ ...} foreach [0,0,0];
} foreach [0,0,0];
This example is of the order (n^2) (3^2 = 9 iterations). For arrays that are twice as big, you will run 4 times
slower, and for arrays that are 3 times as big you will run 9 times slower! Of course, you don't always have a
choice, and if one (or both) of the arrays is guaranteed to be small it's not really as big of a deal.
Deprecated/Slow Commands
pushBack was added in ARMA3 1.26 and is currently the fastest command to push an element into an
array, as of 1.29 it will also return the index of the element. Quick tests shows it's around 2x faster than the
below method, set. Not to mention it is also easier to read.
_a pushBack _v
Instead of:
_a = _a + [_v]
Faster than...
When FIFO removing elements from an array, the set removal method works best, even if it makes a copy of the
new array.
ARRAYX set [0, objnull];
ARRAYX = ARRAYX - [objnull];
Combining arrays
Comparing arrays
To compare arrays use the following function:
KK_fnc_isEqual = {
switch (_this select 0) do {
case (_this select 1) : {true};
default {false};
};
};
Examples:
hint str ([[1,2,3], [1,2,3]] call KK_fnc_isEqual); //true
hint str ([[1,[2,[3]]], [1,[2,[3]]]] call KK_fnc_isEqual); //true
hint str ([[1,[2,[3]]], [1,[2,[4]]]] call KK_fnc_isEqual); //false
In Arma 3 use isEqualTo command to compare arrays.
Comparing values by type
"a" isEqualType 0
//0.0009 ms
Checking if array is []
Traditional (count _arr == 0) is pretty fast, but direct comparison with new comparison command is a little faster:
(_arr isEqualTo [])
Position World is the fastest
getPosASL, getPosATL and visiblePositionASL are faster than getPos, position and visiblePosition. But new to
Arma 3 command getPosWorld is the fastest of them all. getPosWorld player
//0.0014 ms
getPosASL player
//0.0014 ms
getPosATL player
//0.0015 ms
visiblePositionASL player
//0.0014 ms
visiblePosition player
//0.0048 ms
getPos player
//or
position player
//0.005 ms
nearEntities vs nearestObjects
nearEntities is much faster than nearestObjects given on range and amount of object(s) which are within
the given range.
If a range was set to more thean 100 meters it is highly recommend to use nearEntities instead
of nearestObjects.
Note: nearEntities only searches for objects which are alive. Killed units, destroyed vehicles, static objects and
buildings will be ignored by the nearEntities command.
forEach vs count
Both commands will step through supplied array of elements one by one and both commands will contain
reference to current element in _x variable. However, count loop is a little faster than forEach loop, but it
does not have _forEachIndex variable and the code inside count expects Boolean or Nothing while it
returns Number.
format vs +
The solution is to use array to make string and then convert array to string:
s = []; for "_i" from 1 to 10000 do {s pushBack "123"}; s = s joinString ""; //30000 chars @ 30ms
select vs if
when selecting between two variables, select is faster than using an if statement.
a = "You're " + (["a loser","awesome!"] select true)
//0.0046 ms
a = "You're " + (if true then [{"awesome!"},{"a loser"}])
//0.0054 ms
createVehicle(Local)
createVehicle(Local) position is not exact so you must use setPos but this is very slow, to create the object on
[0,0,0] and then set the position is faster.
_obj = 'Land_Stone_4m_F' createVehicle [0,0,0]; //also createVehicleLocal
_obj setPos (getPos player); //0,03ms (100 testcycles)
createSimpleObject vs createVehicle
createSimpleObject is over 43x faster than createVehicle! createVehicle ["Land_VR_Shape_01_cube_1m_F",
[0,0,0],[],0,"none"];// ~3.5 mscreateSimpleObject
["a3\structures_f_mark\vr\shapes\vr_shape_01_cube_1m_f.p3d",[0,0,0]];// ~0.08 ms
is slower than the new "private _var = value" syntax (Arma 3 v1.53+):
private _a = 1; private _b = 2; private _c = 3; private _d = 4;
// 0.0023 ms
_fnc_dump = {
player globalchat str _this;
diag_log str _this;
//copytoclipboard str _this;
};
_t1 = diag_tickTime;
// ... code to test
(diag_tickTime - _t1) call _fnc_dump;
In ArmA 3 you can simply use in-built library function BIS_fnc_codePerformance, now integrated into the
debug console as the speedometer button.