dimon 32 Posted March 20, 2015 what is best of three ways ? //{_x AddEventHandler ["Killed",{deletevehicle _this;}]} foreach allunits; /* while {true} do { { if (!alive _x) then { deletevehicle _x; }; } foreach allunits; sleep 10; }; */ /* { _x spawn { while {true} do { if (!(alive _this)) exitwith { deletevehicle _this; }; sleep 10; }; }; } foreach allunits; */ Share this post Link to post Share on other sites
Schatten 290 Posted March 20, 2015 https://community.bistudio.com/wiki/Code_Optimisation I'm sure that first variant is better, because event handlers for this purpose intended. Also this code is more suitable to hide corpses: {_x AddEventHandler ["Killed", {hideBody (_this select 0)}]} forEach allUnits; Share this post Link to post Share on other sites
dimon 32 Posted March 20, 2015 https://community.bistudio.com/wiki/Code_OptimisationI'm sure that first variant is better, because event handlers for this purpose intended. Also this code is more suitable to hide corpses: {_x AddEventHandler ["Killed", {hideBody (_this select 0)}]} forEach allUnits; well the question is what principle to use... And whether the same event handlers in the same while loop? Schatten, ну Ð²Ð¾Ð¿Ñ€Ð¾Ñ Ð¸Ð¼ÐµÐ½Ð½Ð¾ какой принцип иÑпользовать... Рне ÑвлÑÑŽÑ‚ÑÑ Ð»Ð¸ те же обработчики Ñобытий тем же циклом while? Share this post Link to post Share on other sites
samatra 85 Posted March 20, 2015 (edited) I assume you're trying to do cleanup of dead units? This is definitely not a best approach this as more units will be created through the game calling this code block will assign "Killed" event handler (first method) or another thread (third method) onto same unit multiple times. I'd suggest using https://community.bistudio.com/wiki/allDead instead. Speaking of performance, engine event handlers are always "better" than regular scripted checked, but it all depends on what you're trying to achieve, simply deleting units as soon as they die is definitely not a player-friendly experience as players might want to loot dead units first. Spawning a thread with sleep after unit was Killed might be a solution but personally I prefer to have as less threads as possible. If you're interested in my approach to this task I can share the code. Edited March 20, 2015 by SaMatra Share this post Link to post Share on other sites
dimon 32 Posted March 20, 2015 (edited) OK,ok - sorry ) The code I posted is just an example: in order to understand whether a good event handlers and not better in some situations to write a loop with the desired pause. There is an opinion that the event handlers no load carry - which I highly doubt because as I understand it hangs the same cycle with the test conditions. Addaction - out with the same situation I think there is also a continuous loop with the test conditions. Код Ñ Ð²Ñ‹Ð»Ð¾Ð¶Ð¸Ð» проÑто Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð¼ÐµÑ€Ð°: чтобы понÑÑ‚ÑŒ так ли хороши обработчики Ñобытий и не лучше ли в некоторых ÑитуациÑÑ… напиÑать Ñвой цикл Ñ Ð½ÑƒÐ¶Ð½Ð¾Ð¹ паузой (еÑли еÑÑ‚ÑŒ Ñ‚Ð°ÐºÐ°Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑ‚ÑŒ). СущеÑтвует мнение, что обработчики Ñобытий никакой нагрузки не неÑут - в чем Ñ Ñильно ÑомневаюÑÑŒ ибо к как Ñ Ð¿Ð¾Ð½Ð¸Ð¼Ð°ÑŽ вешаетÑÑ Ñ‚Ð¾Ñ‚ же цикл Ñ Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐºÐ¾Ð¹ уÑловиÑ. Addaction - из Ñ Ñ‚Ð¾Ð¹ же Ñитуации Ñ Ð´ÑƒÐ¼Ð°ÑŽ, там тоже поÑтоÑнный цикл Ñ Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐºÐ¾Ð¹ уÑловиÑ. this question came to me once when I accidentally read this post Edited March 20, 2015 by Dimon add Share this post Link to post Share on other sites
samatra 85 Posted March 20, 2015 Main difference of event handlers is that they execute scripts in non-scheduled environment while your regular script threads (execVM, spawn, addAction, etc.) run in virtual machine or scheduled environment. Having lots of threads doing heavy calculations will seriously postpone execution of other scripting threads, causing so called script lag. Having heavy calculations in event handler called code will not create any script lag but instead will freeze entire game until execution is complete, you need to be very careful with that especially on server machine as server-side freeze will end up with server-wide lag ruining experience for players. SQS and FSMs are bit different but that's another topic. Again, it all depends on what you want to achieve and how much priority it has to have. If you want to avoid data race conditions or have precise on each frame code then non-scheduled execution is a must. I guess body clean up is not that important precision-wise and executing it in ScriptVM threads should be fine, personally I prefer to have single thread iterating through list of something instead of attaching new thread to each item in the list. Share this post Link to post Share on other sites
dimon 32 Posted March 20, 2015 (edited) OK, let's move closer to the issue. There was a dispute that would be better : 1. Hang on every unit event killed 2. At death unit ( using the same event handler ) to drive them in one array and then in one cycle to process the entire array. To have the opportunity to work with this unit after his death: for example, before deleting check the distance to the player 1. for the first option, spawn the function _deadunit=_this select 0; sleep 60; waitUntil{sleep 5;({isPlayer _x} count ((getPosATL _deadunit) nearEntities ["man",50]))<1};//count playableunits hideBody _deadunit; deleteVehicle _deadunit; 2.In the second option we have in the mission already hanging loop which periodically with a pause of 20 seconds checks the array while {true} do { sleep 20; _cnt=count FFA_OBJECTSTOCLEAR; for "_i" from 0 to _cnt - 1 step 1 do { _item=FFA_OBJECTSTOCLEAR select _i; _veh=_item select 0; if !(alive _veh) then { _deathtime=_item select 1; if (_deathtime==0) then { _deathtime=diag_tickTime; FFA_OBJECTSTOCLEAR set [_i,[_veh,_deathtime]]; }; if ((diag_tickTime -_deathtime)>120 && {(({isPlayer _x} count ((getPosATL _veh) nearEntities ["man",50]))<1} ) then { deleteVehicle _veh; FFA_OBJECTSTOCLEAR set [_i,objNull]; }; }; }; FFA_OBJECTSTOCLEAR=FFA_OBJECTSTOCLEAR-[objNull]; }; Ок, перейдем ближе к вопроÑу. Произошел Ñпор что будет лучше : 1. Вешать на каждого юнита Ñобытие killed 2. При Ñмерти юнита ( Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ того же обработчика Ñобытий ) загонÑÑ‚ÑŒ их в один маÑÑив и потом в одном цикле обрабатывать Ñтот веÑÑŒ маÑÑив. Чтобы поÑвилаÑÑŒ возможноÑÑ‚ÑŒ работать Ñ Ñтим юнитом поÑле его Ñмерти: допуÑтим перед удалением проверÑÑ‚ÑŒ раÑÑтоÑние до игрока 1. Ð´Ð»Ñ Ð¿ÐµÑ€Ð²Ð¾Ð³Ð¾ вариант spawn функцию 2.Во втором варианте у Ð½Ð°Ñ Ð² миÑÑии уже виÑит цикл который периодичеÑки Ñ Ð¿Ð°ÑƒÐ·Ð¾Ð¹ 20 Ñекунд проверÑет маÑÑив Edited March 20, 2015 by Dimon Share this post Link to post Share on other sites
samatra 85 Posted March 20, 2015 (edited) Not THAT much of a difference performance-wise honestly since all your code is run in threads (scheduled environment) in the end, even when Killed event handler is executed, you spawn another thread which does sleep and waitUntils. For this kind of task (body cleanup) it all comes down and depends on how it would be better for you as mission designer to manage dead units cleanup, add event handler onto each unit manually, add each unit to "watch list", or simply cycle through allDead command returned array as I suggested. If go nitpicking about performance differences, first approach is worse because it creates many smaller scripting threads, second is worse because it regularly runs "array - array" command (FFA_OBJECTSTOCLEAR=FFA_OBJECTSTOCLEAR-[objNull]) which is fairly expensive if you have very long array. By the way since I had this piece of code for cleanup at hand, going to post it anyway: while {true} do { { // If no cleanup timer set, set it if(_x getVariable ["cleanup_at", 0] == 0) then { _x setVariable ["cleanup_at", diag_tickTime + (switch(true) do { case (_x isKindOf "CAManBase"): {120}; // Dead units - 2 minutes default {300}; // Vehicle wrecks - 5 minutes })]; }; // Check if timer ran out if(diag_tickTime > _x getVariable ["cleanup_at", 0]) then { _dead = _x; if(switch(true) do { // Example of additional condition, delete body anyway if it is right at base so they wouldn't stockpile there as players can die and respawn there, fnc_isOnBase is not provided in this example code case (_dead call fnc_isOnBase): {true}; // Check for players nearby, if there is > 0 players nearby, have switch return false case ({ // If player is within 20 meters, exit "count" loop with result of 1 if(_dead distance _x < 20) exitWith {1}; } count (playableUnits) > 0): {false}; // Default is true => delete the body\wreck default {true}; }) then { deleteVehicle _dead; } else { // Switch condition returned false, means we should not delete body yet and timer has to start again. _dead setVariable ["cleanup_at", 0]; }; }; } forEach (allDead); // Next cycle through dead units in 10 seconds. sleep 10; }; Code supposed to be run once in scheduled environment (execVM, spawn) Pastebin for better readability: http://pastie.org/private/lyjhmlor0h7krjxc0ey4uw Edited March 20, 2015 by SaMatra Share this post Link to post Share on other sites
dimon 32 Posted March 20, 2015 (edited) Thanks ,SaMatra, for the script and for the clarification. ---------- Post added at 13:05 ---------- Previous post was at 12:45 ---------- The next question follows from the first. What would be better from the point of performance in situations when it is necessary to check something (for example to compare who has more in the trigger/marker) 1. We hang on every unit handler killed spawn with functions in which there is a one-time comparison of the number of living units. (of course adding a check on the already running function to avoid triggering at the same time a large number of runs the function when multiple event killed.) _new AddEventHandler ["Killed",{_this spawn FFA_EVENT}]; FFA_EVENT if (FFA_EVENT_EVENT) exitWith{}; FFA_EVENT_EVENT = true; if ((('man' countType list SENSORENEMYSIDE) < 15) && {(('tank' countType list SENSORENEMYSIDE) < 2)} && {(('car' countType list SENSORENEMYSIDE) < 2)} && {(FFA_TOWNGUARDSSPAWNED)} && {(isNull FFA_RADIO)} && {(isServer)} && {(FFA_SERVERSTARTED)} && {(FFA_TNDEF == 1)}) then { [] spawn ffa_func_deftown; }; FFA_EVENT_EVENT = false; 2. We hang in the game loop to check and compare the number of living units. while{true} do { sleep 10; if ((('man' countType list SENSORENEMYSIDE) < 15) && {(('tank' countType list SENSORENEMYSIDE) < 2)} && {(('car' countType list SENSORENEMYSIDE) < 2)} && {(FFA_TOWNGUARDSSPAWNED)} && {(isNull FFA_RADIO)} && {(isServer)} && {(FFA_SERVERSTARTED)} && {(FFA_TNDEF == 1)}) then { [] spawn ffa_func_deftown; }; }; like the first method at first glance better, but a large number were hanged handlers be better than one while? specify in advance that the accuracy and speed checks are not important. Что будет лучше Ñ Ñ‚Ð¾Ñ‡ÐºÐ¸ производительноÑти в Ñитуации когда надо проверить что-то (допуÑтим Ñравнить кого больше в триггере/маркере) 1. Вешаем на каждого юнита обработчик killed Ñ spawn функции в которой идет одноразовое Ñравнение количеÑтва живых юнитов. (еÑтеÑтвенно добавив проверку на то запущена ли уже функциÑ, чтобы избежать Ñрабатывание одновременно большого кол-ва запуÑков функции при множеÑтвенном Ñобытии killed.) 2. Вешаем в игре цикл который будет проверÑÑ‚ÑŒ и Ñравнивать количеÑтво живых юнитов. как бы первый ÑпоÑоб на первый взглÑд лучше, но большое количеÑтво повешенных обработчиков будет ли лучше чем один while Ñ Ð¿Ð°ÑƒÐ·Ð¾Ð¹ 5 Ñек допуÑтим? заранее оговорюÑÑŒ, что точноÑÑ‚ÑŒ и ÑкороÑÑ‚ÑŒ проверки не важна Edited March 20, 2015 by Dimon Share this post Link to post Share on other sites
samatra 85 Posted March 20, 2015 (edited) I'd say that having one constantly working thread is better than spawning many threads just to close most of them right away. So I'd go with second approach. On the other hand you can call instead of spawn in "Killed" event handler, it will do the check right away within current frame, looking at code it shouldn't cause any performance issues. Edited March 20, 2015 by SaMatra Share this post Link to post Share on other sites
dimon 32 Posted March 20, 2015 (edited) I drew the correct conclusion that the fewer concurrent threads - the better? But if I still, for some reason, I would be forced to use an event handler on each unit (for example to find the killer and to reward him), then it is better to use a handler in full... Thanks again for the script, remove, and with your permission, it just optimized (although all this stuff) while {true} do { { if(_x getVariable ["cleanup_at", 0] == 0) then { _x setVariable ["cleanup_at", diag_tickTime + ( call { if (_x isKindOf "CAManBase") exitwith {120}; // Dead units - 2 minutes 300; // Vehicle wrecks - 5 minutes } )]; }; if(diag_tickTime > _x getVariable ["cleanup_at", 0]) then { _dead = _x; if( call { //case (_dead call fnc_isOnBase): {true}; if ({ if(_dead distance _x < 20) exitWith {1};} count (playableUnits) > 0) exitwith {false}; true; } ) then { deleteVehicle _dead; } else { _dead setVariable ["cleanup_at", 0]; }; }; } forEach (allDead); sleep 10; }; Ñ Ñделал правильный вывод, что чем меньше параллельных потоков - тем лучше? Ðо еÑли Ñ Ð²Ñе равно, по каким то причинам, вынужден буду иÑпользовать обработчик Ñобытий на каждом юните (допуÑтим Ñ Ñ†ÐµÐ»ÑŒÑŽ найти killer и наградить его), то уже тогда лучше иÑпользовать обработчик по полной мере... Еще раз ÑпаÑибо за Ñкрипт ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð¸ Ñ Ð²Ð°ÑˆÐµÐ³Ð¾ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ Ñ‡ÑƒÑ‚ÑŒ его оптимизировал (Ñ…Ð¾Ñ‚Ñ Ñто вÑÑ‘ мелочи) Edited March 20, 2015 by Dimon Share this post Link to post Share on other sites
samatra 85 Posted March 20, 2015 Yes, the less script threads you have, the better. Of course it also matters how your threads work and what they do. Sure, feel free to use cleanup script as you like. Using call instead of switch really gives very small performance increase but I just prefer switch for better readability. Share this post Link to post Share on other sites
dimon 32 Posted March 31, 2015 What would be better? { FFA_HOUSESCLIENT= FFA_HOUSES; (owner _x) publicVariableClient "FFA_HOUSESCLIENT"; } foreach playableUnits; or FFA_HOUSESCLIENT= FFA_HOUSES; publicVariable "FFA_HOUSESCLIENT"; Share this post Link to post Share on other sites
samatra 85 Posted April 1, 2015 (edited) As far as I heard publicVariableClient actually sends packets to all clients just applies them on one due to improper implementation - http://feedback.arma3.com/view.php?id=22695 Might not be actual in A3 and just in A2 but according to comments might have been addressed in 1.63. I'd use publicVariable if FFA_HOUSES is static and not going to change. If it is changing dynamically and array increases or decreases in size I'd go with pvc and when array updates instead of sending entire array again I'd just send what changed in it to all clients and when JIP joined, send them full final array with pvc once. Edited April 1, 2015 by SaMatra Share this post Link to post Share on other sites