{"name":"Code Optimisation","title":"Code Optimisation","type":"topic","url":"https://community.bistudio.com/wiki/Code_Optimisation","revid":378687,"updated":"2026-05-26T00:40:51Z","aliases":[],"markdown":"> [!NOTE]\n> This page is about Code Optimisation. For *conception* optimisation, see Mission Optimisation.\n\nThis article will try to be a general guide about improving your code **and** its performance.\n- The first part (Rules) focuses on having a clean, readable and maintainable code.\n- The second part (Code optimisation) is about **improving performance**, sometimes trading it against code readability.\n- 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.\n- The fourth part (Conversion from earlier versions) is a hopefully helpful, short guide about useful new commands or syntaxes to replace the old ways.\n\n### Rules\n\nIn 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.\n\nWith that being said, here are the three basic rules to get yourself in the clear:\n\n1. Make it work\n1. Make it readable\n1. Optimise then\n\n#### Make It Work\n\n> Premature optimization is the root of all evil.\n\nYour 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.\n\n- 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 30% damage*.\n- Use -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 thanks to this Wiki.\n- Read your Arma RPT (report) to read more details about the error that happened in your code.\n\n#### Make It Readable\n\nWhether you are cleaning your code or a different person's, you must understand the code without twisting your brain:\n\n- While 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.\n- *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.\n- Indentation is important for the human mind, and space is too. Space is free, use it.\n- 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.\n- Do you see the same code multiple times, only with different parameters? Now is the time to write a function!\n- 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.\n- Is your function code far too long? Break it in understandable-sized bites for your own sanity.\n- Finally, camel-casing (namingLikeThis) your variables will naturally make the code more readable, especially for long names.\n\n> [!NOTE]\n> See **Code Best Practices** for more information.\n\nSee the following code:\n _w=[]; {_w pushbackunique primaryweapon _x} foreach((allunits+alldeadmen) select{_x call BIS_fnc_objectside==east});\n\nThe same example is far more readable with proper spacing, good variable names and intermediate results:\n _weaponNames = [];\n _allUnitsAliveAndDead = allUnits + allDeadMen;\n _allEastAliveAndDead = _allUnitsAliveAndDead select { _x call BIS_fnc_objectSide == east };\n { _weaponNames pushBackUnique primaryWeapon _x } forEach _allEastAliveAndDead;\n\n##### Constants\n\nUsing a hard-coded constant more than once? Use preprocessor directives rather than storing it in memory or cluttering your code with numbers. Such as:\n\n```sqf\na = _x + 1.053;\nb = _y + 1.053;\n```\n\nAnd\n\n```sqf\n_buffer = 1.053;\na = _x + _buffer;\nb = _y + _buffer;\n```\n\nBecomes\n\n```sqf\n#define BUFFER 1.053 // note: no semicolon\n_a = _x + BUFFER;\n_b = _y + BUFFER;\n```\n\n> [!NOTE]\n> Using the `#define` macro only works within the current SQF *file*. Such definition will not propagate anywhere else.\n\n**Global** \"constants\" can be defined via a Description.ext declaration, though, and accessed using getMissionConfigValue command:\n\nDeclaration in Description.ext:\n\n```cpp\nvar1 = 123;\nvar2 = \"123\";\nvar3[] = { 1, 2, 3 };\nrand = __EVAL(random 999);\n```\n\nUsage in code:\n\n```sqf\nhint str getMissionConfigValue \"var1\"; // 123                                    // 0.0007 ms\nhint str getMissionConfigValue \"var2\"; // \"123\"                                    // 0.0008 ms\nhint str getMissionConfigValue \"var3\"; // [1,2,3]                                // 0.0017 ms\nhint str getMissionConfigValue \"rand\"; // constant random, for example 935.038    // 0.0007 ms\n```\n\nThe getMissionConfigValue command searching Description.ext from top to bottom,\nit is better for a matter of performance to put all your definitions at the top of the file.\n\n#### Optimise Then\n\nOnce you know what is what, you can understand your code better.\n- Use private variables instead of global variables (preceded by an underscore) as much as possible.\n- You were iterating multiple times on the same array?\n  - You should be able to spot your issue now.\n- Are you using execVM on the same file, many times?\n  - Store your function in memory to avoid file reading every call ( e.g `_myFunction = compile preprocessFileLineNumbers \"myFile.sqf\";`).\n- Is your variable name far too long?\n  - Find a smaller name, according to the variable scope;e.g:\n\n```sqf\n{ _opforUnitUniform = uniform _x; systemChat _opforUnitUniform; } forEach _allOpforUnits;\n```\n\nbecomes\n\n```sqf\n{ _uniform = uniform _x; systemChat _uniform; } forEach _allOpforUnits;\n```\n\n### Code Optimisation\n\n> [!IMPORTANT]\n> **Please note:** Tests and benchmarks were done with the latest Arma 3 version at the time Arma 3 v1.82 with **Tank DLC**. Game engine performance may have changed since.\n> Benchmark result in milliseconds (ms) is an average for **10000** iterations.\n\n> [!NOTE]\n> ● means you **must** change your ways today, *or with us you will ride…*\n>\n> ● means you may want to look at it if you are targeting pure performance\n>\n> ● 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.\n\n#### Scheduled and Unscheduled Environment\n\nThere are two code environment types, scheduled and unscheduled.\n- A **scheduled** script has an execution time limit of **3 ms** before being suspended to the benefit of another script until its turn comes back. It is a bit slower than unscheduled, but suspending (sleep, waitUntil) is allowed.\n- An **unscheduled** script is not watched and will run without limitations. It is recommended for time-critical scripts, but suspending (sleep, waitUntil) is **not** allowed!\n\n> [!NOTE]\n> See Scheduler for more information.\n\n#### ● Variable Assignment\n\n```sqf\nprivate _myVar = [33, 66] select false;                                            // 0.0013 ms\nprivate _myVar = if (false) then { 33; } else { 66; };                            // 0.0020 ms\nprivate \"_myVar\"; if (false) then { _myVar = 33; } else { _myVar = 66; };         // 0.0025 ms\n```\n\n#### ● Lazy Evaluation\n\nIn SQF the following code will evaluate every single condition, even if one fails:\n\n```sqf\nif (a && b && c) then {};\n```\n\nEven if `a` returns false (and thus the entire Boolean expression can no longer become true), `b` and `c` will still be executed and evaluated regardless.\n\nTo avoid this behaviour, one can either imbricate if statements or use **lazy evaluation**. The latter is done like so:\n\n```sqf\nif (a && { b && { c } }) then {};\n```\n\nIn the example above, condition evaluation stops once any condition evaluates to false.\n\n##### Influence on Semantics\n\nDepending on the arrangement of the curly brackets, lazy evaluation can change the precedence (and therefore the semantics) of a condition. Consider this example:\n\n| Expression | Condition Value | Evaluated Conditions | Result |  |  |\n|---|---|---|---|---|---|\n| `a` | `b` | `c` |  |  |  |\n| ```sqf<br>a &&  b  \\|\\|  c<br>``` | false | any Boolean | true | `a`, `b`, `c` | true |\n| ```sqf<br>a && {b} \\|\\| {c}<br>``` | `a`, `c` | true |  |  |  |\n| ```sqf<br>a && {b  \\|\\| {c}}<br>``` | `a` | false |  |  |  |\n\n##### Performance\n\nUsing lazy evaluation is not always the best way as it can both speed up and slow down the code, depending on the current condition being evaluated:\n\n```sqf\n[\"true  || { false  || {false}}\", nil, 100000] call BIS_fnc_codePerformance;    // 0.00080 ms\n[\"true  ||  {false} || {false} \", nil, 100000] call BIS_fnc_codePerformance;    // 0.00105 ms\n[\"false ||   false  ||  false  \", nil, 100000] call BIS_fnc_codePerformance;    // 0.00123 ms\n[\"true  ||   false  ||  false  \", nil, 100000] call BIS_fnc_codePerformance;    // 0.00128 ms\n[\"false ||  {false} || {false} \", nil, 100000] call BIS_fnc_codePerformance;    // 0.00200 ms\n```\n\n#### ● Concatenating Strings\n\n`myString = myString + otherString` works fine for small strings, however the bigger the string gets the slower the operation becomes:\n\n```sqf\nmyString = \"\"; for \"_i\" from 1 to 10000 do { myString = myString + \"123\" };        // 290 ms\n```\n\nThe solution is to use a string array that you will concatenate later:\n\n```sqf\nstrings = [];\nfor \"_i\" from 1 to 10000 do { strings pushBack \"123\" };\nstrings = strings joinString \"\";                                                // 30 ms\n```\n\n#### ● Array Manipulation\n\n##### Add Elements\n\nNew commands append and pushBack hold the best score.\n\n```sqf\n_array = [0,1,2,3]; _array append [4,5,6];                                        // 0.0020 ms\n_array = [0,1,2,3]; _array = _array + [4,5,6];                                    // 0.0023 ms\n_array = [0,1,2,3]; { _array set [count _array, _x]; } forEach [4,5,6];            // 0.0080 ms\n```\n\n```sqf\n_array = [0,1,2,3]; _array pushBack 4;                                            // 0.0016 ms\n_array = [0,1,2,3]; _array = _array + [4];                                        // 0.0021 ms\n_array = [0,1,2,3]; _array set [count _array, _x];                                 // 0.0022 ms\n```\n\n##### Iterate Elements\n\n**If _x is not required**, for is twice as fast as forEach - otherwise, use forEach (see results below).\n\n```sqf\nprivate _list = allUnits;                                                                // 64 units                256 units\n\n// if the amount of loops is known\nfor \"_i\" from 0 to 63 do {};                                                            // 0.0120 ms            0.0460 ms\nfor \"_i\" from 0 to count _list -1 do {};                                                // 0.0170 ms            0.0480 ms\nfor \"_i\" from 0 to count _list -1 do { private _x = _list select _i };                    // 0.0500 ms            0.1896 ms <\nfor \"_i\" from 0 to count _list -1 do { private _x = _list select _i; _x setDamage 0 };    // 0.107 ms                0.39 ms\n\n{} forEach _list;                                                                        // 0.0250 ms            0.098 ms\n{ _x setDamage 0 } forEach _list;                                                        // 0.0770 ms            0.312 ms\n\n_list apply {};                                                                            // 0.0250 ms            0.098 ms\n_list apply { _x setDamage 0 };                                                            // 0.0770 ms            0.312 ms\n\nprivate _i = 0;\nwhile { _i < 64 } do { _i = _i + 1; };                                                    // 0.048 ms                0.200 ms\n\n// counts array every loop\nprivate _i = 0;\nwhile { _i < count _list } do { _i = _i + 1; };                                            // 0.062 ms                0.247 ms\n\nprivate _i = 0;\nwhile { _i < 64 } do { private _x = _list select _i; _i = _i + 1; };                    // 0.086 ms                0.42 ms\n```\n\n##### Remove Elements\n\n```sqf\n_array = [0,1,2,3]; _array deleteAt 0;                                                                    // 0.0015 ms\n_array = [0,1,2,3]; _array set [0, objNull]; _array = _array - [objNull];                                // 0.0038 ms\n```\n\n```sqf\n_array = [0,1,2,3]; _array deleteRange [1, 2];                                                            // 0.0018 ms\n_array = [0,1,2,3]; { _array set [_x, objNull] } forEach [1,2]; _array = _array - [objNull];            // 0.0078 ms\n```\n\n#### ● Multiplayer Recommendations\n\n- 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!\n- 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\n- publicVariable and setVariable variable name length impacts network, be sure to send well-named, understandable variables\n(*and not **playerNameBecauseThePlayerIsImportantAndWeNeedToKnowWhoTheyAreAllTheTimeEspeciallyInsideThisImpressiveFunction***)\n- Use, use and use remoteExec & remoteExecCall. Ditch BIS_fnc_MP for good!\n\n> [!NOTE]\n> See Multiplayer Scripting for more information.\n\n### Equivalent Commands Performance\n\n#### ● call\n\ncall without arguments is faster than call with arguments:\n\n```sqf\ncall {};                                // 0.0007 ms\n123 call {};                            // 0.0013 ms\n```\n\nSince the variables defined in the parent scope will be available in the called child scope, it could be possible to speed up the code by avoiding passing arguments all together, for example writing:\n\n```sqf\nplayer addEventHandler [\"HandleDamage\", { call my_fnc_damage }];\n```\n\ninstead of:\n\n```sqf\nplayer addEventHandler [\"HandleDamage\", { _this call my_fnc_damage }];\n```\n\n#### ● execVM and call\n\n> [!IMPORTANT]\n> Using execVM multiple times make the game read the file and recompile it every time.\n>\n> If you use the script more than once, store its code in a variable or better, make it a Function!\n\n```sqf\n// myFile.sqf is an EMPTY file\nprivate _myFunction = compile preprocessFileLineNumbers \"myFile.sqf\"; // compile time is done only once\ncall _myFunction;                // 0.0009 ms\nexecVM \"myFile.sqf\";            // 0.275 ms\n\n// myFile.sqf is BIS_fnc_showRespawnMenu\nprivate _myFunction = compile preprocessFileLineNumbers \"myFile.sqf\"; // compile time is done only once\n[\"close\"] call _myFunction;        // 0.0056 ms\n[\"close\"] execVM \"myFile.sqf\";    // 0.506 ms\n```\n\n#### ● loadFile, preprocessFile and preprocessFileLineNumbers\n\n```sqf\n// myFile.sqf is an empty file\nloadFile \"myFile.sqf\";                    // 0.219 ms\npreprocessFile \"myFile.sqf\";            // 0.353 ms\npreprocessFileLineNumbers \"myFile.sqf\";    // 0.355 ms\n```\n\n```sqf\n// myFile.sqf is BIS_fnc_showRespawnMenu\nloadFile \"myFile.sqf\";                    // 0.3516 ms\npreprocessFile \"myFile.sqf\";            // 2.75 ms\npreprocessFileLineNumbers \"myFile.sqf\"; // 2.73 ms\n```\n\n```sqf\n// myFile.sqf is a missing file\nloadFile \"myFile.sqf\";                    // 0.692 ms\npreprocessFile \"myFile.sqf\";            // 0.6225 ms\npreprocessFileLineNumbers \"myFile.sqf\";    // 0.6225 ms\n```\n\n> [!IMPORTANT]\n> The comparison of loadFile with preprocessFile* is not exactly fair as loadFile doesn't preprocess the file's content.\n>\n> On the other hand, the loaded file cannot contain any `//` or `/* */` comments nor any preprocessor instructions (including debug information like line numbers or file information).\n\n#### ● if\n\n```sqf\nif (condition) then { /* thenCode */ };                                    // 0.0011 ms\nif (condition) exitWith { /* exitCode */ };                                // 0.0014 ms\nif (condition) then { /* thenCode */ } else { /* elseCode */ };            // 0.0015 ms\nif (condition) then [{ /* thenCode */ }, { /* elseCode */ }];            // 0.0016 ms\n```\n\n#### ● if and select\n\nUse `[array] select boolean` instead of the lazy-evaluated if.\n\n```sqf\n_result = [\"false result\", \"true result\"] select true;                    // 0.0011 ms\n_result = if (true) then { \"true result\"; } else { \"false result\"; };    // 0.0017 ms\n```\n\n#### ● if and switch\n\n```sqf\n_result = call {\n    if (false) exitWith {};\n    if (false) exitWith {};\n    if (true)  exitWith {};\n    if (false) exitWith {};\n    if (false) exitWith {};\n};                            // 0.0032 ms\n```\n\n```sqf\n_result = switch (true) do {\n    case (false): {};\n    case (false): {};\n    case (true) : {};\n    case (false): {};\n    case (false): {};\n};                            // 0.0047 ms\n```\n\n#### ● if else and switch\n\n```sqf\n_mode = \"killed\";\n\nswitch _mode do {\n    case \"init\": {};\n    case \"killed\": {};\n    case \"respawned\": {};\n};                            // 0.0019 ms\n```\n\n```sqf\n_mode = \"killed\";\n\nif (_mode == \"init\") then {} else {\n    if (_mode == \"killed\") then {} else {\n        if (_mode == \"respawned\") then {};\n    };\n};                            // 0.0019 ms\n```\n\n#### ● in vs find\n\n```sqf\n// String search\n\"bar\" in \"foobar\"                                    // 0.0008 ms\n\"foobar\" find \"bar\" > -1                            // 0.0012 ms\n```\n\n```sqf\n// array search - case-sensitive\n\"bar\" in [\"foo\", \"Bar\", \"bar\", \"BAR\"];                // 0.0012 ms\n[\"foo\", \"Bar\", \"bar\", \"BAR\"] find \"bar\" > -1;        // 0.0016 ms\n```\n\n#### ● for\n\nThe `for..from..to..do` is twice as fast as its alternative syntax, `for..do`.\n\n```sqf\nfor \"_i\" from 0 to 10 do { /* forCode */ };                                        // 0.015 ms\nfor [{ _i = 0 }, { _i < 100 }, { _i = _i + 1 }] do { /* forCode */ };            // 0.030 ms\n```\n\n#### ● forEach vs count vs findIf\n\nBoth forEach and count commands will step through *all* the array elements and both commands will contain reference to current element with the _x variable.\nHowever, count loop is a little faster than forEach loop, but it does not benefit from the _forEachIndex variable.\n\nAlso, there is a limitation as the code inside count expects Boolean or Nothing while the command itself returns Number.\nThis limitation is very important if you try to replace your forEach by count. If you have to add an extra true/false/nil at the end to make count work, it will be slower than the forEach equivalent.\n\n```sqf\n{ diag_log _x } count   [1,2,3,4,5];                                            // 0.082 ms\n{ diag_log _x } forEach [1,2,3,4,5];                                            // 0.083 ms\n```\n\n```sqf\n// with an empty array\n_someoneIsNear = (allUnits findIf { _x  distance [0,0,0] < 1000 }) != -1;        // 0.0046 ms\n_someoneIsNear = { _x distance [0,0,0] < 1000 } count allUnits > 0;                // 0.0047 ms <\n_someoneIsNear = {\n    if (_x distance [0,0,0] < 1000) exitWith { true };\n    false\n} forEach allUnits;                                                                // 0.0060 ms\n```\n\n```sqf\n// with a 30 items array\n_someoneIsNear = (allUnits findIf { _x  distance [0,0,0] < 1000 }) != -1;        // 0.0275 ms\n_someoneIsNear = { _x distance [0,0,0] < 1000 } count allUnits > 0;                // 0.0645 ms <\n_someoneIsNear = {\n    if (_x distance [0,0,0] < 1000) exitWith { true };\n    false\n} forEach allUnits;                                                                // 0.0390 ms\n```\n\n#### ● findIf\n\nfindIf stops array iteration as soon as the condition is met.\n\n```sqf\n[0,1,2,3,4,5,6,7,8,9] findIf { _x == 2 };                                        // 0.0050 ms\n{ if (_x == 2) exitWith { _forEachIndex; }; } forEach [0,1,2,3,4,5,6,7,8,9];    // 0.0078 ms\n_quantity = { _x == 2 } count [0,1,2,3,4,5,6,7,8,9];                            // 0.0114 ms\n```\n\n#### ● format vs str\n\n```sqf\nstr 33;                // 0.0016 ms\nformat [\"%1\", 33];    // 0.0022 ms\n```\n\n#### ● + vs format vs joinString\n\nnon-String data:\n\n```sqf\n[33, 45, 78] joinString \"\";                // 0.0052 ms - no length limit\nformat [\"%1%2%3\", 33, 45, 78];            // 0.0054 ms - limited to ~8Kb\nstr 33 + str 45 + str 78;                // 0.0059 ms - no length limit\n```\n\nString data:\n\n```sqf\n[\"str1\", \"str2\", \"str3\"] joinString \"\";        // 0.0015 ms - no length limit\nformat [\"%1%2%3\", \"str1\", \"str2\", \"str3\"];    // 0.0015 ms - limited to ~8Kb\n\"str1\" + \"str2\" + \"str3\";                    // 0.0012 ms - no length limit\n```\n\n#### ● private\n\nDirect declaration (`private _var = value`) is faster than declaring *then* assigning the variable.\n\n```sqf\nprivate _a = 1;\nprivate _b = 2;\nprivate _c = 3;\nprivate _d = 4;\n// 0.0023 ms\n```\n\n```sqf\nprivate [\"_a\", \"_b\", \"_c\", \"_d\"];\n_a = 1;\n_b = 2;\n_c = 3;\n_d = 4;\n// 0.0040 ms\n```\n\nHowever, if you have to reuse the same variable in a loop, external declaration is faster.\n\nThe reason behind this is that a declaration in the loop will create, assign and delete the variable in each loop.\n\nAn external declaration creates the variable only once and the loop only assigns the value.\n\n```sqf\nprivate [\"_a\", \"_b\", \"_c\", \"_d\"];\nfor \"_i\" from 1 to 10 do\n{\n    _a = 1; _b = 2; _c = 3; _d = 4;\n};\n// 0.0195 ms\n```\n\n```sqf\nfor \"_i\" from 1 to 10 do\n{\n    private _a = 1; private _b = 2; private _c = 3; private _d = 4;\n};\n// 0.0235 ms\n```\n\n#### ● isNil\n\n```sqf\nisNil \"varName\";                    // 0.0007 ms\nisNil { varName };                    // 0.0012 ms\n```\n\n#### ● isEqualType and typeName\n\nisEqualType is much faster than typeName\n\n```sqf\n\"string\" isEqualType 33;            // 0.0006 ms\ntypeName \"string\" == typeName 33;    // 0.0018 ms\n```\n\n#### ● isEqualTo and count\n\n```sqf\n// with a items array\nallUnits isEqualTo [];                // 0.0040 ms\ncount allUnits == 0;                // 0.0043 ms\n```\n\n#### ● select and param\n\n```sqf\n[1,2,3] select 0;                    // 0.0008 ms\n[1,2,3] param [0];                    // 0.0011 ms\n```\n\n#### ● createSimpleObject vs createVehicle\n\n```sqf\n// createSimpleObject is over 43× faster than createVehicle!\ndeleteVehicle createSimpleObject [\"a3\\structures_f_mark\\vr\\shapes\\vr_shape_01_cube_1m_f.p3d\", [0,0,0]];    // ~0.08 ms\ndeleteVehicle createSimpleObject [\"Land_VR_Shape_01_cube_1m_F\", [0,0,0]];                                // ~3.2 ms\ndeleteVehicle createVehicle [\"Land_VR_Shape_01_cube_1m_F\", [0,0,0], [], 0, \"CAN_COLLIDE\"];                // ~2.7 ms\ndeleteVehicle createVehicle [\"Land_VR_Shape_01_cube_1m_F\", [0,0,0], [], 0, \"NONE\"];                        // ~78 ms\n```\n\n#### ● objectParent and vehicle\n\n```sqf\nisNull objectParent player;            // 0.0013 ms\nvehicle player == player;            // 0.0022 ms\n```\n\n#### ● nearEntities and nearestObjects\n\nnearEntities is much faster than nearestObjects given on range and amount of objects within the given range.\nIf range is over 100 meters it is highly recommended to use nearEntities over nearestObjects.\n\n```sqf\n// tested with a NATO rifle squad amongst solar power plant panels on Altis at coordinates [20762,15837]\ngetPosATL player nearEntities [[\"CAManBase\"], 50];        // 0.0075 ms\nnearestObjects [getPosATL player, [\"CAManBase\"], 50];    // 0.0145 ms\n```\n\n> [!IMPORTANT]\n> nearEntities only searches for alive objects and on-foot soldiers.\n>\n> In-vehicle units, killed units, destroyed vehicles, static objects and buildings will be ignored.\n\n#### ● Global Variables vs Local Variables\n\nIf you need to use global variable repeatedly in a loop, copy its value to local variable and use local variable instead:\n\n```sqf\nSomeGlobalVariable = [123];\nfor \"_i\" from 1 to 100 do\n{\n    SomeGlobalVariable select 0;\n};\n// 0.13 ms\n```\n\nis noticeably slower than\n\n```sqf\nSomeGlobalVariable = [123];\nprivate _var = SomeGlobalVariable;\nfor \"_i\" from 1 to 100 do\n{\n    _var select 0;\n};\n// 0.08 ms\n```\n\n#### ● Config path delimiter\n\n`>>` is slightly faster than `/` when used in config path with configFile or missionConfigFile.\n\n```sqf\nconfigFile >> \"CfgVehicles\";        // 0.0019 ms\nconfigFile  / \"CfgVehicles\";        // 0.0023 ms\n```\n\n> [!NOTE]\n> A config path can be stored in a variable for later use, saving CPU time: `_cfgVehicles = configFile >> \"CfgVehicles\"`.\n\n#### ● getPos* and setPos*\n\n```sqf\ngetPosWorld                            // 0.0015 ms\ngetPosASL                            // 0.0016 ms\ngetPosATL                            // 0.0016 ms\ngetPosASLW                            // 0.0023 ms\ngetPos                                // 0.0030-0.0300 ms; performance depends on where this command is used - see its documentation\nposition                            // same as getPos\ngetPosVisual                        // same as getPos\nvisiblePosition                        // same as getPos\n```\n\n```sqf\nsetPosWorld                            // 0.0060 ms\nsetPosASL                            // 0.0060 ms\nsetPosATL                            // 0.0060 ms\nsetPos                                // 0.0063 ms\nsetPosASLW                            // 0.0068 ms\nsetVehiclePosition                    // 0.0077 ms with \"CAN_COLLIDE\"\n                                    // 0.0390 ms with \"NONE\"\n```\n\n#### ● toLower/toUpper vs toLowerANSI/toUpperANSI\n\n```sqf\n// _myString is a 100 chars \"aAaAaA(…)\" string\ntoLowerANSI _myString;                // 0.0006 ms\ntoLower _myString;                    // 0.0016 ms\n\ntoUpperANSI _myString;                // 0.0006 ms\ntoUpper _myString;                    // 0.0016 ms\n```\n\n### Equivalent Data Structures Performance\n\n#### Key-Value Data Structures\n\n```sqf\nprivate _hashMap = createHashMapFromArray [[\"id\", 123], [\"name\", \"player name\"], [\"unit\", player]]; // since Arma 3 v2.02\nprivate _goodFormat = [[\"id\", \"name\", \"unit\"], [123, \"player name\", player]];\nprivate _slowFormat = [[\"id\", 123], [\"name\", \"player name\"], [\"unit\", player]];\n```\n\n```sqf\nprivate _name = _hashMap get \"name\"; // 0.0018ms\n\n// this takes 0.0038ms:\nprivate _index = _goodFormat select 0 find \"name\"; // loop in engine\nprivate _name = _goodFormat select 1 select _index;\n\n// this takes 0.0116ms:\nprivate _index = _slowFormat findIf { _x select 0 == \"name\" }; // loop in script\nprivate _name = _slowFormat select _index select 1;\n```\n\n> [!NOTE]\n> Database Functions use a slow format.\n\n### Conversion From Earlier Versions\n\nEach iteration of Bohemia games (Operation Flashpoint, Armed Assault, Arma 2, Take On Helicopters, Arma 3) brought their own new commands, especially Arma 2 and Arma 3.\n\nFor that, if you are converting scripts from older versions of the engine, the following aspects should be reviewed.\n\n#### Loops\n\n- forEach loops, depending on the situation, can be replaced by:\n  - apply\n  - count\n  - findIf\n  - select\n\n#### Array Operations\n\n- **Adding an item:** `myArray + [element]` and `myArray set [count myArray, element]` have been replaced by pushBack\n- **Selecting a random item:** BIS_fnc_selectRandom has been replaced by selectRandom\n- **Removing items:** `myArray set [1, objNull]; myArray - [objNull]` has been replaced by deleteAt and deleteRange\n- **Concatenating:** `myArray = myArray + [element]` has been *reinforced* with append: if you don't need the original array to be modified, use +\n- **Comparing:** use isEqualTo instead of BIS_fnc_areEqual\n- **Finding common items:** in forEach loop has been replaced by arrayIntersect\n- **Condition filtering:** forEach can be replaced by select (alternative syntax)\n\n```sqf\nresult = (arrayOfNumbers select { _x % 2 == 0 });    // 1.55 ms\n```\n\n```sqf\nresult = [];\n{\n    if (_x % 2 == 0) then { result pushBack _x; };\n} forEach arrayOfNumbers;                            // 2.57 ms\n```\n\n#### Vector Operations\n\n- BIS_fnc_vectorMultiply has been replaced by vectorMultiply (at least 6x faster)\n- BIS_fnc_vectorDivide too, to an extent (see its page for more information)\n\n```sqf\nprivate _vector = [102, 687, 1543];\nprivate _factor = 53;\n\n_vector vectorMultiply _factor;                    // 0.0028 ms\n[_vector, _factor] call BIS_fnc_vectorMultiply;    // 0.0145 ms\n\n_vector vectorMultiply (1 / _factor);            // 0.003 ms - but beware of 0 divisor\n[_vector, _factor] call BIS_fnc_vectorDivide;    // 0.017 ms\n```\n\n#### String Operations\n\nString manipulation has been simplified with the following commands:\n- alternative syntax for select: `string select index`\n- toArray and toString have been *reinforced* with splitString and joinString\n\n#### Number Operations\n\n- BIS_fnc_linearConversion has been replaced by linearConversion. The command is **9 times faster**.\n- BIS_fnc_selectRandomWeighted has been replaced by selectRandomWeighted. The command is **7 times faster**.\n\n#### Type Comparison\n\n- typeName has been more than *reinforced* with isEqualType.\n\n#### Multiplayer\n\n- BIS_fnc_MP has been replaced by remoteExec and remoteExecCall and internally uses them. Use the engine commands from now on!\n\n> [!NOTE]\n> See also Multiplayer Scripting.\n\n#### Parameters\n\n- BIS_fnc_param has been replaced by param and params. The commands are approximately **14 times** faster. Use them!","sections":[{"heading":null,"level":1,"markdown":"> [!NOTE]\n> This page is about Code Optimisation. For *conception* optimisation, see Mission Optimisation.\n\nThis article will try to be a general guide about improving your code **and** its performance.\n- The first part (Rules) focuses on having a clean, readable and maintainable code.\n- The second part (Code optimisation) is about **improving performance**, sometimes trading it against code readability.\n- 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.\n- The fourth part (Conversion from earlier versions) is a hopefully helpful, short guide about useful new commands or syntaxes to replace the old ways."},{"heading":"Rules","level":3,"markdown":"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.\n\nWith that being said, here are the three basic rules to get yourself in the clear:\n\n1. Make it work\n1. Make it readable\n1. Optimise then"},{"heading":"Make It Work","level":4,"markdown":"> Premature optimization is the root of all evil.\n\nYour 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.\n\n- 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 30% damage*.\n- Use -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 thanks to this Wiki.\n- Read your Arma RPT (report) to read more details about the error that happened in your code."},{"heading":"Make It Readable","level":4,"markdown":"Whether you are cleaning your code or a different person's, you must understand the code without twisting your brain:\n\n- While 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.\n- *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.\n- Indentation is important for the human mind, and space is too. Space is free, use it.\n- 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.\n- Do you see the same code multiple times, only with different parameters? Now is the time to write a function!\n- 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.\n- Is your function code far too long? Break it in understandable-sized bites for your own sanity.\n- Finally, camel-casing (namingLikeThis) your variables will naturally make the code more readable, especially for long names.\n\n> [!NOTE]\n> See **Code Best Practices** for more information.\n\nSee the following code:\n _w=[]; {_w pushbackunique primaryweapon _x} foreach((allunits+alldeadmen) select{_x call BIS_fnc_objectside==east});\n\nThe same example is far more readable with proper spacing, good variable names and intermediate results:\n _weaponNames = [];\n _allUnitsAliveAndDead = allUnits + allDeadMen;\n _allEastAliveAndDead = _allUnitsAliveAndDead select { _x call BIS_fnc_objectSide == east };\n { _weaponNames pushBackUnique primaryWeapon _x } forEach _allEastAliveAndDead;"},{"heading":"Constants","level":5,"markdown":"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:\n\n```sqf\na = _x + 1.053;\nb = _y + 1.053;\n```\n\nAnd\n\n```sqf\n_buffer = 1.053;\na = _x + _buffer;\nb = _y + _buffer;\n```\n\nBecomes\n\n```sqf\n#define BUFFER 1.053 // note: no semicolon\n_a = _x + BUFFER;\n_b = _y + BUFFER;\n```\n\n> [!NOTE]\n> Using the `#define` macro only works within the current SQF *file*. Such definition will not propagate anywhere else.\n\n**Global** \"constants\" can be defined via a Description.ext declaration, though, and accessed using getMissionConfigValue command:\n\nDeclaration in Description.ext:\n\n```cpp\nvar1 = 123;\nvar2 = \"123\";\nvar3[] = { 1, 2, 3 };\nrand = __EVAL(random 999);\n```\n\nUsage in code:\n\n```sqf\nhint str getMissionConfigValue \"var1\"; // 123                                    // 0.0007 ms\nhint str getMissionConfigValue \"var2\"; // \"123\"                                    // 0.0008 ms\nhint str getMissionConfigValue \"var3\"; // [1,2,3]                                // 0.0017 ms\nhint str getMissionConfigValue \"rand\"; // constant random, for example 935.038    // 0.0007 ms\n```\n\nThe getMissionConfigValue command searching Description.ext from top to bottom,\nit is better for a matter of performance to put all your definitions at the top of the file."},{"heading":"Optimise Then","level":4,"markdown":"Once you know what is what, you can understand your code better.\n- Use private variables instead of global variables (preceded by an underscore) as much as possible.\n- You were iterating multiple times on the same array?\n  - You should be able to spot your issue now.\n- Are you using execVM on the same file, many times?\n  - Store your function in memory to avoid file reading every call ( e.g `_myFunction = compile preprocessFileLineNumbers \"myFile.sqf\";`).\n- Is your variable name far too long?\n  - Find a smaller name, according to the variable scope;e.g:\n\n```sqf\n{ _opforUnitUniform = uniform _x; systemChat _opforUnitUniform; } forEach _allOpforUnits;\n```\n\nbecomes\n\n```sqf\n{ _uniform = uniform _x; systemChat _uniform; } forEach _allOpforUnits;\n```"},{"heading":"Code Optimisation","level":3,"markdown":"> [!IMPORTANT]\n> **Please note:** Tests and benchmarks were done with the latest Arma 3 version at the time Arma 3 v1.82 with **Tank DLC**. Game engine performance may have changed since.\n> Benchmark result in milliseconds (ms) is an average for **10000** iterations.\n\n> [!NOTE]\n> ● means you **must** change your ways today, *or with us you will ride…*\n>\n> ● means you may want to look at it if you are targeting pure performance\n>\n> ● 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."},{"heading":"Scheduled and Unscheduled Environment","level":4,"markdown":"There are two code environment types, scheduled and unscheduled.\n- A **scheduled** script has an execution time limit of **3 ms** before being suspended to the benefit of another script until its turn comes back. It is a bit slower than unscheduled, but suspending (sleep, waitUntil) is allowed.\n- An **unscheduled** script is not watched and will run without limitations. It is recommended for time-critical scripts, but suspending (sleep, waitUntil) is **not** allowed!\n\n> [!NOTE]\n> See Scheduler for more information."},{"heading":"● Variable Assignment","level":4,"markdown":"```sqf\nprivate _myVar = [33, 66] select false;                                            // 0.0013 ms\nprivate _myVar = if (false) then { 33; } else { 66; };                            // 0.0020 ms\nprivate \"_myVar\"; if (false) then { _myVar = 33; } else { _myVar = 66; };         // 0.0025 ms\n```"},{"heading":"● Lazy Evaluation","level":4,"markdown":"In SQF the following code will evaluate every single condition, even if one fails:\n\n```sqf\nif (a && b && c) then {};\n```\n\nEven if `a` returns false (and thus the entire Boolean expression can no longer become true), `b` and `c` will still be executed and evaluated regardless.\n\nTo avoid this behaviour, one can either imbricate if statements or use **lazy evaluation**. The latter is done like so:\n\n```sqf\nif (a && { b && { c } }) then {};\n```\n\nIn the example above, condition evaluation stops once any condition evaluates to false."},{"heading":"Influence on Semantics","level":5,"markdown":"Depending on the arrangement of the curly brackets, lazy evaluation can change the precedence (and therefore the semantics) of a condition. Consider this example:\n\n| Expression | Condition Value | Evaluated Conditions | Result |  |  |\n|---|---|---|---|---|---|\n| `a` | `b` | `c` |  |  |  |\n| ```sqf<br>a &&  b  \\|\\|  c<br>``` | false | any Boolean | true | `a`, `b`, `c` | true |\n| ```sqf<br>a && {b} \\|\\| {c}<br>``` | `a`, `c` | true |  |  |  |\n| ```sqf<br>a && {b  \\|\\| {c}}<br>``` | `a` | false |  |  |  |"},{"heading":"Performance","level":5,"markdown":"Using lazy evaluation is not always the best way as it can both speed up and slow down the code, depending on the current condition being evaluated:\n\n```sqf\n[\"true  || { false  || {false}}\", nil, 100000] call BIS_fnc_codePerformance;    // 0.00080 ms\n[\"true  ||  {false} || {false} \", nil, 100000] call BIS_fnc_codePerformance;    // 0.00105 ms\n[\"false ||   false  ||  false  \", nil, 100000] call BIS_fnc_codePerformance;    // 0.00123 ms\n[\"true  ||   false  ||  false  \", nil, 100000] call BIS_fnc_codePerformance;    // 0.00128 ms\n[\"false ||  {false} || {false} \", nil, 100000] call BIS_fnc_codePerformance;    // 0.00200 ms\n```"},{"heading":"● Concatenating Strings","level":4,"markdown":"`myString = myString + otherString` works fine for small strings, however the bigger the string gets the slower the operation becomes:\n\n```sqf\nmyString = \"\"; for \"_i\" from 1 to 10000 do { myString = myString + \"123\" };        // 290 ms\n```\n\nThe solution is to use a string array that you will concatenate later:\n\n```sqf\nstrings = [];\nfor \"_i\" from 1 to 10000 do { strings pushBack \"123\" };\nstrings = strings joinString \"\";                                                // 30 ms\n```"},{"heading":"● Array Manipulation","level":4,"markdown":""},{"heading":"Add Elements","level":5,"markdown":"New commands append and pushBack hold the best score.\n\n```sqf\n_array = [0,1,2,3]; _array append [4,5,6];                                        // 0.0020 ms\n_array = [0,1,2,3]; _array = _array + [4,5,6];                                    // 0.0023 ms\n_array = [0,1,2,3]; { _array set [count _array, _x]; } forEach [4,5,6];            // 0.0080 ms\n```\n\n```sqf\n_array = [0,1,2,3]; _array pushBack 4;                                            // 0.0016 ms\n_array = [0,1,2,3]; _array = _array + [4];                                        // 0.0021 ms\n_array = [0,1,2,3]; _array set [count _array, _x];                                 // 0.0022 ms\n```"},{"heading":"Iterate Elements","level":5,"markdown":"**If _x is not required**, for is twice as fast as forEach - otherwise, use forEach (see results below).\n\n```sqf\nprivate _list = allUnits;                                                                // 64 units                256 units\n\n// if the amount of loops is known\nfor \"_i\" from 0 to 63 do {};                                                            // 0.0120 ms            0.0460 ms\nfor \"_i\" from 0 to count _list -1 do {};                                                // 0.0170 ms            0.0480 ms\nfor \"_i\" from 0 to count _list -1 do { private _x = _list select _i };                    // 0.0500 ms            0.1896 ms <\nfor \"_i\" from 0 to count _list -1 do { private _x = _list select _i; _x setDamage 0 };    // 0.107 ms                0.39 ms\n\n{} forEach _list;                                                                        // 0.0250 ms            0.098 ms\n{ _x setDamage 0 } forEach _list;                                                        // 0.0770 ms            0.312 ms\n\n_list apply {};                                                                            // 0.0250 ms            0.098 ms\n_list apply { _x setDamage 0 };                                                            // 0.0770 ms            0.312 ms\n\nprivate _i = 0;\nwhile { _i < 64 } do { _i = _i + 1; };                                                    // 0.048 ms                0.200 ms\n\n// counts array every loop\nprivate _i = 0;\nwhile { _i < count _list } do { _i = _i + 1; };                                            // 0.062 ms                0.247 ms\n\nprivate _i = 0;\nwhile { _i < 64 } do { private _x = _list select _i; _i = _i + 1; };                    // 0.086 ms                0.42 ms\n```"},{"heading":"Remove Elements","level":5,"markdown":"```sqf\n_array = [0,1,2,3]; _array deleteAt 0;                                                                    // 0.0015 ms\n_array = [0,1,2,3]; _array set [0, objNull]; _array = _array - [objNull];                                // 0.0038 ms\n```\n\n```sqf\n_array = [0,1,2,3]; _array deleteRange [1, 2];                                                            // 0.0018 ms\n_array = [0,1,2,3]; { _array set [_x, objNull] } forEach [1,2]; _array = _array - [objNull];            // 0.0078 ms\n```"},{"heading":"● Multiplayer Recommendations","level":4,"markdown":"- 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!\n- 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\n- publicVariable and setVariable variable name length impacts network, be sure to send well-named, understandable variables\n(*and not **playerNameBecauseThePlayerIsImportantAndWeNeedToKnowWhoTheyAreAllTheTimeEspeciallyInsideThisImpressiveFunction***)\n- Use, use and use remoteExec & remoteExecCall. Ditch BIS_fnc_MP for good!\n\n> [!NOTE]\n> See Multiplayer Scripting for more information."},{"heading":"Equivalent Commands Performance","level":3,"markdown":""},{"heading":"● call","level":4,"markdown":"call without arguments is faster than call with arguments:\n\n```sqf\ncall {};                                // 0.0007 ms\n123 call {};                            // 0.0013 ms\n```\n\nSince the variables defined in the parent scope will be available in the called child scope, it could be possible to speed up the code by avoiding passing arguments all together, for example writing:\n\n```sqf\nplayer addEventHandler [\"HandleDamage\", { call my_fnc_damage }];\n```\n\ninstead of:\n\n```sqf\nplayer addEventHandler [\"HandleDamage\", { _this call my_fnc_damage }];\n```"},{"heading":"● execVM and call","level":4,"markdown":"> [!IMPORTANT]\n> Using execVM multiple times make the game read the file and recompile it every time.\n>\n> If you use the script more than once, store its code in a variable or better, make it a Function!\n\n```sqf\n// myFile.sqf is an EMPTY file\nprivate _myFunction = compile preprocessFileLineNumbers \"myFile.sqf\"; // compile time is done only once\ncall _myFunction;                // 0.0009 ms\nexecVM \"myFile.sqf\";            // 0.275 ms\n\n// myFile.sqf is BIS_fnc_showRespawnMenu\nprivate _myFunction = compile preprocessFileLineNumbers \"myFile.sqf\"; // compile time is done only once\n[\"close\"] call _myFunction;        // 0.0056 ms\n[\"close\"] execVM \"myFile.sqf\";    // 0.506 ms\n```"},{"heading":"● loadFile, preprocessFile and preprocessFileLineNumbers","level":4,"markdown":"```sqf\n// myFile.sqf is an empty file\nloadFile \"myFile.sqf\";                    // 0.219 ms\npreprocessFile \"myFile.sqf\";            // 0.353 ms\npreprocessFileLineNumbers \"myFile.sqf\";    // 0.355 ms\n```\n\n```sqf\n// myFile.sqf is BIS_fnc_showRespawnMenu\nloadFile \"myFile.sqf\";                    // 0.3516 ms\npreprocessFile \"myFile.sqf\";            // 2.75 ms\npreprocessFileLineNumbers \"myFile.sqf\"; // 2.73 ms\n```\n\n```sqf\n// myFile.sqf is a missing file\nloadFile \"myFile.sqf\";                    // 0.692 ms\npreprocessFile \"myFile.sqf\";            // 0.6225 ms\npreprocessFileLineNumbers \"myFile.sqf\";    // 0.6225 ms\n```\n\n> [!IMPORTANT]\n> The comparison of loadFile with preprocessFile* is not exactly fair as loadFile doesn't preprocess the file's content.\n>\n> On the other hand, the loaded file cannot contain any `//` or `/* */` comments nor any preprocessor instructions (including debug information like line numbers or file information)."},{"heading":"● if","level":4,"markdown":"```sqf\nif (condition) then { /* thenCode */ };                                    // 0.0011 ms\nif (condition) exitWith { /* exitCode */ };                                // 0.0014 ms\nif (condition) then { /* thenCode */ } else { /* elseCode */ };            // 0.0015 ms\nif (condition) then [{ /* thenCode */ }, { /* elseCode */ }];            // 0.0016 ms\n```"},{"heading":"● if and select","level":4,"markdown":"Use `[array] select boolean` instead of the lazy-evaluated if.\n\n```sqf\n_result = [\"false result\", \"true result\"] select true;                    // 0.0011 ms\n_result = if (true) then { \"true result\"; } else { \"false result\"; };    // 0.0017 ms\n```"},{"heading":"● if and switch","level":4,"markdown":"```sqf\n_result = call {\n    if (false) exitWith {};\n    if (false) exitWith {};\n    if (true)  exitWith {};\n    if (false) exitWith {};\n    if (false) exitWith {};\n};                            // 0.0032 ms\n```\n\n```sqf\n_result = switch (true) do {\n    case (false): {};\n    case (false): {};\n    case (true) : {};\n    case (false): {};\n    case (false): {};\n};                            // 0.0047 ms\n```"},{"heading":"● if else and switch","level":4,"markdown":"```sqf\n_mode = \"killed\";\n\nswitch _mode do {\n    case \"init\": {};\n    case \"killed\": {};\n    case \"respawned\": {};\n};                            // 0.0019 ms\n```\n\n```sqf\n_mode = \"killed\";\n\nif (_mode == \"init\") then {} else {\n    if (_mode == \"killed\") then {} else {\n        if (_mode == \"respawned\") then {};\n    };\n};                            // 0.0019 ms\n```"},{"heading":"● in vs find","level":4,"markdown":"```sqf\n// String search\n\"bar\" in \"foobar\"                                    // 0.0008 ms\n\"foobar\" find \"bar\" > -1                            // 0.0012 ms\n```\n\n```sqf\n// array search - case-sensitive\n\"bar\" in [\"foo\", \"Bar\", \"bar\", \"BAR\"];                // 0.0012 ms\n[\"foo\", \"Bar\", \"bar\", \"BAR\"] find \"bar\" > -1;        // 0.0016 ms\n```"},{"heading":"● for","level":4,"markdown":"The `for..from..to..do` is twice as fast as its alternative syntax, `for..do`.\n\n```sqf\nfor \"_i\" from 0 to 10 do { /* forCode */ };                                        // 0.015 ms\nfor [{ _i = 0 }, { _i < 100 }, { _i = _i + 1 }] do { /* forCode */ };            // 0.030 ms\n```"},{"heading":"● forEach vs count vs findIf","level":4,"markdown":"Both forEach and count commands will step through *all* the array elements and both commands will contain reference to current element with the _x variable.\nHowever, count loop is a little faster than forEach loop, but it does not benefit from the _forEachIndex variable.\n\nAlso, there is a limitation as the code inside count expects Boolean or Nothing while the command itself returns Number.\nThis limitation is very important if you try to replace your forEach by count. If you have to add an extra true/false/nil at the end to make count work, it will be slower than the forEach equivalent.\n\n```sqf\n{ diag_log _x } count   [1,2,3,4,5];                                            // 0.082 ms\n{ diag_log _x } forEach [1,2,3,4,5];                                            // 0.083 ms\n```\n\n```sqf\n// with an empty array\n_someoneIsNear = (allUnits findIf { _x  distance [0,0,0] < 1000 }) != -1;        // 0.0046 ms\n_someoneIsNear = { _x distance [0,0,0] < 1000 } count allUnits > 0;                // 0.0047 ms <\n_someoneIsNear = {\n    if (_x distance [0,0,0] < 1000) exitWith { true };\n    false\n} forEach allUnits;                                                                // 0.0060 ms\n```\n\n```sqf\n// with a 30 items array\n_someoneIsNear = (allUnits findIf { _x  distance [0,0,0] < 1000 }) != -1;        // 0.0275 ms\n_someoneIsNear = { _x distance [0,0,0] < 1000 } count allUnits > 0;                // 0.0645 ms <\n_someoneIsNear = {\n    if (_x distance [0,0,0] < 1000) exitWith { true };\n    false\n} forEach allUnits;                                                                // 0.0390 ms\n```"},{"heading":"● findIf","level":4,"markdown":"findIf stops array iteration as soon as the condition is met.\n\n```sqf\n[0,1,2,3,4,5,6,7,8,9] findIf { _x == 2 };                                        // 0.0050 ms\n{ if (_x == 2) exitWith { _forEachIndex; }; } forEach [0,1,2,3,4,5,6,7,8,9];    // 0.0078 ms\n_quantity = { _x == 2 } count [0,1,2,3,4,5,6,7,8,9];                            // 0.0114 ms\n```"},{"heading":"● format vs str","level":4,"markdown":"```sqf\nstr 33;                // 0.0016 ms\nformat [\"%1\", 33];    // 0.0022 ms\n```"},{"heading":"● + vs format vs joinString","level":4,"markdown":"non-String data:\n\n```sqf\n[33, 45, 78] joinString \"\";                // 0.0052 ms - no length limit\nformat [\"%1%2%3\", 33, 45, 78];            // 0.0054 ms - limited to ~8Kb\nstr 33 + str 45 + str 78;                // 0.0059 ms - no length limit\n```\n\nString data:\n\n```sqf\n[\"str1\", \"str2\", \"str3\"] joinString \"\";        // 0.0015 ms - no length limit\nformat [\"%1%2%3\", \"str1\", \"str2\", \"str3\"];    // 0.0015 ms - limited to ~8Kb\n\"str1\" + \"str2\" + \"str3\";                    // 0.0012 ms - no length limit\n```"},{"heading":"● private","level":4,"markdown":"Direct declaration (`private _var = value`) is faster than declaring *then* assigning the variable.\n\n```sqf\nprivate _a = 1;\nprivate _b = 2;\nprivate _c = 3;\nprivate _d = 4;\n// 0.0023 ms\n```\n\n```sqf\nprivate [\"_a\", \"_b\", \"_c\", \"_d\"];\n_a = 1;\n_b = 2;\n_c = 3;\n_d = 4;\n// 0.0040 ms\n```\n\nHowever, if you have to reuse the same variable in a loop, external declaration is faster.\n\nThe reason behind this is that a declaration in the loop will create, assign and delete the variable in each loop.\n\nAn external declaration creates the variable only once and the loop only assigns the value.\n\n```sqf\nprivate [\"_a\", \"_b\", \"_c\", \"_d\"];\nfor \"_i\" from 1 to 10 do\n{\n    _a = 1; _b = 2; _c = 3; _d = 4;\n};\n// 0.0195 ms\n```\n\n```sqf\nfor \"_i\" from 1 to 10 do\n{\n    private _a = 1; private _b = 2; private _c = 3; private _d = 4;\n};\n// 0.0235 ms\n```"},{"heading":"● isNil","level":4,"markdown":"```sqf\nisNil \"varName\";                    // 0.0007 ms\nisNil { varName };                    // 0.0012 ms\n```"},{"heading":"● isEqualType and typeName","level":4,"markdown":"isEqualType is much faster than typeName\n\n```sqf\n\"string\" isEqualType 33;            // 0.0006 ms\ntypeName \"string\" == typeName 33;    // 0.0018 ms\n```"},{"heading":"● isEqualTo and count","level":4,"markdown":"```sqf\n// with a items array\nallUnits isEqualTo [];                // 0.0040 ms\ncount allUnits == 0;                // 0.0043 ms\n```"},{"heading":"● select and param","level":4,"markdown":"```sqf\n[1,2,3] select 0;                    // 0.0008 ms\n[1,2,3] param [0];                    // 0.0011 ms\n```"},{"heading":"● createSimpleObject vs createVehicle","level":4,"markdown":"```sqf\n// createSimpleObject is over 43× faster than createVehicle!\ndeleteVehicle createSimpleObject [\"a3\\structures_f_mark\\vr\\shapes\\vr_shape_01_cube_1m_f.p3d\", [0,0,0]];    // ~0.08 ms\ndeleteVehicle createSimpleObject [\"Land_VR_Shape_01_cube_1m_F\", [0,0,0]];                                // ~3.2 ms\ndeleteVehicle createVehicle [\"Land_VR_Shape_01_cube_1m_F\", [0,0,0], [], 0, \"CAN_COLLIDE\"];                // ~2.7 ms\ndeleteVehicle createVehicle [\"Land_VR_Shape_01_cube_1m_F\", [0,0,0], [], 0, \"NONE\"];                        // ~78 ms\n```"},{"heading":"● objectParent and vehicle","level":4,"markdown":"```sqf\nisNull objectParent player;            // 0.0013 ms\nvehicle player == player;            // 0.0022 ms\n```"},{"heading":"● nearEntities and nearestObjects","level":4,"markdown":"nearEntities is much faster than nearestObjects given on range and amount of objects within the given range.\nIf range is over 100 meters it is highly recommended to use nearEntities over nearestObjects.\n\n```sqf\n// tested with a NATO rifle squad amongst solar power plant panels on Altis at coordinates [20762,15837]\ngetPosATL player nearEntities [[\"CAManBase\"], 50];        // 0.0075 ms\nnearestObjects [getPosATL player, [\"CAManBase\"], 50];    // 0.0145 ms\n```\n\n> [!IMPORTANT]\n> nearEntities only searches for alive objects and on-foot soldiers.\n>\n> In-vehicle units, killed units, destroyed vehicles, static objects and buildings will be ignored."},{"heading":"● Global Variables vs Local Variables","level":4,"markdown":"If you need to use global variable repeatedly in a loop, copy its value to local variable and use local variable instead:\n\n```sqf\nSomeGlobalVariable = [123];\nfor \"_i\" from 1 to 100 do\n{\n    SomeGlobalVariable select 0;\n};\n// 0.13 ms\n```\n\nis noticeably slower than\n\n```sqf\nSomeGlobalVariable = [123];\nprivate _var = SomeGlobalVariable;\nfor \"_i\" from 1 to 100 do\n{\n    _var select 0;\n};\n// 0.08 ms\n```"},{"heading":"● Config path delimiter","level":4,"markdown":"`>>` is slightly faster than `/` when used in config path with configFile or missionConfigFile.\n\n```sqf\nconfigFile >> \"CfgVehicles\";        // 0.0019 ms\nconfigFile  / \"CfgVehicles\";        // 0.0023 ms\n```\n\n> [!NOTE]\n> A config path can be stored in a variable for later use, saving CPU time: `_cfgVehicles = configFile >> \"CfgVehicles\"`."},{"heading":"● getPos* and setPos*","level":4,"markdown":"```sqf\ngetPosWorld                            // 0.0015 ms\ngetPosASL                            // 0.0016 ms\ngetPosATL                            // 0.0016 ms\ngetPosASLW                            // 0.0023 ms\ngetPos                                // 0.0030-0.0300 ms; performance depends on where this command is used - see its documentation\nposition                            // same as getPos\ngetPosVisual                        // same as getPos\nvisiblePosition                        // same as getPos\n```\n\n```sqf\nsetPosWorld                            // 0.0060 ms\nsetPosASL                            // 0.0060 ms\nsetPosATL                            // 0.0060 ms\nsetPos                                // 0.0063 ms\nsetPosASLW                            // 0.0068 ms\nsetVehiclePosition                    // 0.0077 ms with \"CAN_COLLIDE\"\n                                    // 0.0390 ms with \"NONE\"\n```"},{"heading":"● toLower/toUpper vs toLowerANSI/toUpperANSI","level":4,"markdown":"```sqf\n// _myString is a 100 chars \"aAaAaA(…)\" string\ntoLowerANSI _myString;                // 0.0006 ms\ntoLower _myString;                    // 0.0016 ms\n\ntoUpperANSI _myString;                // 0.0006 ms\ntoUpper _myString;                    // 0.0016 ms\n```"},{"heading":"Equivalent Data Structures Performance","level":3,"markdown":""},{"heading":"Key-Value Data Structures","level":4,"markdown":"```sqf\nprivate _hashMap = createHashMapFromArray [[\"id\", 123], [\"name\", \"player name\"], [\"unit\", player]]; // since Arma 3 v2.02\nprivate _goodFormat = [[\"id\", \"name\", \"unit\"], [123, \"player name\", player]];\nprivate _slowFormat = [[\"id\", 123], [\"name\", \"player name\"], [\"unit\", player]];\n```\n\n```sqf\nprivate _name = _hashMap get \"name\"; // 0.0018ms\n\n// this takes 0.0038ms:\nprivate _index = _goodFormat select 0 find \"name\"; // loop in engine\nprivate _name = _goodFormat select 1 select _index;\n\n// this takes 0.0116ms:\nprivate _index = _slowFormat findIf { _x select 0 == \"name\" }; // loop in script\nprivate _name = _slowFormat select _index select 1;\n```\n\n> [!NOTE]\n> Database Functions use a slow format."},{"heading":"Conversion From Earlier Versions","level":3,"markdown":"Each iteration of Bohemia games (Operation Flashpoint, Armed Assault, Arma 2, Take On Helicopters, Arma 3) brought their own new commands, especially Arma 2 and Arma 3.\n\nFor that, if you are converting scripts from older versions of the engine, the following aspects should be reviewed."},{"heading":"Loops","level":4,"markdown":"- forEach loops, depending on the situation, can be replaced by:\n  - apply\n  - count\n  - findIf\n  - select"},{"heading":"Array Operations","level":4,"markdown":"- **Adding an item:** `myArray + [element]` and `myArray set [count myArray, element]` have been replaced by pushBack\n- **Selecting a random item:** BIS_fnc_selectRandom has been replaced by selectRandom\n- **Removing items:** `myArray set [1, objNull]; myArray - [objNull]` has been replaced by deleteAt and deleteRange\n- **Concatenating:** `myArray = myArray + [element]` has been *reinforced* with append: if you don't need the original array to be modified, use +\n- **Comparing:** use isEqualTo instead of BIS_fnc_areEqual\n- **Finding common items:** in forEach loop has been replaced by arrayIntersect\n- **Condition filtering:** forEach can be replaced by select (alternative syntax)\n\n```sqf\nresult = (arrayOfNumbers select { _x % 2 == 0 });    // 1.55 ms\n```\n\n```sqf\nresult = [];\n{\n    if (_x % 2 == 0) then { result pushBack _x; };\n} forEach arrayOfNumbers;                            // 2.57 ms\n```"},{"heading":"Vector Operations","level":4,"markdown":"- BIS_fnc_vectorMultiply has been replaced by vectorMultiply (at least 6x faster)\n- BIS_fnc_vectorDivide too, to an extent (see its page for more information)\n\n```sqf\nprivate _vector = [102, 687, 1543];\nprivate _factor = 53;\n\n_vector vectorMultiply _factor;                    // 0.0028 ms\n[_vector, _factor] call BIS_fnc_vectorMultiply;    // 0.0145 ms\n\n_vector vectorMultiply (1 / _factor);            // 0.003 ms - but beware of 0 divisor\n[_vector, _factor] call BIS_fnc_vectorDivide;    // 0.017 ms\n```"},{"heading":"String Operations","level":4,"markdown":"String manipulation has been simplified with the following commands:\n- alternative syntax for select: `string select index`\n- toArray and toString have been *reinforced* with splitString and joinString"},{"heading":"Number Operations","level":4,"markdown":"- BIS_fnc_linearConversion has been replaced by linearConversion. The command is **9 times faster**.\n- BIS_fnc_selectRandomWeighted has been replaced by selectRandomWeighted. The command is **7 times faster**."},{"heading":"Type Comparison","level":4,"markdown":"- typeName has been more than *reinforced* with isEqualType."},{"heading":"Multiplayer","level":4,"markdown":"- BIS_fnc_MP has been replaced by remoteExec and remoteExecCall and internally uses them. Use the engine commands from now on!\n\n> [!NOTE]\n> See also Multiplayer Scripting."},{"heading":"Parameters","level":4,"markdown":"- BIS_fnc_param has been replaced by param and params. The commands are approximately **14 times** faster. Use them!"}],"categories":["Arma Scripting Tutorials"],"groups":["Scripting"]}