• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[General] First design of general Timer System has failed : (

Status
Not open for further replies.
So this is it.. except that the data retrieval idea was a failure..

The problems:
- ResumeTimer is extremely slow
- The data retrieval in it was actually slower than a hashtable + get handle id read..

->
JASS:
        static method operator expired takes nothing returns thistype
            return l[R2I(TimerGetRemaining(GetExpiredTimer())*100+.5)]
        endmethod


I decided to put this up here so that people could look at my little experimental timer library.

Fear not, I shall try again >: ).

My next idea involves a minimum binary heap >: o (exactly how JASS timers work except that they use <= rather than <).

The only problem with this idea is that I'd need to use the TimerGetRemaining native in order to read the timeouts /cry.

Actually... I just compared 80 timer get remaining calls to 40 hashtable + get handle id calls and the latter won by quite a bit.

I guess dynamic times in a timer system is just never going to beat hashtable + get handle id : |. It's never even going to come anywhere close.

If there is even an R2I call in there, a timer system will lose.

Anyways, if only arrays were actually arrays and JASS math wasn't so slow and R2I was just a type cast, then this would probably own 2 hashtable reads >.> (GetHandleId and LoadInteger).


But anyways, if anyone has an idea as to how to sort timers w/o the use of TimerGetRemaining, that'd be awesome.


I guess this also means that KT2 officially sucks since this one is already better.

At this line, KT2 already loses >.<
set t_id=R2I(TimerGetTimeout(GetExpiredTimer())*800)

So... anywhere, here is my experimental timer system that failed if anyone wants to look ^)^.

