Bl44a 0 Posted September 20, 2016 I will do some testing next week with the built in timers, but I wanted to see if someone with more detailed knowledge of the functions had input, or if anyone had a better solution they could propose. So a summary of what I am doing is; cycling through players (80 players max); checking their distance from client running the script; checking their side; checking their conscious state. My initial method which I am looking to optimize does the following: { if _x distance player < 1000 then if side _x == side player then if !unconscious then <all conditions met, do something> } forEach playableUnit My idea to improve the script is to do something like this: { if side _x == side player then if !unconscious then <all conditions met, do something> } forEach (player nearEntities 1000) My initial assumption without testing or doing a proof is that for a low number of players, the first method would be better, but as the number of players increases, the second would be quicker. Is there a much better solution that I am missing? Thanks. Share this post Link to post Share on other sites
jshock 513 Posted September 20, 2016 Your heaviest evaluation will be the distance, but the real question is how often this is evaluated? Share this post Link to post Share on other sites
Bl44a 0 Posted September 20, 2016 Your heaviest evaluation will be the distance, but the real question is how often this is evaluated? Right, which is why my intuition is that for a large number of players, the first method (checking the distance of each player) is not good. This is added to a missioneventhandler (draw3d), so each frame. now that I think about, I could just rearrange my conditionals.. I could do: if on team then if distance < 1000 then if !incapacitated that way the beefy distance check will only run on players on the same team. Which is at most n/2. In this case it might be better than nearentities. I'm not at my gaming PC so I can't go look at the function in functionviewer do any testing. That's why I was curious if anyone had an idea of how nearentities specifically worked. For all I know it could get every entity on the map, check its distance, and then return an array of only those entities that are closer than the provided distance... In which case it would run horribly. This is an exaggeration and likely not the case but just an example. Share this post Link to post Share on other sites
jshock 513 Posted September 20, 2016 As far as I remember sqf will evaluate all conditions no matter the order or outcome of one conditional compared to another. You will need to explicitly evaluate lazily: if (cond1 && {cond2} && {cond3}) then {stuff};Where if cond1 is false, it implictly falsifies the entire condition, whereas your original would evaluate all of them anyhow, but anyways, seems you understand that concept, just wanted to say sqf doesn't do that inherently.And never mind to it all, your're nesting the if statements, same damn thing as what I just put lol, long day. As far as nearEntites goes, I don't know what's under the hood there, but it's safe to assume it would probably iterate through all entities (AI or players) not just players. So I would recommend iterating through "allPlayers" command, doing the distance check last, and the other conditions in whatever order. And for another optimization that could help some, use count instead of forEach, no difference between the two other than forEach provides an indexing variable that increments for each element iterated through, which you aren't using. I would recommend coding it on the basis that the number of players could inherently be infinite, because when your're dealing with only a handful the performance requirement would be negilgible. Again, small but could help, the side of the player is unlikely to change, so instead of re-evaling every iteration, save that value outside the loop. Share this post Link to post Share on other sites
R3vo 2654 Posted September 20, 2016 What about something like this? { //Your if stuff false; } count ({player distance _x < 1000} select playableUnits) Edit: Fixed example, thanks to jshock Share this post Link to post Share on other sites
jshock 513 Posted September 20, 2016 What about something like this? { //Your if stuff false; } count ({player distance _x } count playableUnits) Count returns a number not an array for use, did you mean select? Share this post Link to post Share on other sites
das attorney 858 Posted September 20, 2016 No, he's good, but it's debatable. If you're needing to jam in a bool return to satisfy the bool/nothing return of count, then you could use forEach without additional line of (albeit simple) code per iteration. Nobody's really bothered posting up some relative timings from their PC and a lot of people on this forum say things like "works like a charm" for everything, so without testing code personally, I wouldn't believe anyone on this forum. 1 Share this post Link to post Share on other sites
Bl44a 0 Posted September 21, 2016 And for another optimization that could help some, use count instead of forEach, no difference between the two other than forEach provides an indexing variable that increments for each element iterated through, which you aren't using. ahh, I never looked into count. I didn't know it was an alternative to forEach (minus the indexing variable). Thanks, for the input everyone Share this post Link to post Share on other sites
pedeathtrian 100 Posted September 21, 2016 If you're that performance-concerned, don't forget to use distanceSqr instead of distance. And yeah, measuring everything will definitely not hurt. Share this post Link to post Share on other sites
jshock 513 Posted September 21, 2016 This page shares a lot of interesting info, with some timings for most instances: https://community.bistudio.com/wiki/Code_Optimisation Share this post Link to post Share on other sites
Grumpy Old Man 3547 Posted September 21, 2016 Testing code performance can be a fun thing to do if you're in the single player editor. The debug console basically has everything you need for it, monitoring variables, an "Execute" input and the dedicated code performance button. Usually I'll test performance on the VR map since there's nothing much going on that could influence script performance. My rig is a 4770k at 4.5 and a GTX 770 4gb classified from evga. I used this to spawn and equally distribute 79 other units: for "_i" from 1 to 79 do { _pos = [random 10000,random 10000,0]; _unit = createGroup side player createUnit [typeOf player,_pos,[],0,"NONE"]; }; Now we have 80 units on the map, including the player unit. The side of all units stays west to provide comparable results. Using your first approach: { if (_x distance player < 1000) then { if (side _x == side player) then { if !(lifeState _x == "INCAPACITATED") then { true } } } } forEach allUnits; Runs for 0.179888 ms average. That's already great, considering one frame at 60 fps takes ~16.6667ms to run. I replaced "==" with "isEqualTo" and tried to alterate the order of checks. Side check first: { if (side _x isEqualTo side player) then { if (_x distance player < 1000) then { if !(lifeState _x isEqualTo "INCAPACITATED") then { true } } } } forEach allUnits; 0.287687 ms. Ouch. That's worse than before, hence lifeState check, then distance, then side: { if !(lifeState _x isEqualTo "INCAPACITATED") then { if (_x distance player < 1000) then { if (side _x isEqualTo side player) then { true } } } } forEach allUnits; 0.271813 ms A bit better, still worse than the original attempt. Last try, first distance check, then lifeState, then side: { if (_x distance player < 1000) then { if !(lifeState _x isEqualTo "INCAPACITATED") then { if (side _x isEqualTo side player) then { true } } } } forEach allUnits; 0.179953 ms. And there we are. Seems that the distance check at first safes the most performance, since most units would be further away from the player than 1000m. This brings the question how these 3 versions will perform if ALL units are within 1000m of the player. Using this to spawn 79 units within 1000m: for "_i" from 1 to 79 do { _unit = createGroup side player createUnit [typeOf player,getposatl player,[],1000,"NONE"]; }; Now back to the first example above, distance, side, lifeState: { if (_x distance player < 1000) then { if (side _x == side player) then { if !(lifeState _x == "INCAPACITATED") then { true } } } } forEach allUnits; 0.437445 ms. Second example from above, side, distance, lifeState: { if (side _x isEqualTo side player) then { if (_x distance player < 1000) then { if !(lifeState _x isEqualTo "INCAPACITATED") then { true } } } } forEach allUnits; 0.409836 ms, a tad better than the first example. Third example, distance, lifeState, side: { if (_x distance player < 1000) then { if !(lifeState _x isEqualTo "INCAPACITATED") then { if (side _x isEqualTo side player) then { true } } } } forEach allUnits; 0.403714 ms. Coming to the conclusion that depending on the type of mission it's best to set the first condition to something that would return false for most units, so the forEach loop can continue and check the next unit. A mission that has lots of players spread out all over the map (probably most likely for wasteland or similar kind of missions) benefits most from checking for distance first. On missions with players distributed amongst three sides (west, east, independent) the biggest benefit would be doing the sidecheck first, then check for the distance. All in all, the best performant snippet eats up roughly 1% runtime of a frame, the worst eats 2.7%. No snippet comes even close to 3ms runtime, so it's safe to say using this on every frame should give no issues, unless there's other more heavy stuff happening. Cheers 1 Share this post Link to post Share on other sites
R3vo 2654 Posted September 21, 2016 { if (side _x == side player) then { if !(lifeState _x == "INCAPACITATED") then { true }; }; false; } count (allUnits select {player distance _x < 1000 }) I used the code above to spawn 79 units and here's the result: 0.175654 ms Share this post Link to post Share on other sites
barbolani 198 Posted September 21, 2016 Hi! My study on this + a question. Of course order of questions is important, you have to put them in order to ask first the one which will have more chances of having a NO. But in dynamic environments thats very unpredictable. I usually aim for some balance with the commands you are using, for example if the first question is using findEmptyPosition probably you should leave it for the last. First easy ones that require a quick check for the engine, such as alive, isPlayer, later math calculations, like distance, later checks on strings or arrays, like in array, or lifeState and later the most heavy demand like findEmptyPosition. Ok, now comes my question. I am supposing array select {whatever} is better tan iterating asking through the array. In the example from above: _side = side player;// so we dont have to check the side of the player everytime. {dostuff} forEach (playableUnits select {(_x distance player < 1000) and (side _x == _side) and (!lifeState _x != "INCAPACITATED")}); Is not that better than checking the conditions "by yourself"? Share this post Link to post Share on other sites
jshock 513 Posted September 21, 2016 Only one way to test there barbolani . Optimizations threads are always exciting to me, learning new stuff and all, only reason I'm not throwing up timings is it's 4am and I'm on my phone trying to beat insomnia (at least to some extent). I would like to see the timings on something like barbolani posted but more like the following, just to fully test out some different script commands and possibly some future optimizations (doubtful on the following, just want to see the results :p): (playableUnits select {allConditions}) apply {_x call fnc_doStuff}; Will test this and others after college shit tomorrow :). 1 Share this post Link to post Share on other sites
barbolani 198 Posted September 21, 2016 I would do it but I am on the office doing nothing :) All those things come to my head during coding, but in the end I am messed up with some bug and allways say "ok, will make some tests as soon as I solve this" :) Share this post Link to post Share on other sites
pedeathtrian 100 Posted September 21, 2016 _side = side player;// so we dont have to check the side of the player everytime. {dostuff} forEach (playableUnits select {(_x distance player < 1000) and (side _x == _side) and (!lifeState _x != "INCAPACITATED")}); Is not that better than checking the conditions "by yourself"? Having all other things being equal, it should not be better. At first, select creates (returns) an array which is not there in initial variant. Here we have some slight memory and execution overhead. At second, additional iteration loop is having place because of forEach; in case of apply there will also be overhead caused by calls and storing their results back into array. At third, there will be no gains in terms of reducing initial operations count: no new corners to be cut, etc. select is still O(N) where N is number of elements in input (playableUnits). Having O(N) both ways, what matters is amount of operations performed on every iteration, e.g. you can win a (very tiny) bit in some places: like not creating _forEachIndex in select; and lose using extra calls, and extra memory. Consider measuring for different orders of magnitude of input's size to have a full and clear picture (esp. if not sure algorithm complexity is linear). Share this post Link to post Share on other sites
jshock 513 Posted September 21, 2016 Having all other things being equal, it should not be better. At first, select creates (returns) an array which is not there in initial variant. Here we have some slight memory and execution overhead. At second, additional iteration loop is having place because of forEach; in case of apply there will also be overhead caused by calls and storing their results back into array. At third, there will be no gains in terms of reducing initial operations count: no new corners to be cut, etc. select is still O(N) where N is number of elements in input (playableUnits). Having O(N) both ways, what matters is amount of operations performed on every iteration, e.g. you can win a (very tiny) bit in some places: like not creating _forEachIndex in select; and lose using extra calls, and extra memory. Consider measuring for different orders of magnitude of input's size to have a full and clear picture (esp. if not sure algorithm complexity is linear). This I do understand, I just wanted to see what we could get away with in sqf, cause I know with some things I've been surprised ;). Share this post Link to post Share on other sites
barbolani 198 Posted September 21, 2016 So, if I understood well (don't think so), doing this: _side = side player;// so we dont have to check the side of the player everytime. {dostuff} forEach (playableUnits select {(_x distance player < 1000) and (side _x == _side) and (!lifeState _x != "INCAPACITATED")}); Is almost the same tan doing this: _side = sidePlayer; _arr = []; { if (condition) then {if (condition) then {if (condition) then {_arr pushBack _x}}}; } forEach playableUnits; {doStuff} forEach _arr; Correct? Share this post Link to post Share on other sites
Neviothr 102 Posted September 23, 2016 What about something like this? { //Your if stuff false; } count ({player distance _x < 1000} select playableUnits) Edit: Fixed example, thanks to jshock Why is there a false at the end of the count loop? Share this post Link to post Share on other sites
Grumpy Old Man 3547 Posted September 23, 2016 Why is there a false at the end of the count loop? Because count expects bool as return, would throw an error otherwise. Cheers 1 Share this post Link to post Share on other sites