Jump to content
Vandeanson

Code optimization, multiple threads of waituntil and spawn

Recommended Posts

57 minutes ago, pierremgi said:

only when needed?

What do you mean? 

Share this post


Link to post
Share on other sites
11 hours ago, Vandeanson said:

tried to further slim down the PosCheck:

It now errors because _spawnPos is a string if no players were found.

Move the isFlatEmpty check into the if statement too. Btw nice idea to just remove the waitUntil completely, I didn't see that.
while true loop with exitWith is basically same as waitUntil.

 

11 hours ago, Vandeanson said:

_closePl >= 1

You only want to check if atleast one? You can use findIf then and not have to iterate the whole array with count.

 

11 hours ago, Vandeanson said:

if (_farAwayPlayers == _allplayers1cnt)

Both these variables are undefined, atleast in the code snippet you sent. They are defined in the waitUntil scope but not outside of it.

 

Also please start using the private keyword, it makes your scripts faster and safer.

Share this post


Link to post
Share on other sites
2 hours ago, Dedmen said:

It now errors because _spawnPos is a string if no players were found.

Thanks, i didnt realize that! 

 

2 hours ago, Dedmen said:

Move the isFlatEmpty check into the if statement too

Ok will check, that would indeed be nice! 

 

 

2 hours ago, Dedmen said:

Both these variables are undefined, atleast in the code snippet you sent. They are defined in the waitUntil scope but not outside of it.

 

Also please start using the private keyword, it makes your scripts faster and safer.

 

No worries, i took that to heart. I am defining these early in the code, for example:

Private _allplayers = "" ;

So much more convenient to code with private:) 

 

 

2 hours ago, Dedmen said:
13 hours ago, Vandeanson said:

_closePl >= 1

You only want to check if atleast one? You can use findIf then and not have to iterate the whole array with count

 

Ok will check. I forgot i could check for >= 0 or maybe != -1 with findif, to find out if it has found a matching player. 

 

Thanks! 

Share this post


Link to post
Share on other sites
2 hours ago, Dedmen said:

It now errors because _spawnPos is a string if no players were found.

 

Oh, i guess defining 

 

private _spawnPos = [] ;

 

Instead of... ="" ;

 

by default would fix that. 

Share this post


Link to post
Share on other sites
On 5/9/2019 at 10:01 PM, pierremgi said:

So play testing your scenario and in debug console:

