Warcraft 3 documentation
vJASS & Zinc Documentation
For the latest documentation about how it works vJASS and Zinc language layers for Warcraft III, please follow these links:
Jasshelper documentation - Zinc documentation - WC3 Optimizer documentation

Which could be more efficient/better performance? No New Posts Coding Help

Started by
8uY_YoU

0 Members and 1 Guest are viewing this topic.

Which could be more efficient/better performance?
on: February 18, 2015, 10:14:34 AM

Hello guys,
I have problems when people say my map is kinda laggy
As far as I know my code is leak free. However, I noticed that I put too much system inside which gives a lot pressure to the CPU (especially periodic calls + groupenums calls).
So, before I refine my codes, I need some suggestion

I will give examples from each category and you guys just point out, which is more efficient method to use and give better performance.
If there is another better method that I didn't know or some of my methods are wrong, tell me!

1. Periodic Calls

a. Using Timer
Code: jass
  1. function RunThis takes nothing returns nothing
  2.     // code here
  3. endfunction
  4.  
  5. function InitTrig_test takes nothing returns nothing
  6.     local timer t = CreateTimer(  )
  7.     call TimerStart(t,0.25,true,function RunThis)
  8. endfunction

b. Using Trigger
Code: jass
  1. function RunThis takes nothing returns boolean
  2.     // code here
  3.     return false
  4. endfunction
  5.  
  6. function InitTrig_test takes nothing returns nothing
  7.     local trigger t = CreateTrigger(  )
  8.     call TriggerRegisterTimerEvent(t,0.25,true)
  9.     call TriggerAddCondition(t,Condition(function RunThis))
  10. endfunction

2. Group Execution

a. Blizz-way (Filtering then Execute)
Code: jass
  1. function GroupExecuteUnits takes nothing returns nothing
  2.     //execute each valid unit
  3. endfunction
  4.  
  5. function GroupFilter takes nothing returns boolean
  6. endfunction
  7.  
  8. function GroupExec takes nothing returns nothing
  9.     local group g = CreateGroup()
  10.     local unit c = GetTriggerUnit()
  11.     local real x = GetUnitX(c)
  12.     local real y = GetUnitY(c)
  13.     call GroupEnumUnitsInRange(g,x,y,300,Filter(function GroupFilter))
  14.     call ForGroup(g, function GroupExecuteUnits)
  15.     call DestroyGroup(g)
  16.     set g = null
  17.     set c = null
  18. endfunction

b. In one function way
Code: jass
  1. function GroupExec takes nothing returns nothing
  2.     local group g = CreateGroup()
  3.     local unit c = GetTriggerUnit()
  4.     local real x = GetUnitX(c)
  5.     local real y = GetUnitY(c)
  6.     local unit temp
  7.     call GroupEnumUnitsInRange(g,x,y,300,null)
  8.     loop
  9.         set temp = FirstOfGroup(g)
  10.     exitwhen temp == null
  11.         if IsUnitEnemy(temp,GetOwningPlayer(c)) then
  12.             //execute each valid unit
  13.         endif
  14.         call GroupRemoveUnit(g,temp)
  15.     endloop
  16.     call DestroyGroup(g)
  17.     set g = null
  18.     set c = null
  19. endfunction

c. What-i-am-currently-using way
Code: jass
  1. function GroupFilter takes nothing returns boolean
  2.         //execute each valid unit
  3.     endif
  4.     return false
  5. endfunction
  6.  
  7. function GroupExec takes nothing returns nothing
  8.     local group g = CreateGroup()
  9.     local unit c = GetTriggerUnit()
  10.     local real x = GetUnitX(c)
  11.     local real y = GetUnitY(c)
  12.     local filterfunc ff = Filter(function GroupFilter)
  13.     call GroupEnumUnitsInRange(g,x,y,300,ff)
  14.     call DestroyGroup(g)
  15.     call DestroyFilter(ff)
  16.     set ff = null
  17.     set g = null
  18.     set c = null
  19. endfunction

