Which could be more efficient/better performance?

Coding Help

11 years
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) Select
function RunThis takes nothing returns nothing
    // code here
endfunction

function InitTrig_test takes nothing returns nothing
    local timer t = CreateTimer(  )
    call TimerStart(t,0.25,true,function RunThis)
endfunction


b. Using Trigger
Code (jass) Select
function RunThis takes nothing returns boolean
    // code here
    return false
endfunction

function InitTrig_test takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterTimerEvent(t,0.25,true)
    call TriggerAddCondition(t,Condition(function RunThis))
endfunction


2. Group Execution

a. Blizz-way (Filtering then Execute)
Code (jass) Select
function GroupExecuteUnits takes nothing returns nothing
    //execute each valid unit
endfunction

function GroupFilter takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(GetTriggerUnit()))
endfunction

function GroupExec takes nothing returns nothing
    local group g = CreateGroup()
    local unit c = GetTriggerUnit()
    local real x = GetUnitX(c)
    local real y = GetUnitY(c)
    call GroupEnumUnitsInRange(g,x,y,300,Filter(function GroupFilter))
    call ForGroup(g, function GroupExecuteUnits)
    call DestroyGroup(g)
    set g = null
    set c = null
endfunction


b. In one function way
Code (jass) Select
function GroupExec takes nothing returns nothing
    local group g = CreateGroup()
    local unit c = GetTriggerUnit()
    local real x = GetUnitX(c)
    local real y = GetUnitY(c)
    local unit temp
    call GroupEnumUnitsInRange(g,x,y,300,null)
    loop
        set temp = FirstOfGroup(g)
    exitwhen temp == null
        if IsUnitEnemy(temp,GetOwningPlayer(c)) then
            //execute each valid unit
        endif
        call GroupRemoveUnit(g,temp)
    endloop
    call DestroyGroup(g)
    set g = null
    set c = null
endfunction


c. What-i-am-currently-using way
Code (jass) Select
function GroupFilter takes nothing returns boolean
    if IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(GetTriggerUnit())) then
        //execute each valid unit
    endif
    return false
endfunction

function GroupExec takes nothing returns nothing
    local group g = CreateGroup()
    local unit c = GetTriggerUnit()
    local real x = GetUnitX(c)
    local real y = GetUnitY(c)
    local filterfunc ff = Filter(function GroupFilter)
    call GroupEnumUnitsInRange(g,x,y,300,ff)
    call DestroyGroup(g)
    call DestroyFilter(ff)
    set ff = null
    set g = null
    set c = null
endfunction


3. Multi-Instancing]

a. Indexing (in this case, one timer for all instances)
Code (jass) Select
function Execute takes nothing returns nothing
    local integer i = 1
    loop
    exitwhen i > udg_INDEX
        set udg_Durations[i] = udg_Durations[i] - TimerGetTimeout(udg_Time)
        if udg_Durations[i] > 0 then
            //do a heal/damage to udg_Units[i]
        else
            if i != udg_INDEX then
                set udg_Durations[i] = udg_Durations[udg_INDEX]
                set udg_Units[i] = udg_Units[udg_INDEX]
            endif
            set udg_INDEX = udg_INDEX - 1
            set i = i - 1
        endif
        set i = i + 1
    endloop

    if udg_INDEX == 0 then
        call PauseTimer(udg_Time)
    endif
endfunction

function Register takes unit u, real duration returns nothing
    set udg_INDEX = udg_INDEX + 1
    set udg_Units[udg_INDEX] = u
    set udg_Durations[udg_INDEX] = duration
    if udg_INDEX == 1 then
        call TimerStart(udg_Time,0.5,true,function Execute)
    endif
endfunction


b. Keying To Handle (in this case, one timer for one instance)
Code (jass) Select
function Execute takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer key = GetHandleId(t)
    local unit u = LoadUnitHandle(udg_HashTable,key,0)
    local real duration = LoadReal(udg_HashTable,key,1)
   
    ////do a heal/damage to unit "u"
   
    set duration = duration - TimerGetTimeout(t)
    call SaveReal(udg_HashTable,key,0,duration)
    if duration <= 0 then
        call FlushChildHashtable(udg_HashTable,key)
        call PauseTimer(t)
        call DestroyTimer(t)
    endif
    set u = null
    set t = null
endfunction

function Register takes unit u, real duration returns nothing
    local timer t = CreateTimer()
    local integer key = GetHandleId(t)
    call SaveUnitHandle(udg_HashTable,key,0,u)
    call SaveReal(udg_HashTable,key,1,duration)
    call TimerStart(t,0.5,true,function Execute)
endfunction

Note: the periodic method can be replaced by trigger periodic method

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

THANKYOU
PS: I haven't run these codes
11 years
I'm elaborating an answer for you, hopefully this night I'll have some ideas for you :)
11 years
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
11 years
edited 11 years
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) Select
    function RunThis takes nothing returns nothing
        // code here. The timer hancle can be managed in this function to destroy or pause...
    endfunction
     
    function InitTrig_test takes nothing returns nothing
        call TimerStart(CreateTimer(),0.25,true,function RunThis) // with this you save a variable assignation...
    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) Select
    function GroupExec takes nothing returns nothing
        // 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...
        local unit c = GetTriggerUnit()
        local real x = GetUnitX(c)
        local real y = GetUnitY(c)
        local unit temp
        call GroupEnumUnitsInRange(bj_lastCreatedGroup,x,y,300,null)
        loop
            set temp = FirstOfGroup(bj_lastCreatedGroup)
        exitwhen temp == null
            if IsUnitEnemy(temp,GetOwningPlayer(c)) then
                //execute each valid unit
            endif
            call GroupRemoveUnit(bj_lastCreatedGroup,temp)
        endloop
        set c = null
    endfunction
11 years
Answered some parts... :)
11 years
edited 11 years
Quote from: moyack 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.
Will alloc's allocate will instantly override current allocate method on struct?

Quote
Let's start with timers:

Code (jass) Select
    function RunThis takes nothing returns nothing
        // code here. The timer hancle can be managed in this function to destroy or pause...
    endfunction
     
    function InitTrig_test takes nothing returns nothing
        call TimerStart(CreateTimer(),0.25,true,function RunThis) // with this you save a variable assignation...
    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) Select
    function GroupExec takes nothing returns nothing
        // 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...
        local unit c = GetTriggerUnit()
        local real x = GetUnitX(c)
        local real y = GetUnitY(c)
        local unit temp
        call GroupEnumUnitsInRange(bj_lastCreatedGroup,x,y,300,null)
        loop
            set temp = FirstOfGroup(bj_lastCreatedGroup)
        exitwhen temp == null
            if IsUnitEnemy(temp,GetOwningPlayer(c)) then
                //execute each valid unit
            endif
            call GroupRemoveUnit(bj_lastCreatedGroup,temp)
        endloop
        set c = null
    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