- in watch lines:  count diag_ActiveSQFScripts (you don't need the Diagnostics.exe!)  

- and in console run: copyToClipboard str diag_ActiveSQFScripts

Did this for a while now and found it quite informative. Really glad you told me about it, gave me a better overview of whats going on and helped with some issues. It also made me integrate parts of externslized functions (IED spawn, AI spawn, Loot spawn,....) back into the main spawners. This way i can have separate functions for these things but i could avoid having separate cleanup code/waituntils. Hence for each spawner script, previously, 5-6 active scripts where in a loop/on hold which is now reduced to 2 per sitespawn.

Share this post


Link to post
Share on other sites

I've been experimenting a bit with optimization and I found out I could spawn this script for 65 markers (that means 65 times) without even loosing 1 FPSs.


Here is the script:

Spoiler

 


//SPAWN FOOT PATROL ON MARKER ["marker name", spawn distance, patrol area] execVM "spawn\spawnUnitOnce.sqf";
if (!isServer) exitWith {};

params [
	["_mkrName", "", [""]],
	["_spawnDistance", 1000, [0]],
	["_patrolArea", 250, [0]],
	["_searchBuildings", false, [true]]
];

_debug = false;
_spawnPos = getMarkerPos _mkrName;

sleep (10 + (random 10));
waitUntil {
sleep (2 + (random 4)); 
private _closestPlayer = _spawnPos call closestPlayer;
(_closestPlayer distance2D _spawnPos) < _spawnDistance};

_side = createCenter EAST;
private _footPatGrp = createGroup east;
//_footPatGrp setGroupId [_mkrName];
_groupSize = selectRandom [2,3,4,5,6,7,8];
if (alivePriest) then {_groupSize = _groupSize + 3};

for [{_i = 0},{_i < _groupSize},{_i = _i + 1}] do {
	_enemyType = selectRandom ["O_Soldier_F","O_Soldier_AR_F","O_helipilot_F","O_soldier_exp_F","O_Soldier_GL_F","O_soldier_M_F","O_medic_F","O_soldier_repair_F","O_Soldier_LAT_F","O_Soldier_lite_F"];
	_enemyUnit = [_spawnPos, 0, _enemyType, _footPatGrp] call BIS_fnc_spawnVehicle;
};

sleep (2 + (random 4));

//Enable Group Optimizations
_footPatGrp enableDynamicSimulation true;
_footPatGrp deleteGroupWhenEmpty true;

// Set Group Behavior
_footPatGrp setFormation (selectRandom ["FILE","LINE","COLUMN","STAG COLUMN", "WEDGE", "ECH LEFT", "ECH RIGHT", "VEE", "DIAMOND"]);
_footPatGrp setSpeedMode "LIMITED";
_footPatGrp setCombatMode "YELLOW";
_footPatGrp setBehaviour "SAFE";

// Get Leader Ready
_grpLeader = leader _footPatGrp;
_grpLeader addPrimaryWeaponItem "acc_flashlight";
_grpLeader enableGunLights "forceOn";
// Leader Gets the Patrol Area Script
[_grpLeader, _patrolArea, 1, true] execVM "scripts\hz\foot\hz_footAreaPatrol.sqf";

/*	------------------------------------------------------------------------------------------
	Define if the group is composed of Bandits, Mercenaries or Cultists.
	Group 1: Bandits
	Group 2: Hired Mercs
	Group 3: Cultists
	------------------------------------------------------------------------------------------	*/
_grpType = floor random [0, 5, 10];

// Give each unit its Event Handlers.
{[_x, _grpType] execVM "scripts\hz\foot\hz_setEnemy.sqf";} forEach units _footPatGrp;

 

 

So waitUntils with simple conditions and Ai units with dynamic simulation seem to be very optimized currently.

 

In fact in all my latest experiments it is the objects spawned by these scripts the reason behind the big negative FPS impacts. Even more so than even the most demanding scripts.

 

For example Ravage, with all modules placed and running and its default settings it is using (taking away) barely 8 fps.

 

And for all the things that Ravage does under the hood having just an 8 fps impact is quite low for the +300 in immersion that you get from it! Specially considering that switching from Altis to Malden hurts my FPSs far more (-20).

 

So far I am inclined to say that it is up to BI to finally fix the way objects are rendered in Arma 3. You can clearly see that the issue is fixed in DayZ where I get 120 FPS while I get 80 on Altis and 60 on Malden.

 

Another example. I just recently switched all my scripts into functions format, took me quite a long time and it netted me perhaps +2 FPSs at most. Then I reduced the objects being spawned by the functions and even on EDEN by 50% and I got 20+ FPSs back!

 

So my final conclusion: I know that you spawn many objects and units with your scritps, and that is probably hurting performance A LOT more than any scripting/coding inefficiency you might have.

  • Like 1

Share this post


Link to post
Share on other sites
4 hours ago, LSValmont said:


Here is the script:

  Reveal hidden contents

 



//SPAWN FOOT PATROL ON MARKER ["marker name", spawn distance, patrol area] execVM "spawn\spawnUnitOnce.sqf";
if (!isServer) exitWith {};

params [
	["_mkrName", "", [""]],
	["_spawnDistance", 1000, [0]],
	["_patrolArea", 250, [0]],
	["_searchBuildings", false, [true]]
];

_debug = false;
_spawnPos = getMarkerPos _mkrName;

sleep (10 + (random 10));
waitUntil {
sleep (2 + (random 4)); 
private _closestPlayer = _spawnPos call closestPlayer;
(_closestPlayer distance2D _spawnPos) < _spawnDistance};

_side = createCenter EAST;
private _footPatGrp = createGroup east;
//_footPatGrp setGroupId [_mkrName];
_groupSize = selectRandom [2,3,4,5,6,7,8];
if (alivePriest) then {_groupSize = _groupSize + 3};

for [{_i = 0},{_i < _groupSize},{_i = _i + 1}] do {
	_enemyType = selectRandom ["O_Soldier_F","O_Soldier_AR_F","O_helipilot_F","O_soldier_exp_F","O_Soldier_GL_F","O_soldier_M_F","O_medic_F","O_soldier_repair_F","O_Soldier_LAT_F","O_Soldier_lite_F"];
	_enemyUnit = [_spawnPos, 0, _enemyType, _footPatGrp] call BIS_fnc_spawnVehicle;
};

sleep (2 + (random 4));

//Enable Group Optimizations
_footPatGrp enableDynamicSimulation true;
_footPatGrp deleteGroupWhenEmpty true;

// Set Group Behavior
_footPatGrp setFormation (selectRandom ["FILE","LINE","COLUMN","STAG COLUMN", "WEDGE", "ECH LEFT", "ECH RIGHT", "VEE", "DIAMOND"]);
_footPatGrp setSpeedMode "LIMITED";
_footPatGrp setCombatMode "YELLOW";
_footPatGrp setBehaviour "SAFE";

// Get Leader Ready
_grpLeader = leader _footPatGrp;
_grpLeader addPrimaryWeaponItem "acc_flashlight";
_grpLeader enableGunLights "forceOn";
// Leader Gets the Patrol Area Script
[_grpLeader, _patrolArea, 1, true] execVM "scripts\hz\foot\hz_footAreaPatrol.sqf";

/*	------------------------------------------------------------------------------------------
	Define if the group is composed of Bandits, Mercenaries or Cultists.
	Group 1: Bandits
	Group 2: Hired Mercs
	Group 3: Cultists
	------------------------------------------------------------------------------------------	*/
_grpType = floor random [0, 5, 10];

// Give each unit its Event Handlers.
{[_x, _grpType] execVM "scripts\hz\foot\hz_setEnemy.sqf";} forEach units _footPatGrp;

 

Another example. I just recently switched all my scripts into functions format, took me quite a long time and it netted me perhaps +2 FPSs at most. Then I reduced the objects being spawned by the functions and even on EDEN by 50% and I got 20+ FPSs back!

 

So my final conclusion: I know that you spawn many objects and units with your scritps, and that is probably hurting performance A LOT more than any scripting/coding inefficiency you might have.

 

Markers and event triggers (if you don't mess with hard conditions) don't hurt. For objects and units, you hit the point BI created the dynamic simulation and the garbage collection, waiting for better days... Arma4/enfusion in 2022?

  • Like 2

Share this post


Link to post
Share on other sites
19 hours ago, Vandeanson said:

by default would fix that. 

Then you're giving it a invalid position and it will throw a wrong array size error.

You should fix your code, not work around the problem. You shouldn't check isFlatEmpty if you have no position to check.

 

6 hours ago, LSValmont said:

So waitUntils with simple conditions and Ai units with dynamic simulation seem to be very optimized currently.

There is no optimization in SQF.

 

6 hours ago, LSValmont said:

I could spawn this script for 65 markers (that means 65 times) without even loosing 1 FPSs

I already said why spawning scripts can't really influence fps, so no wonder.
Scheduled scripts are limited to 3ms per frame, no matter if you have 1 or 1000. So of course the FPS don't change.

 

6 hours ago, LSValmont said:

I just recently switched all my scripts into functions format, took me quite a long time and it netted me perhaps +2 FPSs at most.

What does that mean? Moving to CfgFunctions doesn't yield any fps improvement, unless you were execVM'ing before.

Putting scripts in a different place doesn't change their performance.

 

 

  • Like 1

Share this post


Link to post
Share on other sites
On 5/16/2019 at 12:26 AM, Vandeanson said:

while {true} do { _allplayers = (allPlayers select {lifeState _x != "DEAD-RESPAWN"}) - entities "HeadlessClient_F";

 

if (count _allplayers > 0) then { _SpawnPos = [getPosATL selectRandom _allplayers, VD_SpawnMinDist, VD_SpawnMaxDist, 10, 0, 0.1, 0, VD_BC_BLMrkrs] call BIS_fnc_findSafePos; sleep 0.5; };

 

if (_SpawnPos isFlatEmpty [15, -1, 0.2, 20, 0, false] isEqualTo []) exitwith {[_SpawnPos] spawn fnc_VABC_Spwn1};

 

sleep 1; };

 

@Dedmen

 

I thought about your initial comment again,about _spawnpos causing a problem if no player is found with _allplayers. 

 

If there are no players, the code defining _spawnpos and the isflatempty checks are skipped, or that is the intention. 

Instead it will loop, check for players again and do that until there is a player spawned in. 

Then it creates a _spawnpos based on such players position and will check if that position returns true with isflatempty. 

 

It also appears to me that _spawnPos is overwritten with a position before its ever used later in the code. So its default value should not matter, right? 

 

I might oversee something here, not sure what though. 

 

Cheers

Vd

 

Share this post


Link to post
Share on other sites

 

Your code readable (at least for me)...

while {true} do 
{ 
 _allplayers = (allPlayers select { lifeState _x != "DEAD-RESPAWN" }) - entities "HeadlessClient_F"; 

 if (count _allplayers > 0) then 
 { 
  _SpawnPos = [getPosATL selectRandom _allplayers, VD_SpawnMinDist, VD_SpawnMaxDist, 10, 0, 0.1, 0, VD_BC_BLMrkrs] call BIS_fnc_findSafePos;
  sleep 0.5;
 }; 
  
 if (_SpawnPos isFlatEmpty [15, -1, 0.2, 20, 0, false] isEqualTo []) exitwith {[_SpawnPos] spawn fnc_VABC_Spwn1}; 
 sleep 1;
}; 

 

1 hour ago, Vandeanson said:

If there are no players, the code defining _spawnpos and the isflatempty checks are skipped

No. you r doing that isFlatEmpty all the time... as dedmen said just include the flatEmpty part into your check above like this:

 

while {true} do 
{ 
 _allplayers = (allPlayers select { lifeState _x != "DEAD-RESPAWN" }) - entities "HeadlessClient_F"; 

 if (count _allplayers > 0) then 
 { 
  _SpawnPos = [getPosATL selectRandom _allplayers, VD_SpawnMinDist, VD_SpawnMaxDist, 10, 0, 0.1, 0, VD_BC_BLMrkrs] call BIS_fnc_findSafePos;
  sleep 0.5;
   
  if (_SpawnPos isFlatEmpty [15, -1, 0.2, 20, 0, false] isEqualTo []) exitwith {[_SpawnPos] spawn fnc_VABC_Spwn1}; 
 }; 
 sleep 1;
}; 

 

  • Thanks 1

Share this post


Link to post
Share on other sites

Oooh sweet, now i get it! Thanks! 

Share this post


Link to post
Share on other sites
4 minutes ago, Vandeanson said:

Oooh sweet, now i get it! Thanks! 

uh, but there is one problem left. exitWith exits the current scope only and that would be the "if (count _allplayers > 0) then"-scope. You could solve this with scopeName and breakTo commands.

 

EDIT:

like this

scopeName "OutOfWhile"

while {true} do 
{ 
 _allplayers = (allPlayers select { lifeState _x != "DEAD-RESPAWN" }) - entities "HeadlessClient_F"; 

 if (count _allplayers > 0) then 
 { 
  _SpawnPos = [getPosATL selectRandom _allplayers, VD_SpawnMinDist, VD_SpawnMaxDist, 10, 0, 0.1, 0, VD_BC_BLMrkrs] call BIS_fnc_findSafePos;
  sleep 0.5;
   
  if (_SpawnPos isFlatEmpty [15, -1, 0.2, 20, 0, false] isEqualTo []) then 
  {
   [_SpawnPos] spawn fnc_VABC_Spwn1;
   breakTo "OutOfWhile"
  }; 
 }; 
 sleep 1;
}; 

 

  • Like 2

Share this post


Link to post
Share on other sites

@sarogahtyp

 

Thanks you, i didnt know about breakTo, should come in handy:) 

 

1 hour ago, sarogahtyp said:

problem left. exitWith exits the current scope only 

Maybe this was the reason why i put the isflatempty check separately, not sure tho, it was quite late:) 

