Lou Montana/Sandbox – User

From Bohemia Interactive Community
Jump to navigation Jump to search
m (WIP)
m (WIP)
Line 4: Line 4:
TODO: color code about importance of performance saving? (green / yellow / red)
TODO: color code about importance of performance saving? (green / yellow / red)
-->
-->
----
= Code Optimisation =
----
----
__NOEDITSECTION__
__NOEDITSECTION__
<div style="float: right; margin-left: 1.5em; margin-bottom: 1em; max-width: 25%;">__TOC__</div>
<div style="float: right; margin-left: 1.5em; margin-bottom: 1em; max-width: 25%;">__TOC__</div>
= Code Optimisation =
== Introduction ==
== Introduction ==
This article will try to be a general guide about improving your code '''and''' its performance.
This article will try to be a general guide about improving your code '''and''' its performance.
* The first part ([[#Rules|Rules]]) will focus on having a clean, readable and maintainable code.
* The second part ([[#Code optimisation|Code optimisation]]) is about '''improving performance''', sometimes trading it against code readability.
* The third part ([[#Equivalent commands performance|Equivalent commands performance]]) mentions commands that in appearance have identical effects but may differ in terms of performance according to the use you may have of them.
* The fourth part ([[#Conversion from earlier versions|Conversion from earlier versions]]) is a hopefully helpful, short guide about useful new commands or syntaxes to replace the old ways.
=== Color code ===
* <div style="background-color: red;    border-radius: 50%; display: inline-block; vertical-align: middle; height: 24px; width: 24px;"></div> means you '''must''' change your ways today, ''or with us you will ride…''
* <div style="background-color: orange; border-radius: 50%; display: inline-block; vertical-align: middle; height: 24px; width: 24px;"></div> means you may want to look at it if you are targeting pure performance
* <div style="background-color: green;  border-radius: 50%; display: inline-block; vertical-align: middle; height: 24px; width: 24px;"></div> means the gain is little to unsignificant. Going through your code looking for this replacement is not worth it, but future  code should be written this way.


== Rules ==
== Rules ==
In the domain of development, any rule is a rule of thumb. If a rule states for example that it is ''better'' that a line of code doesn't go over 80 characters, it doesn't mean that any line '''''must not''''' go over 80 characters; sometimes, the situation needs it. If you have a good structure, '''do not''' change your code to enforce a single arbitrary rule. If you break many of them, you may have to change something. Again, this is according to your judgement.<br />
In the domain of development, any rule is a rule of thumb. If a rule states for example that it is ''better'' that a line of code doesn't go over 80 characters, it doesn't mean that any line '''''must not''''' go over 80 characters; sometimes, the situation needs it. If you have a good structure, '''do not''' change your code to enforce a single arbitrary rule. If you break many of them, you may have to change something. Again, this is according to your judgement.
<br />
 
With that being said, here are the three basic rules to get yourself in the clear:
With that being said, here are the three basic rules to get yourself in the clear:


Line 29: Line 39:


* While [[SQF syntax|SQF]] ''is'' impacted by variable name length, this should not take precedence on the fact that code must be readable by a human being. Variables like '''_u''' instead of '''_uniform''' should not be present.
* While [[SQF syntax|SQF]] ''is'' impacted by variable name length, this should not take precedence on the fact that code must be readable by a human being. Variables like '''_u''' instead of '''_uniform''' should not be present.
* ''One-lining'' (putting everything in one statement) memory improvement is not worth the headache it gives when trying to read it. Don't overuse it.
* ''One-lining'' (putting everything in one statement) memory improvement is most of the time not worth the headache it gives when trying to read it. Don't overuse it.
* Indentation is important for the human mind, and space is too. Space is free, use it.
* Indentation is important for the human mind, and space is too. Space is free, use it.
* Do you see the same code, but with different parameters? Time to write a function.
* Same goes for line return; it helps to see a code block wrapping multiple
* Do you see the same code multiple times, only with different parameters? Now is the time to write a function!
* Is your function code far too long? Split it in understandable-sized bites, for your own sanity.
* Is your function code far too long? Split it in understandable-sized bites, for your own sanity.
* Finally, camel-casing (namingLikeThis) your variables and commands will naturally make the code more readable, especially for long names.
* Finally, camel-casing (namingLikeThis) your variables and commands will naturally make the code more readable, especially for long names.
Line 38: Line 49:


See the following code:
See the following code:
<code style="clear: right;">_w=[];{_w pushbackunique primaryweapon _x}foreach((allunits+alldeadmen)select{_x call bis_fnc_objectside==east});</code>
<code style="clear: right;">_w=[]; {_w pushbackunique primaryweapon _x} foreach((allunits+alldeadmen) select{_x call bis_fnc_objectside==east});</code>


The same example is far more readable with proper spacing, good variable names and intermediate results:
The same example is far more readable with proper spacing, good variable names and intermediate results:
Line 58: Line 69:
* Is your variable name far too long?
* Is your variable name far too long?
** Find a smaller name, according to the variable scope:
** Find a smaller name, according to the variable scope:
e.g  
e.g
  { _opforUnitUniform {{=}} uniform _x; systemChat _opforUnitUniform; } forEach _allOpforUnits
  { _opforUnitUniform {{=}} uniform _x; systemChat _opforUnitUniform; } [[forEach]] _allOpforUnits;
becomes
becomes
  { _uniform {{=}} uniform _x; systemChat _uniform; } forEach _allopforUnits
  { _uniform {{=}} uniform _x; systemChat _uniform; } [[forEach]] _allopforUnits;




== Code optimisation ==
== Code optimisation ==
=== Lazy evaluation ===
[[private]] _myVar = [[if]] ([[false]]) [[then]] { 33; } [[else]] { 66; }; {{codecomment|// 0.0020 ms}}
[[private]] "_myVar"; [[if]] ([[false]]) [[then]] { _myVar = 33; } [[else]] { _myVar = 66; }; {{codecomment|// 0.0025 ms}}


=== Successive condition check ===
=== Successive condition check ===
In [[SQF syntax|SQF]] the following code will check '''all and every''' condition, even if one fail:
In [[SQF syntax|SQF]] the following code will check '''all and every''' condition, even if one fail:
  [[if]] (condition1 && condition2 && condition3) [[then]] { {{codecomment|/* thenCode */}} };
  [[if]] (condition1 && condition2 && condition3) [[then]] { {{codecomment|/* thenCode */}} };
e.g:
This code will check ''condition1'', and if it is not [[true]] ''condition2'' and ''condition3'' will execute anyway.
[[if]] ([[alive]] unit1 && [[not]] [[alive]] aPvehicle && [[daytime]] > 5) [[then]] { {{codecomment|/* thenCode */}} };
This code will check if {{Inline code|unit1}} is alive, and if it is not {{Inline code|[[not]] [[alive]] aPvehicle && [[daytime]] > 5}} will execute anyway.
To avoid this behaviour, you can either imbricate [[if|ifs]] or use '''lazy evaluation''' such as the following:
To avoid this behaviour, you can either imbricate [[if|ifs]] or use '''lazy evaluation''' such as the following:
  [[if]] ([[alive]] unit1 && { [[not]] [[alive]] aPvehicle } && {{ [[daytime]] > 5}}) [[then]] { {{codecomment|/* thenCode */}} };
  [[if]] (condition1 && { condition2 } && { condition3 }) [[then]] { {{codecomment|/* thenCode */}} };
This lazy evaluation will stop code execution on the first [[false]] statement.
This method will stop condition evaluation on the first [[false]] statement.


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:
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 &nbsp;|| {{false} || {false}}", [[nil]], 100000] [[call]] [[BIS_fnc_codePerformance]]; {{codecomment|// 0.00080 ms}}
["true &nbsp;|| &nbsp;{false} || {false&nbsp;}", [[nil]], 100000] [[call]] [[BIS_fnc_codePerformance]]; {{codecomment|// 0.00105 ms}}
["false || &nbsp;&nbsp;false &nbsp;|| &nbsp;false&nbsp;&nbsp;", [[nil]], 100000] [[call]] [[BIS_fnc_codePerformance]]; {{codecomment|// 0.00123 ms}}
["true &nbsp;|| &nbsp;&nbsp;false &nbsp;|| &nbsp;false&nbsp;&nbsp;", [[nil]], 100000] [[call]] [[BIS_fnc_codePerformance]]; {{codecomment|// 0.00128 ms}}
["false || &nbsp;{false} || {false}&nbsp;", [[nil]], 100000] [[call]] [[BIS_fnc_codePerformance]]; {{codecomment|// 0.00200 ms}}


[true &nbsp;|| {{false} || {false}}] call BIS_fnc_codePerformance; {{codecomment|// fastest}}
=== Concatenating multiple small strings together ===
[true &nbsp;|| &nbsp;{false} || {false&nbsp;}] call BIS_fnc_codePerformance; {{codecomment|// faster}}
{{Inline code|myString {{=}} myString + otherString}} works fine for small strings, however the bigger the string gets the slower the operation becomes:
[true &nbsp;|| &nbsp;&nbsp;false &nbsp;|| &nbsp;false&nbsp;&nbsp;] call BIS_fnc_codePerformance; {{codecomment|// normal}}
  myString = ""; [[for]] "_i" [[from]] 1 [[to]] 10000 [[do]] { myString = myString + "123" }; {{codecomment|// 290 ms}}
[false || &nbsp;&nbsp;false &nbsp;|| &nbsp;false&nbsp;&nbsp;] call BIS_fnc_codePerformance; {{codecomment|// normal}}
[false || &nbsp;{false} || {false}&nbsp;] call BIS_fnc_codePerformance; {{codecomment|// slowest}}
 
==== Adding large strings together ====
{{Inline code|a {{=}} a + b}} works fine for small strings, however the bigger the string gets the slower this becomes:
  s = ""; [[for]] "_i" [[from]] 1 [[to]] 10000 [[do]] {s = s + "123"}; {{codecomment|// 30000 chars @ 290ms}}


The solution is to use a string array that you will concatenate later:
The solution is to use a string array that you will concatenate later:
  strings = []; [[for]] "_i" [[from]] 1 [[to]] 10000 [[do]] {strings [[pushBack]] "123"}; strings = strings [[joinString]] ""; {{codecomment|// 30000 chars @ 30ms}}
  strings = [];
 
[[for]] "_i" [[from]] 1 [[to]] 10000 [[do]] {strings [[pushBack]] "123"};
strings = strings [[joinString]] ""; {{codecomment|// 30 ms}}


=== Multiplayer recommendations ===
=== Multiplayer recommendations ===
Line 96: Line 109:
* no [[publicVariable]] or public [[setVariable]] at high frequency
* no [[publicVariable]] or public [[setVariable]] at high frequency
* the server is supposed to have a lot of memory, use it
* the server is supposed to have a lot of memory, use it
* variable name length impacts network?


=== createSimpleObject vs createVehicle ===
=== createSimpleObject vs createVehicle ===
Line 105: Line 119:


=== if ===
=== if ===
Both {{Inline code|[[if]] (condition) [[then]] { {{codecomment|/* thenCode */}} } [[else]] { {{codecomment|/* elseCode */}} }; }}
[[if]]..[[then]] { {{codecomment|/* thenCode */}} }; {{codecomment|// fastest}}
and {{Inline code|[[if]] (condition) [[then]] [{ {{codecomment|/* thenCode */}} }, { {{codecomment|/* elseCode */}} }]; }}
[[if]]..[[exitWith]] { {{codecomment|/* exitCode */}} }; {{codecomment|// fast}}
[[if]]..[[then]] { {{codecomment|/* thenCode */}} } [[else]] { {{codecomment|/* elseCode */}} }; {{codecomment|// normal}}
[[if]]..[[then]] [{ {{codecomment|/* thenCode */}} },{ {{codecomment|/* elseCode */}} }] {{codecomment|// normal}}
Both {{Inline code|[[if]]..[[then]] { {{codecomment|/* thenCode */}} } [[else]] { {{codecomment|/* elseCode */}} }; }}<br />
and {{Inline code|[[if]]..[[then]] [{ {{codecomment|/* thenCode */}} }, { {{codecomment|/* elseCode */}} }]; }}<br />
have the same performance.
have the same performance.


=== for ===
=== for ===
Use:
The {{Inline code|[[for]]..[[from]]..[[to]]..[[do]]}} is twice as fast as its alternative syntax, {{Inline code|[[for]]..[[do]]}}.
  [[for]] "_i" [[from]] 0 [[to]] 100 [[do]] { {{codecomment|/* forCode */}} }; {{codecomment|// faster}}
  [[for]] "_i" [[from]] 0 [[to]] 10 [[do]] { {{codecomment|/* forCode */}} }; {{codecomment|// 0.015 ms}}
Instead of:
  [[for]] [{_i = 0}, {_i < 100}, {_i = _i + 1}] [[do]] { {{codecomment|/* forCode */}} }; {{codecomment|// 0.030 ms}}
  [[for]] [{_i = 0}, {_i < 100}, {_i = _i + 1}] [[do]] { {{codecomment|/* forCode */}} }; {{codecomment|// slower}}


=== forEach vs count ===
=== forEach vs count ===
Line 120: Line 138:
Also, there is a limitation as the code inside [[count]] expects [[Boolean]] or [[Nothing]] while itself returns [[Number]].
Also, there is a limitation as the code inside [[count]] expects [[Boolean]] or [[Nothing]] while itself returns [[Number]].


<code>{[[diag_log]] _x} [[count]] [1,2,3,4,5,6,7,8,9]; {{codecomment|// faster}}</code>
<code>{[[diag_log]] _x} [[count]] [1,2,3,4,5]; {{codecomment|// 0.082 ms}}</code>
<code>{[[diag_log]] _x} [[forEach]] [1,2,3,4,5,6,7,8,9]; {{codecomment|// slower}}</code>
<code>{[[diag_log]] _x} [[forEach]] [1,2,3,4,5]; {{codecomment|// 0.083 ms}}</code>


Usage:
Usage:
Line 129: Line 147:
  [[false]]
  [[false]]
  } [[forEach]] [[allUnits]];
  } [[forEach]] [[allUnits]];
''TODO: findIf?''


[[findIf]] (since {{arma3}} v1.82) stops array iteration as soon as the condition is met.
[0,1,2,3,4,5,6,7,8,9] [[findIf]] { _x == 2 }; {{codecomment|// 0.005 ms}}
_quantity = { [[_x]] == 2 } [[count]] [0,1,2,3,4,5,6,7,8,9]; {{codecomment|// 0.0114 ms}}


=== + and format ===
=== + and format ===
Line 140: Line 159:


=== select and if ===
=== select and if ===
Use:
Use [[Inline code|[array] [[select]] [[Boolean]]}} instead of the lazy-evaluated [[if]].
  result = ["false result", "true result"] [[select]] _condition; {{codecomment|// faster}}
  _result = ["false result", "true result"] [[select]] [[true]]; {{codecomment|// 0.0011 ms}}
Instead of the lazy-evaluated [[if]]:
_result = [[if]] ([[true]]) [[then]] { "true result"; } [[else]] { "false result"; }; {{codecomment|// 0.0017 ms}}
result = [[if]] (_condition) [[then]] { "true result"; } [[else]] { "false result"; }; {{codecomment|// slower}}


=== private ===
=== private ===
Use:
Direct declaration ({{Inline code|[[private]] _var {{=}} value}} since {{arma3}} v1.53) is faster than declaring ''then'' assigning the variable.
  private _a = 1;
  private _a = 1;
  private _b = 2;
  private _b = 2;
Line 153: Line 171:
  {{codecomment|// 0.0023 ms}}
  {{codecomment|// 0.0023 ms}}


Instead of:
  private ["_a", "_b", "_c", "_d"];
  private ["_a", "_b", "_c", "_d"];
  _a = 1;
  _a = 1;
Line 161: Line 178:
  {{codecomment|// 0.0040 ms}}
  {{codecomment|// 0.0040 ms}}


However:
However, if you have to reuse the same variable in a loop, external declaration is faster.<br />
The reason behind this is that a declaration in the loop will create, assign and delete the variable in each loop.<br />
An external declaration creates the variable only once and the loop only assigns the value.
  [[for]] "_i" from 1 to 100 do
  [[for]] "_i" from 1 to 100 do
  {
  {
Line 168: Line 187:
  {{codecomment|// 0.186776 ms}}
  {{codecomment|// 0.186776 ms}}


is slower than:
  private ["_a", "_b", "_c", "_d"];
  private ["_a", "_b", "_c", "_d"];
  for "_i" from 1 to 100 do
  for "_i" from 1 to 100 do
Line 178: Line 196:
  };
  };
  {{codecomment|// 0.146327 ms}}
  {{codecomment|// 0.146327 ms}}
The reason behind this is that in the first example variables are created, assigned and deleted in each loop.<br />
In the second example the variables are only created/deleted once and changed often.


=== objectParent and vehicle ===
=== objectParent and vehicle ===
Line 193: Line 209:


=== Config path delimiter ===
=== Config path delimiter ===
{{Inline code|>>}} is slightly faster than {{Inline code|/}} when used in config path with [[configFile]] or [[missionConfigFile]]:
{{Inline code|[[config_greater_greater_name|>>]]}} is slightly faster than {{Inline code|[[config_/_name|/]]}} when used in config path with [[configFile]] or [[missionConfigFile]]:
  [[configFile]] >> "CfgVehicles" {{codecomment|// 0.0019 ms}}
  [[configFile]] >> "CfgVehicles" {{codecomment|// 0.0019 ms}}
is (very slighlty) faster than
is (very slighlty) faster than
Line 215: Line 231:
  [[setPos]] {{codecomment|// 0.0063 ms}}
  [[setPos]] {{codecomment|// 0.0063 ms}}
  [[setPosASLW]] {{codecomment|// 0.0068 ms}}
  [[setPosASLW]] {{codecomment|// 0.0068 ms}}
  [[setVehiclePosition]] {{codecomment|// 0.0077 ms "CAN_COLLIDE"}}
  [[setVehiclePosition]] {{codecomment|// 0.0077 ms with "CAN_COLLIDE"}}
  {{codecomment|// 0.0390 ms "NONE"}}
  {{codecomment|// 0.0390 ms with "NONE"}}




Line 234: Line 250:
* '''Selecting a random item:''' [[BIS_fnc_selectRandom]] has been replaced by [[selectRandom]]
* '''Selecting a random item:''' [[BIS_fnc_selectRandom]] has been replaced by [[selectRandom]]
* '''Removing items:''' {{Inline code|myArray [[set]] [1, objNull]; myArray [[a_-_b|-]] [objNull]}} has been replaced by [[deleteAt]] and [[deleteRange]]
* '''Removing items:''' {{Inline code|myArray [[set]] [1, objNull]; myArray [[a_-_b|-]] [objNull]}} has been replaced by [[deleteAt]] and [[deleteRange]]
* '''Concatenating:''' {{Inline code|myArray {{=}} myArray [[valuea_plus_valueb|+]] [element]}} has been ''reinforced'' by [[append]]: if you don't need the original array to be modified, use +
* '''Concatenating:''' {{Inline code|myArray {{=}} myArray [[valuea_plus_valueb|+]] [element]}} has been ''reinforced'' with [[append]]: if you don't need the original array to be modified, use +
* '''Comparing:''' use [[isEqualTo]] instead of [[BIS_fnc_areEqual]] or a custom comparison function
* '''Comparing:''' use [[isEqualTo]] instead of [[BIS_fnc_areEqual]] or a custom comparison function
** {{Inline code|[[count]] myArray {{=}}{{=}} 0}} is pretty fast, but direct comparison with [[isEqualTo]] is still a little faster: {{Inline code|myArray [[isEqualTo]] []}}
** {{Inline code|[[count]] myArray {{=}}{{=}} 0}} is pretty fast, but direct comparison with [[isEqualTo]] is still a little faster: {{Inline code|myArray [[isEqualTo]] []}}
Line 251: Line 267:
[[String]] manipulation has been simplified with the following commands:
[[String]] manipulation has been simplified with the following commands:
* string [[select]] index
* string [[select]] index
* [[toArray]] and [[toString]] have been reinforced with [[splitString]] and [[joinString]]
* [[toArray]] and [[toString]] have been ''reinforced'' with [[splitString]] and [[joinString]]


=== Multiplayer ===
=== Multiplayer ===
* [[BIS_fnc_MP]] has been replaced by [[remoteExec]] and [[remoteExecCall]]
* [[BIS_fnc_MP]] has been replaced by [[remoteExec]] and [[remoteExecCall]]. Use them!

Revision as of 22:04, 9 May 2018

This page is an intended replacement for Code Optimisation. It is obviously a WORK-IN-PROGRESS.

Code Optimisation


Introduction

This article will try to be a general guide about improving your code and its performance.

  • The first part (Rules) will focus on having a clean, readable and maintainable code.
  • The second part (Code optimisation) is about improving performance, sometimes trading it against code readability.
  • The third part (Equivalent commands performance) mentions commands that in appearance have identical effects but may differ in terms of performance according to the use you may have of them.
  • The fourth part (Conversion from earlier versions) is a hopefully helpful, short guide about useful new commands or syntaxes to replace the old ways.

Color code

  • means you must change your ways today, or with us you will ride…
  • means you may want to look at it if you are targeting pure performance
  • means the gain is little to unsignificant. Going through your code looking for this replacement is not worth it, but future code should be written this way.


Rules

In the domain of development, any rule is a rule of thumb. If a rule states for example that it is better that a line of code doesn't go over 80 characters, it doesn't mean that any line must not go over 80 characters; sometimes, the situation needs it. If you have a good structure, do not change your code to enforce a single arbitrary rule. If you break many of them, you may have to change something. Again, this is according to your judgement.

With that being said, here are the three basic rules to get yourself in the clear:

  1. Make it work
  2. Make it readable
  3. Optimise then

Make it work

Your first goal when coding is to make your code do what you want it does. A good way to reach this objective is to read and getting inspired by other people's code. If you understand it by reading it once, it is probably a good source of inspiration.
When starting from scratch if you know what you want but miss the specific steps to get to your point, it is a good practice to write down in your native language what you want to do. E.g Get all the units near the city, and for each west soldier in them, add damage.

Make it readable

Whether you are cleaning your code or a different person's, you must understand the code without twisting your brain:

  • While SQF is impacted by variable name length, this should not take precedence on the fact that code must be readable by a human being. Variables like _u instead of _uniform should not be present.
  • One-lining (putting everything in one statement) memory improvement is most of the time not worth the headache it gives when trying to read it. Don't overuse it.
  • Indentation is important for the human mind, and space is too. Space is free, use it.
  • Same goes for line return; it helps to see a code block wrapping multiple
  • Do you see the same code multiple times, only with different parameters? Now is the time to write a function!
  • Is your function code far too long? Split it in understandable-sized bites, for your own sanity.
  • Finally, camel-casing (namingLikeThis) your variables and commands will naturally make the code more readable, especially for long names.
_i is an accepted variable standard for a for..do iteration

See the following code: _w=[]; {_w pushbackunique primaryweapon _x} foreach((allunits+alldeadmen) select{_x call bis_fnc_objectside==east});

The same example is far more readable with proper spacing, good variable names and intermediate results: _weaponNames = []; _allUnitsAliveAndDead = allUnits + allDeadMen; _allEastAliveAndDead = _allUnitsAliveAndDead select { _x call BIS_fnc_objectSide == east }; { _weaponNames pushBackUnique primaryWeapon _x } forEach _allEastAliveAndDead;

  • if you have a lot of if..else, you may want to look at a switch condition, or again break your code in smaller functions.

Optimise then

Once you know what is what, you can understand your code better.

  • You were iterating multiple times on the same array?
    • You should be able to spot your issue now.
  • Are you using execVM on the same file, many times?
  • Is your function code far too long?
    • Split it in understandable-sized bites, for your own sanity.
  • Is your variable name far too long?
    • Find a smaller name, according to the variable scope:

e.g

{ _opforUnitUniform = uniform _x; systemChat _opforUnitUniform; } forEach _allOpforUnits;

becomes

{ _uniform = uniform _x; systemChat _uniform; } forEach _allopforUnits;


Code optimisation

Lazy evaluation

private _myVar = if (false) then { 33; } else { 66; }; 	 	 	 	 	 	// 0.0020 ms
private "_myVar"; if (false) then { _myVar = 33; } else { _myVar = 66; }; 	// 0.0025 ms

Successive condition check

In SQF the following code will check all and every condition, even if one fail:

if (condition1 && condition2 && condition3) then { /* thenCode */ };

This code will check condition1, and if it is not true condition2 and condition3 will execute anyway. To avoid this behaviour, you can either imbricate ifs or use lazy evaluation such as the following:

if (condition1 && { condition2 } && { condition3 }) then { /* thenCode */ };

This method will stop condition evaluation on the first false statement.

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}}", nil, 100000] call BIS_fnc_codePerformance;	// 0.00080 ms
["true  ||  {false} || {false }", nil, 100000] call BIS_fnc_codePerformance;	// 0.00105 ms
["false ||   false  ||  false  ", nil, 100000] call BIS_fnc_codePerformance;	// 0.00123 ms
["true  ||   false  ||  false  ", nil, 100000] call BIS_fnc_codePerformance;	// 0.00128 ms
["false ||  {false} || {false} ", nil, 100000] call BIS_fnc_codePerformance;	// 0.00200 ms

Concatenating multiple small strings together

myString = myString + otherString works fine for small strings, however the bigger the string gets the slower the operation becomes:

myString = ""; for "_i" from 1 to 10000 do { myString = myString + "123" }; // 290 ms

The solution is to use a string array that you will concatenate later:

strings = [];
for "_i" from 1 to 10000 do {strings pushBack "123"};
strings = strings joinString ""; // 30 ms

Multiplayer recommendations

TODO

  • no publicVariable or public setVariable at high frequency
  • the server is supposed to have a lot of memory, use it
  • variable name length impacts network?

createSimpleObject vs createVehicle

TODO create at [0,0,0]


Equivalent commands performance

if

if..then { /* thenCode */ };							// fastest
if..exitWith { /* exitCode */ };						// fast
if..then { /* thenCode */ } else { /* elseCode */ };	// normal
if..then [{ /* thenCode */ },{ /* elseCode */ }]		// normal

Both if..then { /* thenCode */ } else { /* elseCode */ };
and if..then [{ /* thenCode */ }, { /* elseCode */ }];
have the same performance.


for

The for..from..to..do is twice as fast as its alternative syntax, for..do.

for "_i" from 0 to 10 do { /* forCode */ };						// 0.015 ms
for [{_i = 0}, {_i < 100}, {_i = _i + 1}] do { /* forCode */ };	// 0.030 ms

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 the _x variable. However, count loop is a little faster than forEach loop, but it does not have _forEachIndex variable.
Also, there is a limitation as the code inside count expects Boolean or Nothing while itself returns Number.

{diag_log _x} count [1,2,3,4,5]; // 0.082 ms {diag_log _x} forEach [1,2,3,4,5]; // 0.083 ms

Usage: _someoneIsNear = {_x distance [0,0,0] < 1000} count allUnits > 0;

_someoneIsNear = {
	if (_x distance [0,0,0] < 1000) exitWith { true };
	false
} forEach allUnits;

findIf (since Arma 3 v1.82) stops array iteration as soon as the condition is met.

[0,1,2,3,4,5,6,7,8,9] findIf { _x == 2 };				// 0.005 ms
_quantity = { _x == 2 } count [0,1,2,3,4,5,6,7,8,9];	// 0.0114 ms

+ and format

When concatenating more than two strings, format is faster than +. Use:

format ["%1%2%3%4%5", "string1", "string2", "string3", "string4", "string5"]; // faster

Instead of:

"string1" + "string2" + "string3" + "string4" + "string5"; // slower

select and if

Use [[Inline code|[array] select Boolean}} instead of the lazy-evaluated if.

_result = ["false result", "true result"] select true;					// 0.0011 ms
_result = if (true) then { "true result"; } else { "false result"; };	// 0.0017 ms

private

Direct declaration (private _var = value since Arma 3 v1.53) is faster than declaring then assigning the variable.

private _a = 1;
private _b = 2;
private _c = 3;
private _d = 4; 
// 0.0023 ms
private ["_a", "_b", "_c", "_d"];
_a = 1;
_b = 2;
_c = 3;
_d = 4; 
// 0.0040 ms

However, if you have to reuse the same variable in a loop, external declaration is faster.
The reason behind this is that a declaration in the loop will create, assign and delete the variable in each loop.
An external declaration creates the variable only once and the loop only assigns the value.

for "_i" from 1 to 100 do
{
	private _a = 1; private _b = 2; private _c = 3; private _d = 4;
};
// 0.186776 ms
private ["_a", "_b", "_c", "_d"];
for "_i" from 1 to 100 do
{
	_a = 1;
	_b = 2;
	_c = 3;
	_d = 4;
};
// 0.146327 ms

objectParent and vehicle

isNull objectParent player // 0.0013 ms, slightly faster vehicle player == player // 0.0022 ms

nearEntities and nearestObjects

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.

Config path delimiter

>> is slightly faster than / when used in config path with configFile or missionConfigFile:

configFile >> "CfgVehicles" // 0.0019 ms

is (very slighlty) faster than

configFile / "CfgVehicles" // 0.0023 ms

getPos* and setPos*

getPosWorld			// 0.0015 ms
getPosASL			// 0.0016 ms
getPosATL			// 0.0016 ms
getPos				// 0.0020 ms
position			// 0.0020 ms
getPosVisual		// 0.0021 ms
visiblePosition		// 0.0021 ms
getPosASLW			// 0.0023 ms
setPosWorld			// 0.0060 ms
setPosASL			// 0.0060 ms
setPosATL			// 0.0060 ms
setPos				// 0.0063 ms
setPosASLW			// 0.0068 ms
setVehiclePosition	// 0.0077 ms with "CAN_COLLIDE"
					// 0.0390 ms with "NONE"


Conversion from earlier versions

Each iteration of Bohemia games (Operation Flashpoint, Arma, Arma 2, Take On Helicopters, Arma 3) brought their own new commands, especially Arma 2 and Arma 3.
For that, if you are converting scripts from older versions of the engine, some aspects should be reviewed:

Loops

Array operations

result = []; { if (_x % 2 == 0) then { result pushBack _x; }; } forEach arrayOfNumbers; // 2.57 ms

result = (arrayOfNumbers select { _x % 2 == 0 }); // 1.55 ms

String operations

String manipulation has been simplified with the following commands:

Multiplayer