-
Notifications
You must be signed in to change notification settings - Fork 0
Optimization Guide
ffredyk edited this page Jul 23, 2026
·
1 revision
Performance tips for SQ# scripts. Based on Arma 3 Code Optimisation best practices, adapted for SQ#'s bytecode VM and scheduler model.
- Make it work — Get correct behavior first
- Make it readable — Clean names, consistent format
- Optimise then — Only after it works and is readable
// SLOW — each + creates a new string (1000 allocations):
_msg = "";
for "_i" from 0 to 999 do {
_msg = _msg + "x";
};
// FAST — build array, join once (1 allocation):
_parts = [];
for "_i" from 0 to 999 do {
_parts pushBack "x";
};
_msg = joinString [_parts, ""];// FAST — mutate in place:
_arr pushBack _item; // O(1) amortized
_arr append _otherArray; // O(n) bulk
// SLOW — creates new array every time:
_arr = _arr + [_item]; // O(n) copy// FAST — for-from-to (native loop):
for "_i" from 0 to (count _arr - 1) do {
_x = _arr select _i;
process(_x);
};
// FAST — forEach (when _x needed):
{ process(_x) } forEach _arr;
// SLOW — while with count every iteration:
_i = 0;
while { _i < count _arr } do { // count called every loop!
_x = _arr select _i;
process(_x);
_i = _i + 1;
};
// FIX — cache the count:
_i = 0;
_count = count _arr;
while { _i < _count } do {
_x = _arr select _i;
process(_x);
_i = _i + 1;
};// SLOW — checks ALL elements:
{ _x == target } count _arr > 0;
// FAST — stops at first match:
(_arr find target) != -1;
// Also fast — forEach with exitWith:
_arr forEach {
if (_x == target) exitWith { _found = true; };
};// SLOW — grows dynamically:
_arr = [];
for "_i" from 0 to 999 do {
_arr pushBack _i; // may reallocate internally
};
// FASTER — pre-allocate:
_arr = [];
_arr resize 1000;
for "_i" from 0 to 999 do {
_arr set [_i, _i]; // no reallocation
};
// FASTEST — just pushBack (amortized O(1)):
_arr = [];
for "_i" from 0 to 999 do {
_arr pushBack _i; // good enough for most cases
};// SLOW — repeated lookups:
for "_i" from 0 to 999 do {
_val = _map get "key"; // hashmap lookup each iteration
process(_val);
};
// FAST — cache the value:
_val = _map get "key"; // one lookup
for "_i" from 0 to 999 do {
process(_val);
};// SLOW for frequent lookups — O(n):
_idx = _arr find _target;
// FAST — O(1):
_val = _map get _target;Use hashmaps when you need frequent key-based lookups. Use arrays for ordered data and iteration.
// SLOW — copy data to every scheduler:
[+_bigArray] spawnOn ["AI", { ... }]; // deep copies entire array
[+_bigArray] spawnOn ["Physics", { ... }]; // copies again
// FAST — freeze once, share everywhere:
_frozen = freeze _bigArray;
[_frozen] spawnOn ["AI", { _local = thaw (_this select 0); ... }];
[_frozen] spawnOn ["Physics", { _local = thaw (_this select 0); ... }];// Check if work is needed before spawning:
if (schedulerLoad 2 > 80) then {
// AI scheduler overloaded — defer work
_deferred pushBack _task;
} else {
_task spawnOn ["AI", { process(_this); }];
};// FAST — compile-time interpolation:
_msg = f"Player {_name} has {_hp} HP";
// SLOWER — runtime parsing:
_msg = format ["Player %1 has %2 HP", _name, _hp];Prefer f-strings when the template is known at compile time. Use format only for dynamic templates.
// BAD:
while { _i < count _arr } do { ... };
// FIX:
_count = count _arr;
while { _i < _count } do { ... };// BAD:
_msg = "";
{ _msg = _msg + str _x + "," } forEach _arr;
// FIX:
_parts = [];
{ _parts pushBack str _x } forEach _arr;
_msg = joinString [_parts, ","];// BAD:
{ _arr = _arr + [_x] } forEach _items;
// FIX:
_arr append _items;// BAD — busy-wait:
while { !scriptDone _handle } do { sleep 0.01; };
// GOOD:
_result = await _handle;Use diag_tickTime to measure:
_start = diag_tickTime;
// ... code to measure ...
_elapsed = diag_tickTime - _start;
diag_log f"Operation took {_elapsed * 1000}ms";- Strings — string handling and performance
- Arrays & Collections — array operations
- Concurrency — scheduler load management
- Thread Safety — frozen array sharing
- diag_tickTime — monotonic timer for benchmarking
- Getting Started
- Language Guide
- Type System
- Control Flow
- Functions & Code
- Strings & Text
- Arrays & Collections
- Concurrency
- Promise System
- Thread Safety
- Host API & Embedding
- CLI Tool
- Bytecode Reference
- Multiplayer
- Syntax Sugar
- Optimization Guide
- Benchmarks
- count
- select
- pushBack
- append
- deleteAt
- deleteRange
- resize
- reverse
- sort
- find
- in
- forEach
- freeze
- thaw
- isFrozen
- currentScheduler
- clientOwner
- allSchedulers
- schedulerName
- schedulerExists
- schedulerBudget
- setSchedulerBudget
- fiberCount
- readyFiberCount
- waitingFiberCount
- schedulerLoad
- sendTo