3. Multi-Instancing]

a. Indexing (in this case, one timer for all instances)
Code: jass
  1. function Execute takes nothing returns nothing
  2.     local integer i = 1
  3.     loop
  4.     exitwhen i > udg_INDEX
  5.         set udg_Durations[i] = udg_Durations[i] - TimerGetTimeout(udg_Time)
  6.         if udg_Durations[i] > 0 then
  7.             //do a heal/damage to udg_Units[i]
  8.         else
  9.             if i != udg_INDEX then
  10.                 set udg_Durations[i] = udg_Durations[udg_INDEX]
  11.                 set udg_Units[i] = udg_Units[udg_INDEX]
  12.             endif
  13.             set udg_INDEX = udg_INDEX - 1
  14.             set i = i - 1
  15.         endif
  16.         set i = i + 1
  17.     endloop
  18.  
  19.     if udg_INDEX == 0 then
  20.         call PauseTimer(udg_Time)
  21.     endif
  22. endfunction
  23.  
  24. function Register takes unit u, real duration returns nothing
  25.     set udg_INDEX = udg_INDEX + 1
  26.     set udg_Units[udg_INDEX] = u
  27.     set udg_Durations[udg_INDEX] = duration
  28.     if udg_INDEX == 1 then
  29.         call TimerStart(udg_Time,0.5,true,function Execute)
  30.     endif
  31. endfunction

b. Keying To Handle (in this case, one timer for one instance)
Code: jass
  1. function Execute takes nothing returns nothing
  2.     local timer t = GetExpiredTimer()
  3.     local integer key = GetHandleId(t)
  4.     local unit u = LoadUnitHandle(udg_HashTable,key,0)
  5.     local real duration = LoadReal(udg_HashTable,key,1)
  6.    
  7.     ////do a heal/damage to unit "u"
  8.    
  9.     set duration = duration - TimerGetTimeout(t)
  10.     call SaveReal(udg_HashTable,key,0,duration)
  11.     if duration <= 0 then
  12.         call FlushChildHashtable(udg_HashTable,key)
  13.         call PauseTimer(t)
  14.         call DestroyTimer(t)
  15.     endif
  16.     set u = null
  17.     set t = null
  18. endfunction
  19.  
  20. function Register takes unit u, real duration returns nothing
  21.     local timer t = CreateTimer()
  22.     local integer key = GetHandleId(t)
  23.     call SaveUnitHandle(udg_HashTable,key,0,u)
  24.     call SaveReal(udg_HashTable,key,1,duration)
  25.     call TimerStart(t,0.5,true,function Execute)
  26. endfunction
Note: the periodic method can be replaced by trigger periodic method

==============================================

THANKYOU
PS: I haven't run these codes



Re: Which could be more efficient/better performance?
Reply #1 on: February 19, 2015, 11:16:51 AM

I'm elaborating an answer for you, hopefully this night I'll have some ideas for you :)


Re: Which could be more efficient/better performance?
Reply #2 on: February 19, 2015, 09:28:24 PM

Thanks admin  ;)

I am desperately need some references to enhance performance of systems in my map
Since ppl with low-spec PC keep struggling with my map



Re: Which could be more efficient/better performance?
Reply #3 on: February 21, 2015, 05:14:13 PM

The first thing is to start using some libraries to speed up your code, the first one is an indexing library like ALLOC: https://wc3modding.info/5012/snippet-allocloop-aka-alloc-2/

With this one the allocation of things would be dramatically increased. And it works because it's slower to create/destroy handles than reusing it.

I'll post some examples about what you can do with this baby.

Let's start with timers:

Code: jass
  1.     function RunThis takes nothing returns nothing
  2.         // code here. The timer hancle can be managed in this function to destroy or pause...
  3.     endfunction
  4.      
  5.     function InitTrig_test takes nothing returns nothing
  6.         call TimerStart(CreateTimer(),0.25,true,function RunThis) // with this you save a variable assignation...
  7.     endfunction

