Lou Montana/Sandbox – User

From Bohemia Interactive Community
Jump to navigation Jump to search
m (WIP)
m (Update w/ links)
 
(128 intermediate revisions by 5 users not shown)
Line 1: Line 1:
{{Informative|This page is an intended replacement for '''[[Code Optimisation]]'''. It is obviously '''a WORK-IN-PROGRESS'''.}}
[[Category: Sandbox]]
[[Category:Sandbox]]
{{Feature|informative|To go on [[Initialisation Order]].}}
<!--
TODO: color code about importance of performance saving? (green / yellow / red)
-->
----
= Code Optimisation =
----
__NOEDITSECTION__
<div style="float: right; margin-left: 1.5em; margin-bottom: 1em; max-width: 25%;">__TOC__</div>
== Introduction ==
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 ===
{| class="wikitable sortable align-center align-left-col-1"
* <div style="background-color: red;    border-radius: 50%; display: inline-block; vertical-align: middle; height: 1.125em; width: 1.125em;"></div> means you '''must''' change your ways today, ''or with us you will ride…''
|+ Order of Initialisation (use column sorting for respective machine order)
* <div style="background-color: orange; border-radius: 50%; display: inline-block; vertical-align: middle; height: 1.125em; width: 1.125em;"></div> means you may want to look at it if you are targeting pure performance
! rowspan="2" class="unsortable" style="text-align: center" | Task
* <div style="background-color: green;  border-radius: 50%; display: inline-block; vertical-align: middle; height: 1.125em; width: 1.125em;"></div> means the gain is little to insignificant. Going through your code for this replacement is not worth it. You ''may'' only consider it for future code.
! rowspan="2" | Exec Environment
! rowspan="1" colspan="5" class="unsortable" | Machine
|-
! Single Player
! Dedicated Server
! Hosted Server
! Multiplayer Client
! [[Multiplayer Scripting#Join In Progress|JIP]] MP Client


|-
| [[Arma 3: Functions Library|Functions]] with <syntaxhighlight lang="cpp" inline>recompile</syntaxhighlight> {{Link|Arma 3: Functions Library#Attributes 3|attribute}} are recompiled
| {{n/a}}
| 1 <!-- Single Player -->
| 1 <!-- Dedicated Server -->
| 1 <!-- Hosted Server -->
| 1 <!-- Multiplayer Client -->
| 1 <!-- JIP MP Client -->


== 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.
| [[Arma 3: Functions Library|Functions]] with <syntaxhighlight lang="cpp" inline>preInit</syntaxhighlight> {{Link|Arma 3: Functions Library#Attributes 3|attribute}} are called
| [[Scheduler#Unscheduled Environment|Unscheduled]]
| 2 <!-- Single Player -->
| 2 <!-- Dedicated Server -->
| 2 <!-- Hosted Server -->
| 2 <!-- Multiplayer Client -->
| 2 <!-- JIP MP Client -->


With that being said, here are the three basic rules to get yourself in the clear:
|-
| Object Init Event Handlers are called
| [[Scheduler#Unscheduled Environment|Unscheduled]]
| 3 <!-- Single Player -->
| 3 <!-- Dedicated Server -->
| 3 <!-- Hosted Server -->
| 3 <!-- Multiplayer Client -->
| {{Icon|unchecked}} <!-- JIP MP Client -->


# [[#Make it work|Make it work]]
|-
# [[#Make it readable|Make it readable]]
| Expressions of [[Eden Editor: Configuring Attributes|Eden Editor entity attributes]] are called<ref name="isPlayer"><sqf inline>isPlayer _entity</sqf> does not return [[true]] immediately. Once the entity has become a [[player]], it is transferred to the client.</ref>
# [[#Optimise then|Optimise then]]
| [[Scheduler#Unscheduled Environment|Unscheduled]]
| 4 <!-- Single Player -->
| 4 <!-- Dedicated Server -->
| 4 <!-- Hosted Server -->
| {{Icon|unchecked}} <!-- Multiplayer Client -->
| {{Icon|unchecked}} <!-- JIP MP Client -->


=== Make it work ===
|-
{{note|"Premature optimization is the root of all evil." – ''[https://en.wikipedia.org/wiki/Donald_Knuth Donald Knuth]'' }}
| Object initialisation fields are called
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.
| [[Scheduler#Unscheduled Environment|Unscheduled]]
| 5 <!-- Single Player -->
| 5 <!-- Dedicated Server -->
| 5 <!-- Hosted Server -->
| 4 <!-- Multiplayer Client -->
| 3 <!-- JIP MP Client -->


* 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 [[allUnits|all the units]] [[distance|near]] [[locationPosition|the city]], and [[forEach|for each]] [[side|west]] soldier in them, [[setDamage|add 30% damage]]''.
|- style="background-color: #95F0AD"
* Use [[Arma_3_Startup_Parameters#Developer_Options|-showScriptErrors]] startup parameter and '''make sure your code doesn't throw errors.''' Not only will your code run slower but it may also ''not work at all''. Be sure to read the error, isolate the issue and sort it out [[:Category:Scripting Commands|thanks to this Wiki]].
| [[Event Scripts#init.sqs|init.sqs]] is executed
* Read your [[Crash_Files|Arma RPT]] (report) to read more details about the error that happened in your code.
|
| 6 <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


=== Make it readable ===
|- style="background-color: #95F0AD"
Whether you are cleaning your code or a different person's, you must understand the code without twisting your brain:
| [[Event Scripts#init.sqf|init.sqf]] is executed
| [[Scheduler#Scheduled Environment|Scheduled]]<ref name="enginewaits">Note '''in single player''' that while the environment is [[Scheduler#Scheduled Environment|Scheduled]] ([[canSuspend]] returns true), the engine seems to wait until the script is done executing, essentially behaving similarly to an [[Scheduler#Unscheduled Environment|Unscheduled environment]] - infinite loops will freeze the game, [[uiSleep]] may pause the game for up to ~20s (postInit), [[waitUntil]] can cause catastrophic issues, etc.</ref>
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


* While [[SQF syntax|SQF]] ''is'' (non-noticeably) 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.
| Expressions of [[Eden Editor: Configuring Attributes|Eden Editor scenario attributes]] are called<ref name="playerCommandNotAvailable">[[player]] is not available immediately.</ref>
* Indentation is important for the human mind, and space is too. Space is free, use it.
| [[Scheduler#Unscheduled Environment|Unscheduled]]
* Same goes for line return; it helps to see a code block wrapping multiple common instructions instead of having to guess where it starts and stops.
| <!-- Single Player -->
* Do you see the same code multiple times, only with different parameters? Now is the time to write a function!
| <!-- Dedicated Server -->
* 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.
| <!-- Hosted Server -->
* Is your function code far too long? Break it in understandable-sized bites for your own sanity.
| <!-- Multiplayer Client -->
* Finally, camel-casing (namingLikeThis) your variables and commands will naturally make the code more readable, especially for long names.
| <!-- JIP MP Client -->


{{Informative| '''_i''' is an accepted variable standard for a [[for]]..[[do]] iteration}}
|- style="background-color: #95DEF0"
| Persistent functions are called
|
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


See the following code:
|-
_w=[]; {_w pushbackunique primaryweapon _x} foreach((allunits+alldeadmen) select{_x call bis_fnc_objectside==east});
| [[Modules]] are initialised
|
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| {{Icon|unchecked}} <!-- JIP MP Client -->


The same example is far more readable with proper spacing, good variable names and intermediate results:
|- style="background-color: #DEF0AD"
_weaponNames = [];
| [[Event Scripts#initServer.sqf|initServer.sqf]] is executed
_allUnitsAliveAndDead = allUnits + allDeadMen;
| [[Scheduler#Scheduled Environment|Scheduled]]
_allEastAliveAndDead = _allUnitsAliveAndDead select { _x call BIS_fnc_objectSide == east };
| <!-- Single Player -->
{ _weaponNames pushBackUnique primaryWeapon _x } forEach _allEastAliveAndDead;
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| {{n/a}} <!-- Multiplayer Client -->
| {{n/a}} <!-- JIP MP Client -->


==== Constants ====
|- style="background-color: #DEF0AD"
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:
| [[Event Scripts#initPlayerLocal.sqf|initPlayerLocal.sqf]] is executed
a = _x + 1.053;
| [[Scheduler#Scheduled Environment|Scheduled]]
b = _y + 1.053;
| <!-- Single Player -->
And
| {{n/a}} <!-- Dedicated Server -->
_buffer = 1.053;
| <!-- Hosted Server -->
a = _x + _buffer;
| <!-- Multiplayer Client -->
b = _y + _buffer;
| <!-- JIP MP Client -->
Becomes
<span style="color: purple; font-weight: bold;">#define BUFFER 1.053</span> {{codecomment|// note: no semicolon}}
_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 anymore.
|- style="background-color: #DEF0AD"
| [[Event Scripts#initPlayerServer.sqf|initPlayerServer.sqf]] is executed on the server
| [[Scheduler#Scheduled Environment|Scheduled]]
| <!-- Single Player -->
| {{n/a}} <!-- Dedicated Server -->
| ?? <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


=== Optimise then ===
|-
Once you know what is what, you can understand your code better.
| [[Arma 3: Functions Library|Functions]] with <syntaxhighlight lang="cpp" inline>postInit</syntaxhighlight> {{Link|Arma 3: Functions Library#Attributes 3|attribute}} are called
* Use private variables instead of global variables (preceded with an underscore) as much as possible
| [[Scheduler#Scheduled Environment|Scheduled]]<ref name="enginewaits"/>
* You were iterating multiple times on the same array?
| <!-- Single Player -->
** You should be able to spot your issue now.
| <!-- Dedicated Server -->
* Are you using [[execVM]] on the same file, many times?
| <!-- Hosted Server -->
** Store your function in memory to avoid file reading every call with {{Inline code|_myFunction {{=}} [[compile]] [[preprocessFileLineNumbers]] "myFile.sqf";}}
| <!-- Multiplayer Client -->
* Is your variable name far too long?
| <!-- JIP MP Client -->
** 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;


|- style="background-color: #95DEF0"
| [[Event Scripts#init.sqs|init.sqs]] is executed
| [[Scheduler#Scheduled Environment|Scheduled]]
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


== Code optimisation ==
|- style="background-color: #95DEF0"
{{Important|
| [[Event Scripts#init.sqf|init.sqf]] is executed
'''Please note:''' tests and benchmarks were done with the latest {{arma3}} version at the time (v1.82, Tanks DLC). Game engine performance may have changed since.<br />Benchmark result in milliseconds (ms) is an average for '''10000''' iterations.}}
| [[Scheduler#Scheduled Environment|Scheduled]]
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


=== Scheduled and unscheduled environment ===
|-
There are two code environment types, [[Scheduler#Scheduled_Environment|scheduled]] and [[Scheduler#Unscheduled_Environment|unscheduled]].
| [[remoteExec]]'s [[Multiplayer Scripting#Join In Progress|JIP]] queue
* A '''scheduled''' script has an execution time limit of '''3 ms''' before being suspended to the benefit of another script until his turn comes back. It is a bit slower than '''unscheduled''' but [[canSuspend|suspending]] ([[sleep]], [[waitUntil]]) is allowed.
| {{n/a}}
* An '''unscheduled''' script is not watched and will run without limitations. It is recommended for time-critical scripts, but [[canSuspend|suspending]] ([[sleep]], [[waitUntil]]) is '''not''' allowed!
| {{n/a}} <!-- Single Player -->
{{note|See [[Scheduler]] full article for more information.}}
| {{n/a}} <!-- Dedicated Server -->
| {{n/a}} <!-- Hosted Server -->
| {{n/a}} <!-- Multiplayer Client -->
| 42 <!-- JIP MP Client -->


=== Lazy evaluation ===
|- style="background-color: #EEE"
[[private]] _myVar = [33, 66] [[select]] ([[false]]); {{codecomment|// 0.0013 ms}}
| ''Scenario going''
[[private]] _myVar = [[if]] ([[false]]) [[then]] { 33; } [[else]] { 66; }; {{codecomment|// 0.0020 ms}}
| {{n/a}}
[[private]] "_myVar"; [[if]] ([[false]]) [[then]] { _myVar = 33; } [[else]] { _myVar = 66; }; {{codecomment|// 0.0025 ms}}
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


=== Successive condition check ===
|-
In [[SQF syntax|SQF]] the following code will check '''all and every''' condition, even if one fail:
| [[Event Scripts#exit.sqf|exit.sqf]]
[[if]] (condition1 && condition2 && condition3) [[then]] { {{codecomment|/* thenCode */}} };
|
This code will check ''condition1'', and if it is not [[true]] ''condition2'' and ''condition3'' will execute anyway.
| <!-- Single Player -->
To avoid this behaviour, you can either imbricate [[if|ifs]] or use '''lazy evaluation''' such as the following:
| <!-- Dedicated Server -->
[[if]] (condition1 && { condition2 } && { condition3 }) [[then]] { {{codecomment|/* thenCode */}} };
| <!-- Hosted Server -->
This method will stop condition evaluation on the first [[false]] statement.
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


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}}
| [[Event Scripts#exit.sqs|exit.sqs]]
["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}}
| <!-- Single Player -->
["true &nbsp;|| &nbsp;&nbsp;false &nbsp;|| &nbsp;false&nbsp;&nbsp;", [[nil]], 100000] [[call]] [[BIS_fnc_codePerformance]]; {{codecomment|// 0.00128 ms}}
| <!-- Dedicated Server -->
["false || &nbsp;{false} || {false}&nbsp;", [[nil]], 100000] [[call]] [[BIS_fnc_codePerformance]]; {{codecomment|// 0.00200 ms}}
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


=== Concatenating multiple small strings together ===
|-
{{Inline code|myString {{=}} myString + otherString}} works fine for small strings, however the bigger the string gets the slower the operation becomes:
| {{Link|Arma 3: Mission Event Handlers#Ended|"Ended" Mission Event Handler}}
myString = ""; [[for]] "_i" [[from]] 1 [[to]] 10000 [[do]] { myString = myString + "123" }; {{codecomment|// 290 ms}}
|
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


The solution is to use a string array that you will concatenate later:
|-
  strings = [];
| {{Link|Arma 3: Mission Event Handlers#MPEnded|"MPEnded" Mission Event Handler}}
[[for]] "_i" [[from]] 1 [[to]] 10000 [[do]] {strings [[pushBack]] "123"};
|
strings = strings [[joinString]] ""; {{codecomment|// 30 ms}}
| <!-- Single Player -->
| <!-- Dedicated Server -->
| <!-- Hosted Server -->
| <!-- Multiplayer Client -->
| <!-- JIP MP Client -->


=== Manipulating arrays ===
|}


==== Adding elements ====
New commands [[append]] and [[pushBack]] hold the best score.
[[private]] _array = [0,1,2,3]; _array [[append]] [4,5,6]; {{codecomment|// 0.0020 ms}}
[[private]] _array = [0,1,2,3]; _array = _array + [4,5,6]; {{codecomment|// 0.0023 ms}}
[[private]] _array = [0,1,2,3]; { _array [[set]]<nowiki>[</nowiki>[[count]] _array, _x]; } [[forEach]] [4,5,6]; {{codecomment|// 0.0080 ms}}


[[private]] _array = [0,1,2,3]; _array [[pushBack]] 4; {{codecomment|// 0.0016 ms}}
== See Also ==
[[private]] _array = [0,1,2,3]; _array = _array + [4]; {{codecomment|// 0.0021 ms}}
[[private]] _array = [0,1,2,3]; _array [[set]] <nowiki>[</nowiki>[[count]] _array, _x]; {{codecomment|// 0.0022 ms}}


==== Removing elements ====
* [[Arma 3: Functions Library]]<!--
[[private]] _array = [0,1,2,3]; _array [[deleteAt]] 0; {{codecomment|// 0.0015 ms}}
* [[Arma 2: Functions Library]] -->
[[private]] _array = [0,1,2,3]; _array [[set]] [0, [[objNull]]]; _array = _array - <nowiki>[</nowiki>[[objNull]]]; {{codecomment|// 0.0038 ms}}
* [[Arma 3: Remote Execution]], [[BIS_fnc_MP]] <!-- keep? -->
 
* [[Eden Editor: Configuring Attributes|Eden Editor: Configuring Attributes]]
[[private]] _array = [0,1,2,3]; _array [[deleteRange]] [1, 2]; {{codecomment|// 0.0018 ms}}
* [[Event Scripts]]
[[private]] _array = [0,1,2,3]; { _array [[set]] [_x, [[objNull]]] } [[forEach]] [1,2]; _array = _array - <nowiki>[</nowiki>[[objNull]]]; {{codecomment|// 0.0078 ms}}
* [[Scheduler]]
 
=== Multiplayer recommendations ===
* Do not saturate the network with information: [[publicVariable]] or public [[setVariable]] shouldn't be used at high frequency, else '''everyone's performance experience''' is at risk!
* The server is supposed to have a good CPU and a lot of memory, use it: store functions, run them from it, send only the result to the clients
* [[publicVariable]] and [[setVariable]] variable name length impacts network, be sure to send well-named, understandable variables<br /><span style="font-size: 0.9em;">(''and not '''playerNameBecauseThePlayerIsImportantAndWeNeedToKnowWhoTheyAreAllTheTimeEspeciallyInsideThisImpressiveFunction''''')</span>
* Use, use and use [[remoteExec]] &amp; [[remoteExecCall]]. Ditch [[BIS_fnc_MP]] for good!
 
 
<!--
=== createSimpleObject vs createVehicle ===
''TODO''
* [[createSimpleObject]] is over '''43x''' faster than createVehicle!
createVehicle ["Land_VR_Shape_01_cube_1m_F",[0,0,0],[],0,"none"];// ~3.5 ms
createSimpleObject ["a3\structures_f_mark\vr\shapes\vr_shape_01_cube_1m_f.p3d",[0,0,0]];// ~0.08 ms
private _cube = createVehicle ["Land_VR_Shape_01_cube_1m_F",[0,0,0],[],0,"none"]; deleteVehicle _cube; // 244 cycles, 4.11066 ms
private _cube = createSimpleObject ["a3\structures_f_mark\vr\shapes\vr_shape_01_cube_1m_f.p3d",[0,0,0]]; deleteVehicle _cube; // freezes the game for seconds, but 9748 cycles / 0.102585 ms?? Cannot confirm.
* Creating an object at [0,0,0] ''then'' [[setPos]] it will be faster than creating it at the wanted pos (''if spawning obstacles are present? What about "CAN_COLLIDE"?'')
-->
== Equivalent commands performance ==
 
=== if ===
[[if]]..[[then]] { {{codecomment|/* thenCode */}} }; {{codecomment|// 0.0011 ms}}
[[if]]..[[exitWith]] { {{codecomment|/* exitCode */}} }; {{codecomment|// 0.0014 ms}}
[[if]]..[[then]] { {{codecomment|/* thenCode */}} } [[else]] { {{codecomment|/* elseCode */}} }; {{codecomment|// 0.0015 ms}}
[[if]]..[[then]] [{ {{codecomment|/* thenCode */}} },{ {{codecomment|/* elseCode */}} }] {{codecomment|// 0.0016 ms}}
 
=== if and select ===
Use {{Inline code|[array] [[select]] [[Boolean]]}} instead of the lazy-evaluated [[if]].
_result = ["false result", "true result"] [[select]] [[true]]; {{codecomment|// 0.0011 ms}}
_result = [[if]] ([[true]]) [[then]] { "true result"; } [[else]] { "false result"; }; {{codecomment|// 0.0017 ms}}
 
=== if and switch ===
_result = [[call]] {
[[if]] ([[false]]) [[exitWith]] {};
[[if]] ([[false]]) [[exitWith]] {};
[[if]] ([[true]])  [[exitWith]] {};
[[if]] ([[false]]) [[exitWith]] {};
[[if]] ([[false]]) [[exitWith]] {};
}; {{codecomment|// 0.0032 ms}}
 
_result = [[switch]] ([[true]]) [[do]] {
[[case]] ([[false]]): {};
[[case]] ([[false]]): {};
[[case]] ([[true]]): {};
[[case]] ([[false]]): {};
[[case]] ([[false]]): {};
}; {{codecomment|// 0.0047 ms}}
 
=== for ===
The {{Inline code|[[for]]..[[from]]..[[to]]..[[do]]}} is twice as fast as its alternative syntax, {{Inline code|[[for]]..[[do]]}}.
[[for]] "_i" [[from]] 0 [[to]] 10 [[do]] { {{codecomment|/* forCode */}} }; {{codecomment|// 0.015 ms}}
[[for]] [{_i = 0}, {_i < 100}, {_i = _i + 1}] [[do]] { {{codecomment|/* forCode */}} }; {{codecomment|// 0.030 ms}}
 
=== forEach vs count vs findIf ===
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.<br />
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]; {{codecomment|// 0.082 ms}}
{ [[diag_log]] _x } [[forEach]] [1,2,3,4,5]; {{codecomment|// 0.083 ms}}
 
_someoneIsNear = ([[allUnits]] [[findIf]] { _x  [[distance]] [0,0,0] < 1000 }) != -1; {{codecomment|// 0.0046 ms}}
_someoneIsNear = { _x [[distance]] [0,0,0] < 1000 } [[count]] [[allUnits]] > 0; {{codecomment|// 0.0047 ms}}
_someoneIsNear = {
[[if]] ([[_x]] [[distance]] [0,0,0] < 1000) [[exitWith]] { [[true]] };
[[false]]
} [[forEach]] [[allUnits]]; {{codecomment|// 0.0060 ms}}
 
[[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.0050 ms}}
_quantity = { [[_x]] == 2 } [[count]] [0,1,2,3,4,5,6,7,8,9]; {{codecomment|// 0.0114 ms}}
 
=== + and format ===
When concatenating more than two strings, [[format]] is faster than [[valuea_plus_valueb|+]].
[[format]] ["%1%2%3%4%5", "string1", "string2", "string3", "string4", "string5"]; {{codecomment|// 0.0019 ms}}
"string1" + "string2" + "string3" + "string4" + "string5"; {{codecomment|// 0.0021 ms}}
 
=== format and str ===
[[str]] 33; {{codecomment|// 0.0016 ms}}
[[format]] ["%1", 33]; {{codecomment|// 0.0022 ms}}
 
=== private ===
Direct declaration ({{Inline code|[[private]] _var {{=}} value}} since {{arma3}} v1.53) is faster than declaring ''then'' assigning the variable.
[[private]] _a = 1;
[[private]] _b = 2;
[[private]] _c = 3;
[[private]] _d = 4;
{{codecomment|// 0.0023 ms}}
 
[[private]] ["_a", "_b", "_c", "_d"];
_a = 1;
_b = 2;
_c = 3;
_d = 4;
{{codecomment|// 0.0040 ms}}
 
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.
[[private]] ["_a", "_b", "_c", "_d"];
[[for]] "_i" [[from]] 1 [[to]] 10 [[do]]
{
_a = 1; _b = 2; _c = 3; _d = 4;
};
{{codecomment|// 0.0195 ms}}
 
[[for]] "_i" [[from]] 1 [[to]] 10 [[do]]
{
[[private]] _a = 1; [[private]] _b = 2; [[private]] _c = 3; [[private]] _d = 4;
};
{{codecomment|// 0.0235 ms}}
 
=== isNil ===
[[isNil]] "varName"; {{codecomment|// 0.0007 ms}}
[[isNil]] {varName}; {{codecomment|// 0.0012 ms}}
 
=== isEqualType and typeName ===
[[isEqualType]] is much faster than [[typeName]]
"string" [[isEqualType]] 33; {{codecomment|// 0.0006 ms}}
[[typeName]] "string" == [[typeName]] 33; {{codecomment|// 0.0018 ms}}
 
=== objectParent and vehicle ===
[[isNull]] [[objectParent]] [[player]]; {{codecomment|// 0.0013 ms}}
[[vehicle]] [[player]] == [[player]]; {{codecomment|// 0.0022 ms}}
 
=== nearEntities and nearestObjects ===
* [[nearEntities]] is much faster than [[nearestObjects]] given on range and amount of objects within the given range.
If range is over 100 meters it is highly recommended to use [[nearEntities]] over [[nearestObjects]].
 
Note: [[nearEntities]] only searches for [[alive]] objects.
Killed units, destroyed vehicles, static objects and buildings will be ignored by the [[nearEntities]] command.
 
=== Config path delimiter ===
{{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.0023 ms}}
{{note|A config path can be stored in a variable for later use, saving CPU time: ''{{Inline code|_cfgVehicles {{=}} [[configFile]] >> "CfgVehicles"}}'' }}
 
=== getPos* and setPos* ===
[[getPosWorld]] {{codecomment|// 0.0015 ms}}
[[getPosASL]] {{codecomment|// 0.0016 ms}}
[[getPosATL]] {{codecomment|// 0.0016 ms}}
[[getPos]] {{codecomment|// 0.0020 ms}}
[[position]] {{codecomment|// 0.0020 ms}}
[[getPosVisual]] {{codecomment|// 0.0021 ms}}
[[visiblePosition]] {{codecomment|// 0.0021 ms}}
[[getPosASLW]] {{codecomment|// 0.0023 ms}}
 
[[setPosWorld]] {{codecomment|// 0.0060 ms}}
[[setPosASL]] {{codecomment|// 0.0060 ms}}
[[setPosATL]] {{codecomment|// 0.0060 ms}}
[[setPos]] {{codecomment|// 0.0063 ms}}
[[setPosASLW]] {{codecomment|// 0.0068 ms}}
[[setVehiclePosition]] {{codecomment|// 0.0077 ms with "CAN_COLLIDE"}}
{{codecomment|// 0.0390 ms with "NONE"}}
 
 
== Conversion from earlier versions ==
Each iteration of Bohemia games ({{ofp}}, {{arma}}, {{arma2}}, {{tkoh}}, {{arma3}}) brought their own new commands, especially {{arma2}} and {{arma3}}.<br />
For that, if you are converting scripts from older versions of the engine, the following aspects should be reviewed.
 
=== Loops ===
* [[forEach]] loops, depending on the situation, can be replaced by:
** [[apply]]
** [[count]]
** [[findIf]]
** [[select]]
 
=== Array operations ===
* '''Adding an item:''' {{Inline code|myArray [[valuea_plus_valueb|+]] [element]}} and {{Inline code|myArray [[set]] <nowiki>[</nowiki>[[count]] myArray, element]}} have been replaced by [[pushBack]]
* '''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]]
* '''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
** {{Inline code|[[count]] myArray {{=}}{{=}} 0}} is pretty fast, but direct comparison with [[isEqualTo]] is still a little faster: {{Inline code|myArray [[isEqualTo]] []}}
* '''Finding common items:''' [[in]] [[forEach]] loop has been replaced by [[arrayIntersect]]
* '''Condition filtering:''' [[forEach]] can be replaced by [[select]] (alternative syntax)
result = [];
{
[[if]] (_x % 2 == 0) [[then]]
{
result [[pushBack]] [[_x]];
};
} [[forEach]] arrayOfNumbers; {{codecomment|// 2.57 ms}}
result = (arrayOfNumbers [[select]] { _x % 2 == 0 }); {{codecomment|// 1.55 ms}}
 
=== String operations ===
[[String]] manipulation has been simplified with the following commands:
* string [[select]] index
* [[toArray]] and [[toString]] have been ''reinforced'' with [[splitString]] and [[joinString]]
 
=== Type comparison ===
* [[typeName]] has been more than reinforced with [[isEqualType]]
 
=== Multiplayer ===
* [[BIS_fnc_MP]] has been replaced by [[remoteExec]] and [[remoteExecCall]] and internally uses them. Use the engine commands from now on!

Latest revision as of 00:29, 18 March 2024

Order of Initialisation (use column sorting for respective machine order)
Task Exec Environment Machine
Single Player Dedicated Server Hosted Server Multiplayer Client JIP MP Client
Functions with recompile attribute are recompiled N/A 1 1 1 1 1
Functions with preInit attribute are called Unscheduled 2 2 2 2 2
Object Init Event Handlers are called Unscheduled 3 3 3 3 Unchecked
Expressions of Eden Editor entity attributes are called[1] Unscheduled 4 4 4 Unchecked Unchecked
Object initialisation fields are called Unscheduled 5 5 5 4 3
init.sqs is executed 6
init.sqf is executed Scheduled[2]
Expressions of Eden Editor scenario attributes are called[3] Unscheduled
Persistent functions are called
Modules are initialised Unchecked
initServer.sqf is executed Scheduled N/A N/A
initPlayerLocal.sqf is executed Scheduled N/A
initPlayerServer.sqf is executed on the server Scheduled N/A ??
Functions with postInit attribute are called Scheduled[2]
init.sqs is executed Scheduled
init.sqf is executed Scheduled
remoteExec's JIP queue N/A N/A N/A N/A N/A 42
Scenario going N/A
exit.sqf
exit.sqs
"Ended" Mission Event Handler
"MPEnded" Mission Event Handler


See Also

  1. isPlayer _entity does not return true immediately. Once the entity has become a player, it is transferred to the client.
  2. 2.0 2.1 Note in single player that while the environment is Scheduled (canSuspend returns true), the engine seems to wait until the script is done executing, essentially behaving similarly to an Unscheduled environment - infinite loops will freeze the game, uiSleep may pause the game for up to ~20s (postInit), waitUntil can cause catastrophic issues, etc.
  3. player is not available immediately.