Will gladly take over your method and test it! 

Share this post


Link to post
Share on other sites

I'm not sure to understand:

- why you are using isFlatEmpty? This command is already embedded inside Bis_fnc_findSafePos (check the function viewer)

- why you're breaking to outOfWhile if you're re-running the loop with the same conditions? (in fact the randomization on players will change the tested position, but there is some chance to re-run the exact same code (and re-spawn fnc_VABC_Spwn1 as duplication), especially if the number of players is few.

 

I'd rather make a while true loop, a code with a timer check,  some conditions for spawning and starting the timer to freeze the code for a due delay. I'd rather write the exitWith on _allplayers check.

 

Share this post


Link to post
Share on other sites

Small thing that cleans stuff up a little and if I remember optimized a little better too:

 

_allplayers = (allPlayers select { lifeState _x != "DEAD-RESPAWN" && hasInterface });

 

  • Like 1

Share this post


Link to post
Share on other sites
3 hours ago, jshock said:

Small thing that cleans stuff up a little and if I remember optimized a little better too:

Wrong.
hasInterface returns whether the local machine has a interface, not whether that player has one.

Can easily figure that out yourself, how is it supposed to know which player you want to ask about if you don't give it any arguments?


On server it's (always) false, and on client it's always true if not a headless client.
As this code runs on the server, it's always false.

  • Like 3

Share this post


Link to post
Share on other sites
3 hours ago, Dedmen said:

Wrong.
hasInterface returns whether the local machine has a interface, not whether that player has one.

Can easily figure that out yourself, how is it supposed to know which player you want to ask about if you don't give it any arguments?


On server it's (always) false, and on client it's always true if not a headless client.
As this code runs on the server, it's always false.

 

Ah, yep, I knew speed reading this thread at 4am would come back to bite me. My bad there OP.

  • Haha 2

Share this post


Link to post
Share on other sites

Interesting thread, I like this stuff.

 

- I often use quite a few "[] spawn {}" with "while {alive player}" loops. In order to avoid them starting their business after the sleep almost at the same time, I use like "private _sleep = 10 + random 10;" and the "sleep _sleep" inside the loop.

- Avoid regular/intensive assignments, use variables

- Small sleeps between unit spawns

 

Currently I am testing dynamic caching distance for MCCs GAIA, not sure if it is a good idea, because some AI scripts do not like units suddenly ...disappearing/simulation off... and start spamming script errors.

 

Basically the script regularly checks fps (here average over 5 seconds):

_fps = 0; for "_i" from 1 to 10 do {_fps = _fps + diag_fps; sleep 0.5}; _fps = 0.1*_fps;

...and changes the GAIA caching distance (GAIA_CACHE_STAGE_1 - 100) accordingly with some MIN and MAX of course. Which may also cost FPS at the moment after execution.

https://pastebin.com/S3RicpJ1 - Technically, it works. But have not done much testing yet.

 

I am interested in what you think about it. If dynamic engine side caching may be a good idea? I.e., dynamic use of setDynamicSimulationDistance?

 

Share this post


Link to post
Share on other sites

Without any script consideration, fps can drastically decrease if you point at a town or rocky/forest areas, depending on map (vanilla ones speaking, often worst on modded ones). Particles are also FPS demanding (throw some smoke grenades for test).

So, It's hard to stick on what could be the best, for a spawned script. On my mind, there are some risks to be out of sync here.

Keep on mind FPS are related to GPU, video/rendering options, at first!

And your solution can't be a common one as all players have different FPS for many reasons. So, here, you are speaking about local scripts? SP only?

 

On heavy scenario you are loading the CPU bark for scripts and GPU one for displays.

So the fps drop seems more "persistent", along with active scripts but the challenge is not so evident.

 

Perhaps, someone will help for your aim, but on my mind:

- pay a sharp attention to the number of active scripts. Some of them are probably not optimized. (count diag_activeSQFScripts   & copyToClipboard str diag_activeSQFScripts).

- limit the units/objects. Use BI tools like dynamic simulation, simple objects, garbage management  and/or your own spawn/despawn along with players' distances.

- use Event Handlers or ease the mandatory loops (sleep 2 or even 5)... use lazy waitUntil {sleep 2; <condition>} into them if you can.

 

  • Like 1

Share this post


Link to post
Share on other sites

To all contributors in this thread:

 

Thanks a ton for your help! 

 

I have rewritten most of my spawners based on feedback received here and i am quite happy with the result. Some of the feedback i have not been able to apply to my code yet but that will come. 

 

I have now released the update of my mod and am pleased to share it an FYI where all your code suggestions went into:) 

 

https://steamcommunity.com/sharedfiles/filedetails/?id=1655727065

 

Cheers 

Vd

 

 

  • Like 2

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×