Using periodic triggers is a BIG no no. triggers are not recyclable as timers. And for time recycling there's a tool called timerutils.

About Unit grouping, the fastest way is the one you posted:

Code: jass
  1.     function GroupExec takes nothing returns nothing
  2.         // As you can see, I use an already created group, a bj one in order to make it faster in use, and you don't need to destroy it, it's cleaned up by the same loop...
  3.         local unit c = GetTriggerUnit()
  4.         local real x = GetUnitX(c)
  5.         local real y = GetUnitY(c)
  6.         local unit temp
  7.         call GroupEnumUnitsInRange(bj_lastCreatedGroup,x,y,300,null)
  8.         loop
  9.             set temp = FirstOfGroup(bj_lastCreatedGroup)
  10.         exitwhen temp == null
  11.             if IsUnitEnemy(temp,GetOwningPlayer(c)) then
  12.                 //execute each valid unit
  13.             endif
  14.             call GroupRemoveUnit(bj_lastCreatedGroup,temp)
  15.         endloop
  16.         set c = null
  17.     endfunction
« Last Edit: February 22, 2015, 08:50:01 PM by moyack »



Re: Which could be more efficient/better performance?
Reply #4 on: February 22, 2015, 08:52:11 PM

Answered some parts... :)


Re: Which could be more efficient/better performance?
Reply #5 on: February 23, 2015, 12:25:15 AM

The first thing is to start using some libraries to speed up your code, the first one is an indexing library like ALLOC: https://wc3modding.info/5012/snippet-allocloop-aka-alloc-2/
With this one the allocation of things would be dramatically increased. And it works because it's slower to create/destroy handles than reusing it.
Will alloc's allocate will instantly override current allocate method on struct?

Quote
Let's start with timers:

Code: jass
  1.     function RunThis takes nothing returns nothing
  2.         // code here. The timer hancle can be managed in this function to destroy or pause...
  3.     endfunction
  4.      
  5.     function InitTrig_test takes nothing returns nothing
  6.         call TimerStart(CreateTimer(),0.25,true,function RunThis) // with this you save a variable assignation...
  7.     endfunction

Using periodic triggers is a BIG no no. triggers are not recyclable as timers. And for time recycling there's a tool called timerutils.

I had used recycling system like Vex timerutils and dummy system in my map, but sometimes it is bugged, I don't know whether it is my fault or system's fault.
One of bug is when I use a recycled timer.
Timer that is retrieved for me is still currently being used with other instances (still active). So, I took a safe way to use default create and destroy.

Quote
About Unit grouping, the fastest way is the one you posted:

Code: jass
  1.     function GroupExec takes nothing returns nothing
  2.         // As you can see, I use an already created group, a bj one in order to make it faster in use, and you don't need to destroy it, it's cleaned up by the same loop...
  3.         local unit c = GetTriggerUnit()
  4.         local real x = GetUnitX(c)
  5.         local real y = GetUnitY(c)
  6.         local unit temp
  7.         call GroupEnumUnitsInRange(bj_lastCreatedGroup,x,y,300,null)
  8.         loop
  9.             set temp = FirstOfGroup(bj_lastCreatedGroup)
  10.         exitwhen temp == null
  11.             if IsUnitEnemy(temp,GetOwningPlayer(c)) then
  12.                 //execute each valid unit
  13.             endif
  14.             call GroupRemoveUnit(bj_lastCreatedGroup,temp)
  15.         endloop
  16.         set c = null
  17.     endfunction

Aw..
By judging on how you replace my g var, I conclude that it is better to using global variable than local variable, since globals are reusable and creates less handle (locals create one handle per creation)
Am I right?


Edit: http://www.thehelper.net/threads/new-leak-found-keeping-references-to-destroyed-objects.122136/
Got something interesting here, which telling us to null global/local handle var as soon as possible to reset handle ID counter

============================

Well looks like I will moving again from vanilla JASS to vJass one
Gracias admin ;D
« Last Edit: February 23, 2015, 02:54:44 AM by 8uY_YoU »