ResumeTimer is a heavy operation (one reason for failure)
R2I is another pretty heavy operation (another reason for failure)
JASS:
library Timer /* v1.0.0.0
*************************************************************************************
*
*   Essentially TimerUtils
*
*   Cons
*       Only between 0 and 81.91 second timeouts and two decimal places
*
*       No chance of merging timers
*           -   Merging timers (like what is done in KT2) is extremely rare. In most
*           -   cases it will be a timer that evaluates a trigger with the added
*           -   overhead of building and cleaning triggers. What this means
*           -   is that most of the time the timers will end up being slower
*           -   than standard timers.
*
*       No auto restarting timer
*           -   The timer has to be resumed with the resume method
*
*       No auto destroying timer
*           -   The timer has to be destroy method
*
*   Pros
*       Timers can be paused, restarted, and destroyed at any point
*
*       Dynamic timeouts
*
*       Supports fully dynamic code (like TimerStart)
*
*       Data retrieval inlines
*
*       No hashtable or GetHandleId calls
*
*       No loops
*
************************************************************************************
*
*   struct Timer extends array
*
*       integer value
*           -   Value stored within the timer.
*       readonly boolean paused
*           -   Signifies whether the timer is paused or not.
*       readonly Timer expired
*           -   The timer that expired. Only accessible within the expiring timer.
*
*       readonly real timeout
*           -   Timeout of timer
*       readonly real remaining
*           -   Remaining time until timer expires
*       readonly real elapsed
*           -   How long the timer has been running
*
*       readonly timer timer
*           -   Useful for timer dialogs
*           -   Only read this value, do not manipulate directly!
*
*       static method create takes real timeout, integer value, code callBack returns Timer
*           -   Creates and starts a new timer
*           -   Returns the new timer
*           -   Simmilar to StartTimer(CreateTimer())
*       method destroy takes nothing returns nothing
*           -   Destroys timer
*           -   Similer to DestroyTimer
*
*       method pause takes nothing returns nothing
*           -   Pauses timer
*           -   Similer to PauseTimer
*       method resume takes nothing returns nothing
*           -   Resumes timer and restarts expired timers
*           -   Similar to ResumeTimer
*       method start takes code callBack, real timeout returns nothing
*           -   Starts timer
*           -   Similer to TimerStart
*
************************************************************************************/
    globals
        private timer array t   //timers
        private integer c=0     //instance count
        private integer array n //next
        private integer array p //previous
        private integer array l //list array
        private boolean array e //end
        private integer array r //list of node
        private real array g    //timeout
        private integer array u //temporary list of node
    endglobals
    struct Timer extends array
        integer value
        readonly boolean paused
        method operator timeout takes nothing returns real
            return g[this]
        endmethod
        method operator remaining takes nothing returns real
            return TimerGetRemaining(t[this])
        endmethod
        method operator elapsed takes nothing returns real
            return TimerGetElapsed(t[this])
        endmethod
        method operator timer takes nothing returns timer
            return t[this]
        endmethod
        static method operator expired takes nothing returns thistype
            return l[R2I(TimerGetRemaining(GetExpiredTimer())*100+.5)]
        endmethod
        static method create takes real w, integer o, code b returns thistype
            local Timer a
            local integer s=R2I(w*100+.5)   //head
            local integer y=l[s]            //list
            
            //allocate new node
            if (0==n[0]) then               //new allocation
                set a=c+1
                set c=a
                set t[a]=CreateTimer()
                set a.paused=false
            else                            //recycled allocation
                set a=n[0]
                set n[0]=n[a]
                set u[a]=0
            endif
            
            //set values
            set r[a]=s          //list
            set g[a]=s/100.     //timeout
            set a.value=o       //value
            
            //add new node to list
            if (0==y) then                  //add to empty list
                set l[s]=a
                set n[a]=a
                set p[a]=a
                set e[a]=true
            else                            //add to non empty list
                set n[p[y]]=a
                set p[a]=p[y]
                set n[a]=y
                set p[y]=a
            endif
            
            //start timer
            call TimerStart(t[a],g[a],true,b)
            call PauseTimer(t[a])
            call ResumeTimer(t[a])
            
            return a
        endmethod
        
        method destroy takes nothing returns nothing
            //retrieve list
            local integer s=u[this]         //retrieve temporary list
            if (0==s) then                  //retrieve regular list
                set s=r[this]
            endif
            
            //remove from list
            set n[p[this]]=n[this]
            set p[n[this]]=p[this]
            if (e[this]) then                   //handle head
                if (e[n[this]]) then            //if only node remaining, clear list
                    set l[s]=0
                else                            //set next node to head
                    set e[n[this]]=true
                    set l[s]=n[this]
                endif
                set e[this]=false               //unmark as head
            endif
            
            //deallocate
            set n[this]=n[0]
            set n[0]=this
            
            //pause timer
            call PauseTimer(t[this])
        endmethod
        
        method pause takes nothing returns nothing
            //retrieve list
            local integer s=u[this]             //retrieve temporary list
            local integer y
            if (not paused) then                //only run if the node isn't already paused
                set paused=true                 //mark as paused
            
                if (0==s) then                  //retrieve regular list
                    set s=r[this]
                endif
                
                //remove from list
                set n[p[this]]=n[this]
                set p[n[this]]=p[this]
                if (e[this]) then               //handle head
                    if (e[n[this]]) then        //if only node remaining, clear list
                        set l[s]=0
                    else                        //set next node to head
                        set e[n[this]]=true
                        set l[s]=n[this]
                    endif
                    set e[this]=false           //unmark as head
                endif
                
                //pause timer
                call PauseTimer(t[this])
                
                //retrieve temporary list
                set u[this]=R2I(TimerGetRemaining(t[this])*100+.5)
                
                //unset list pointers (for safe destruction)
                set n[this]=this
                set p[this]=this
            endif
        endmethod
        method resume takes code b returns nothing
            //retrieve list
            local integer s=u[this]             //retrieve temporary list
            local integer y                     //retrieve regular list
            if (0==s) then
                set s=r[this]
            endif
            set y=l[s]                          //retrieve first node in list
            
            if (paused) then                    //add to new list
                set paused=false
                
                //add node to list
                if (0==y) then                  //add to empty list
                    set l[s]=this
                    set n[this]=this
                    set p[this]=this
                    set e[this]=true
                else                            //add to non empty list
                    set n[p[y]]=this
                    set p[this]=p[y]
                    set n[this]=y
                    set p[y]=this
                endif
            elseif (e[this]) then               //push to back of list
                                                //if e[this] is false, then resume was called
                                                //on a timer that's still running
                                                
                if (0==u[this]) then            //next on current list
                    set e[this]=false
                    set e[n[this]]=true
                    set l[s]=n[this]
                else                            //remove from current list (came from a temporary list)
                    set u[this]=0               //unmark temporary
                    
                    //remove from list
                    set n[p[this]]=n[this]
                    set p[n[this]]=p[this]
                    if (e[this]) then               //handle head
                        if (e[n[this]]) then        //if only node remaining, clear list
                            set l[s]=0
                        else                        //set next node to head
                            set e[n[this]]=true
                            set l[s]=n[this]
                        endif
                        set e[this]=false           //unmark as head
                    endif
                    
                    //retrieve regular list
                    set s=r[this]
                    set y=l[s]
                    
                    //add node to list
                    if (0==y) then                  //add to empty list
                        set l[s]=this
                        set n[this]=this
                        set p[this]=this
                        set e[this]=true
                    else                            //add to non empty list
                        set n[p[y]]=this
                        set p[this]=p[y]
                        set n[this]=y
                        set p[y]=this
                    endif
                    call TimerStart(t[this],g[this],false,b)
                    call PauseTimer(t[this])
                endif
            endif
            
            //resume the timer
            call ResumeTimer(t[this])
        endmethod
        
        method start takes code b, real w returns nothing
            local integer y
        
            //retrieve list
            local integer s=u[this]         //retrieve temporary list
            if (0==s) then                  //retrieve regular list
                set s=r[this]
            else
                set u[this]=0               //clear out temporary (restarting)
            endif
            
            set paused=false
            
            //remove from list
            set n[p[this]]=n[this]
            set p[n[this]]=p[this]
            if (e[this]) then               //handle head
                if (e[n[this]]) then        //if only node remaining, clear list
                    set l[s]=0
                else                        //set next node to head
                    set e[n[this]]=true
                    set l[s]=n[this]
                endif
                set e[this]=false           //unmark as head
            endif
            
            //retrieve new list
            set s=R2I(w*100+.5)             //list index
            set y=l[s]                      //first node in list
            
            //add node to list
            if (0==y) then                  //add to empty list
                set l[s]=this
                set n[this]=this
                set p[this]=this
                set e[this]=true
            else                            //add to non empty list
                set n[p[y]]=this
                set p[this]=p[y]
                set n[this]=y
                set p[y]=this
            endif
            
            //update values
            set r[this]=s          //list
            set g[this]=s/100.     //timeout
            
            //start timer using new timeout
            call TimerStart(t[this],g[this],false,b)
            call PauseTimer(t[this])
            call ResumeTimer(t[this])
        endmethod
    endstruct
endlibrary
 
TimerUtils Orange/Red are pretty exceptionally fast.
If I had to use a fast attachment system that'd be a
good way to go.

The problem is that you put too much emphasis on
speed. A timer that expires every 1+ seconds does
not have to win any benchmarks. Even if it's 10x
slower than a hashtable lookup, you could have lots
of thousands of hashtable lookups before any lag was
caused, even on a bad computer.

I think the concept behind Timer2 is pretty solid actually.
I'd rather use a slower timer system that hardly uses
any RAM. I think we should keep building on that concept
to make it as efficient as possible.
 
Well, if you took this and put it on a binary heap... or something similar to my priority queue... then you had a trigger on the heap with a trigger condition for each node... you could have this entire thing running on 1 timer for each type of timeout and you'd also have extremely fast data retrieval.

You could go to 1 timer for the whole system, but that could incur exceptional overhead.


What would happen is you would have a loop and exit when the root's not expired.

JASS:
loop
    trigger evaluate (queue changed in trigger evaluation)
    remove root and re insert it
    if (root remaining time isn't 0) then
        trigger evaluate root
    endif
endloop
 
Status
Not open for further replies.
Top