Snippet TimerQueue

Nestharus

o-o
Reaction score
84
Faster than even plain old timers with nothing in them and complete with data attachment, welcome to TimerQueue.

TimerQueue works by merging timers into queues of data and running methods on those queues. This has been thoroughly tested and has been compared to actual regular timers with nothing in them (beating them out pretty badly every time, especially when those timers attempt data attachment with a handle id and a hashtable).

These timers can be destroyed at any point as well.

The only thing is that the timer periods are constant (can't be changed). However, you can create a timer queue for each ability you have for optimal performance =).

Acceptably accurate for periods of .1 or greater.

Concept: http://www.hiveworkshop.com/forums/tutorial-submission-283/timer-queue-188346/#post1829782

Stress Tests
Code:
4000 timers running 32x a second

TimerQueue: 
    24-28 FPS

Regular timers with donothing: 
    <1 FPS

T32x: 
    57-60 FPS

JASS:

library TQ /* v2.0.1.0
*************************************************************************************
*
*   TimerQueue is a data structure in which timers starts one after another. This structure
*   only allows for constant periods as with dynamic periods it is unknown where to place
*   the expiring timer in the queue on expiration.
*
*************************************************************************************
*
*   module TQ
*
*       Interface (declare these in your struct)
*
*           private static constant real INTERVAL
*               -   Determines how often the timer expires.
*           private method expire takes nothing returns nothing
*               -   Called whenever the timer expires
*
*       Methods
*
*           static method allocate takes nothing returns thistype
*               -   Creates a new timer on the queue that will expire in INTERVAL seconds.
*               -   Returns the timer&#039;s instance.
*           method destroy takes nothing returns nothing
*               -   Destroys a timer. The timer is not actually destroyed until it is
*               -   encountered on the queue. The reason for this is because once a timer
*               -   is added to the queue, it can&#039;t be removed until it expires.
*               -   When encountering destroyed timers, their expiration code is not calld
*               -   and they are recycled.
*
*       Demonstration
*       --------------------------------------------------------------------
*           struct TimerQueueDemo extends array
*               private static constant real INTERVAL = 10
*
*               private method expire takes nothing returns nothing
*               endmethod
*
*               implement TQ
*
*               static method create takes nothing returns thistype
*                   return allocate()
*               endmethod
*               method destroy takes nothing returns nothing
*                   call deallocate()
*               endmethod
*           endstruct
*
*   module TQG
*
*       A timer queue group is essentially a group of timer queues all bundled
*       into the same struct and all using the same expire method.
*
*       A timer queue group allows one to use multiple periods, although this
*       group of periods must still be constant. A timer can&#039;t be created
*       with a timeout other than those found in the group.
*
*       Interface (declare these in your struct)
*
*           private method expire takes nothing returns nothing
*               -   Called whenever the timer expires
*
*       Methods
*
*           static method allocate takes real timeout returns thistype
*               -   Creates a new timer given a timeout. This timeout
*               -   must be one of the timeouts of the Timer Group.
*           method destroy takes nothing returns nothing
*               -   Destroys a timer. The timer is not actually destroyed until it is
*               -   encountered on the queue. The reason for this is because once a timer
*               -   is added to the queue, it can&#039;t be removed until it expires.
*               -   When encountering destroyed timers, their expiration code is not calld
*               -   and they are recycled.
*
*       //! textmacro TimerQueue takes ID, TIMER_QUEUE_STRUCT, TIMEOUT
*           -   This creates a new group for a timer queue struct.
*           -   This macro can only be run inside of a scope or library and it must
*           -   be run below the target Timer Queue Group struct.
*           -
*           -   ID                  Refers to the id of the group (must be unique)
*           -   TIMER_QUEUE_STRUCT  Refers to the timer queue group struct to add
*           -                       period to.
*           -   TIMEOUT             The timeout 
*
*       Demonstration
*       --------------------------------------------------------------------
*           scope t1
*               struct TimerQueueDemo extends array
*                   method expire takes nothing returns nothing
*                   endmethod
*
*                   implement TQG
*
*                   static method create takes nothing returns thistype
*                       return allocate(.03125)
*                   endmethod
*
*                   method destroy takes nothing returns nothing
*                       call deallocate()
*                   endmethod
*               endstruct
*
*               //! runtextmacro TimerQueue(&quot;1&quot;, &quot;TimerQueueDemo&quot;, &quot;.03125&quot;)
*               //! runtextmacro TimerQueue(&quot;2&quot;, &quot;TimerQueueDemo&quot;, &quot;5&quot;)
*           endscope
*           
************************************************************************************/
    globals
        //timer instance count
        public integer u = 0
        //recycle head
        public integer k = 0
        //next timer node
        public integer array n
        //timer queue count
        public integer f = 0
        //last timer on queue
        public integer array l
        //is timer last node on mini-queue?
        //each timer queue is split up into a set of merged mini queues
        //a mini queue is a set of timers that all expire at the same time
        //the last node on the queue is marked as end to tell when the queue ends
        //the first node is given the timeOffset to know when the next queue begins
        //and when to start up the mini queue
        public boolean array d
        //first timer on queue
        public integer array h
        
        //remaining time on timer queue
        public real array j
        //time offset on timer node
        public real array m
        
        //is timer destroyed?
        public boolean array z
        
        //array of all of the timers
        public timer array y
        //special trigger array for trigger group allocation specific queues aren&#039;t started
        //on allocation
        public trigger array a
        //retrieve timer queue from trigger group via interval
        public hashtable T = InitHashtable()
        //timer group count
        private integer S = 0
    endglobals
    
    //allocation
    public function I takes integer o, code c, real in returns integer
        //o is current timer
        //c is function handler
        //in is interval
        local integer x = o //timer queue
        local real w //offset
        if (k == 0) then //if recycle is empty allocate new
            set u = u + 1
            set o = u
        else //recycle
            set o = k
            set k = n[k]
        endif
        //if currently no timers on queue, start the timer up
        if (l[x] == 0) then
            if (c == null) then //if null, have to start via TriggerEvaluate
                                //as code arrays aren&#039;t supported
                call TriggerEvaluate(a[o])
            else
                call TimerStart(y[x], in, false, c) //happily have access to timer function
            endif
            set m[o] = 0 //set current time offset to 0 (Interval-RemainingTime is always 0)
            set h[x] = o //set first node on queue to current node
        else //timer queue is activated
            set w = j[x]-TimerGetRemaining(y[x]) //set offset to last remaining time - current remaining time
            if (w == 0) then //if w is 0, add to current mini queue
                set d[l[x]] = false //set current last end to false
            else //start up a new mini queue
                set j[x] = j[x]-w //decrease remaining time
                set m[o] = w //set timeOffset to difference between lastTimer and thisTimer
            endif
        endif
        
        set n[l[x]] = o //set next node of last timer to current node
        set l[x] = o //set last node to current node
        set n[o] = 0 //set next node of current to 0
        set d[o] = true //set end flag of current node to true (last timer on mini-queue)
        
        //return the new timer
        return o
    endfunction
    
    //deallocation
    public function D takes integer o returns nothing
        //o is this
        set z[o] = true //simply set destroyed to true
    endfunction
    
    //create new timer queue
    public function M takes real in returns integer
        //in is interval
        set f = f + 1 //increase timer queue count
        set y[f] = CreateTimer() //create a new timer for the queue
        set j[f] = in //set remaining timer on queue to interval
        return f
    endfunction
    
    //initialization
    private module tt
        private static method onInit takes nothing returns nothing
            //mark 0 end as true (if next is 0, will properly end)
            set d[0] = true
        endmethod
    endmodule
    private struct its extends array
        implement tt
    endstruct
    
    //timer group initialization
    public module Z
        private static method onInit takes nothing returns nothing
            //call internal method since macro params can&#039;t be retrieved here
            //must be within this module to properly do onInit
            call i9()
        endmethod
    endmodule
    
    //timer group queue
    module TQG
        //x is the id of the timer group queue
        readonly static integer x = 0
        static method allocate takes real timeout returns thistype
            //retrieve timer queue given an interval
            local thistype o = LoadInteger(TQ_T, x, R2I(timeout*100000))
            //if timer queue exists, allocate it
            if (o != 0) then
                //pass in null because the expire method is within the timer queue struct, not
                //the timer group struct
                return I(o, null, timeout)
            endif
            return 0
        endmethod
        method deallocate takes nothing returns nothing
            call D(this)
        endmethod
        
        private static method onInit takes nothing returns nothing
            //initialize timer group queue by incrementing total timer group
            //queues by 1 and setting the timer group id to the new count
            set S = S + 1
            set x = S
        endmethod
    endmodule
    
    //! textmacro TimerQueue takes ID, ROOT, TIMEOUT
        private struct $ROOT$$ID$ extends array
            //timer queue id
            private static integer x = 0
            //on expire method
            private static method e takes nothing returns nothing
                //set current timer queue to the timer queue id
                //local for speed
                local integer r = x
                //set the current timer to the head of the timer queue
                local integer o = TQ_h[r]
                //offset
                local real w
                //tempNext
                local thistype v
                //first reused timer
                //if a timer repeats, it gets reused
                //if not, it gets recycled
                local thistype b = 0
                loop
                    //if the timer isn&#039;t destroyed, call the expire method
                    if (not TQ_z[o]) then
                        call $ROOT$(o).expire()
                    endif
                    
                    //if the timer is destroyed, recycle the timer (don&#039;t add back to queue)
                    //this is done after the above in case the timer is destroyed within expire
                    if (TQ_z[o]) then
                        //set tempNext to current next timer so it&#039;s not lost
                        set v = TQ_n[o]
                        //recycle (recycle.next = this; recycle = this)
                        set TQ_n[o] = TQ_k
                        set TQ_k = o
                        //set destroyed to false
                        set TQ_z[o] = false
                    else
                        //add  the timer to the end of the queue
                        //last.next = this; last = this
                        set TQ_n[TQ_l[r]] = o
                        set TQ_l[r] = o
                        //if no timer marked as reused, mark it
                        if (b == 0) then
                            set b = o
                        endif
                        //set next to the next timer
                        set v = TQ_n[o]
                    endif
                    //exit when current timer is marked as last on mini queue
                    exitwhen TQ_d[o]
                    //set this to next
                    set o = v
                endloop
                //mark current timer flag as false (will always be marked as true)
                set TQ_d[o] = false
                //set current timer to saved next
                set o = v
                //set current head to current timer
                set TQ_h[r] = o
                //if current timer is 0, reset the timer and exit
                if (o == 0) then
                    set TQ_j[r] = $TIMEOUT$
                    set TQ_l[r] = 0
                else
                    //mark last timer as last timer on mini queue
                    set TQ_d[TQ_l[r]] = true
                    //set lastTimer.next to 0
                    set TQ_n[TQ_l[r]] = 0
                    //if last timer was the first timer marked then restart the entire queue
                    //this means that there is currently only 1 mini timer queue
                    if (o == b) then
                        //reset remaining time
                        set TQ_j[r] = $TIMEOUT$
                        //set offset to 0
                        set TQ_m[o] = 0
                        call TimerStart(TQ_y[r], $TIMEOUT$, false, function thistype.e)
                    else
                        //set offset to current offset
                        set w = TQ_m[o]
                        //set remaiing time to remaining time + offset
                        set TQ_j[r] = TQ_j[r]+w
                        //start using remaining time
                        call TimerStart(TQ_y[r], w, false, function thistype.e)
                        //if reused any timers, update remaining time
                        if (b != 0) then
                            set TQ_m<b> = TQ_j[r]-w
                            set TQ_j[r] = TQ_j[r]-TQ_m<b>
                        endif
                    endif
                endif
            endmethod 
            //trigger evaluation start timer for function thistype.e
            private static method s takes nothing returns boolean
                call TimerStart(TQ_y[x], $TIMEOUT$, false, function thistype.e)
                return false
            endmethod
            //initialization (onInit)
            private static method i9 takes nothing returns nothing
                //set timer queue id to a new timer queue
                set x = TQ_M($TIMEOUT$)
                //save id into timer queue group
                call SaveInteger(TQ_T, $ROOT$.x, R2I($TIMEOUT$*100000), x)
                //create a trigger in case it needs to be evaluated
                set TQ_a[x] = CreateTrigger()
                call TriggerAddCondition(TQ_a[x], Condition(function thistype.s))
            endmethod
            implement TQ_Z
        endstruct
    //! endtextmacro
    
    //TQ is virtually identical to TQd but works only with specific timer queues rather than groups
    module TQ
        //timer queue id
        private static integer x = 0
        private static method e takes nothing returns nothing
            local integer r = x
            local integer o = h[r]
            local real w
            local thistype v
            local thistype b = 0
            loop
                if (not z[o]) then
                    call thistype(o).expire()
                endif
                
                if (z[o]) then
                    set v = n[o]
                    set n[o] = k
                    set k = o
                    set z[o] = false
                else
                    set n[l[r]] = o
                    set l[r] = o
                    if (b == 0) then
                        set b = o
                    endif
                    set v = n[o]
                endif
                exitwhen d[o]
                set o = v
            endloop
            set d[o] = false
            set o = v
            set h[r] = o
            if (o == 0) then
                set j[r] = thistype.INTERVAL
                set l[r] = 0
            else
                set d[l[r]] = true
                set n[l[r]] = 0
                if (o == b) then
                    set j[r] = thistype.INTERVAL
                    set m[o] = 0
                    call TimerStart(y[r], thistype.INTERVAL, false, function thistype.e)
                else
                    set w = m[o]
                    set j[r] = j[r]+w
                    call TimerStart(y[r], w, false, function thistype.e)
                    if (b != 0) then
                        set m<b> = j[r]-w
                        set j[r] = j[r]-m<b>
                    endif
                endif
            endif
        endmethod 
        static method allocate takes nothing returns thistype
            return I(x, function thistype.e, thistype.INTERVAL)
        endmethod
        method deallocate takes nothing returns nothing
            call D(this)
        endmethod
        private static method onInit takes nothing returns nothing
            set x = M(thistype.INTERVAL)
        endmethod
    endmodule
endlibrary
</b></b></b></b>
 

tooltiperror

Super Moderator
Reaction score
231
Damn overkill, don't you think?

Just use TimerUtils. Seriously. You people keep trying to reinvent the wheel when the timers we have are fine. Is the performance in an actual map really going to need this? Think about it.
 

Romek

Super Moderator
Reaction score
964
I refuse to believe that something this bulky is even remotely fast, let alone faster than T32 (it's direct competition), or even native timers.

I'll post again once I've looked over this, which may take some time considering the way you name things...
 

Nestharus

o-o
Reaction score
84
Names messed with speed a lot... it was originally slower before the name changes, but doubled in speed after... I couldn't even believe it, was like WTF -.-, lol... Bribe was right when he told me that names really mess with speed a lot in JASS ;o.

And just paste those stress test benches in your map and look at the difference in FPS ; P. The empty timers actually have more overhead... crazy huh? Read the theory and you'll understand why this design is actually faster. Furthermore, the loop is pretty much a stack loop ([ljass]set this = this.next[/ljass]). It adds to the end of the queue if reusing the timer, otherwise destroys. It really minimizes a lot ;D.

Here is unoptimized code (including generating all variables rather than sharing with all other modules)

JASS:

library TimerQueue
    //1.0.0.0
    
    //module TimerQueueMod
    //-----------------------------------
        //Interface
        //-----------------------------------
            //private static constant real INTERVAL = SET_TO_YOUR_TIME
                //must be set to an interval (10, 15, w/e)
            //private method expire takes nothing returns nothing
                //called on timer expiration
        
        //Methods
        //-----------------------------------
            //static method allocate takes nothing returns thistype
            //method destroy takes nothing returns nothing
        
        /*Demo
        //-----------------------------------
            struct TimerQueueDemo extends array
                private static constant real INTERVAL = 10

                private method expire takes nothing returns nothing
                endmethod
                
                implement TimerQueueMod
                
                static method create takes nothing returns thistype
                    return allocate()
                endmethod
                method destroy takes nothing returns nothing
                    call deallocate()
                endmethod
            endstruct
        //-----------------------------------*/

    module TimerQueue
        //Timer Queue Variables
        ////////////////////////////////////////////////////////
            private static integer instanceCount = 0
            private static thistype recycle = 0
            private thistype nextTimer
            private static thistype lastTimer = 0
            private boolean end
            
            private static real remainingTime = INTERVAL
            
            private real timeStamp
            private real timeOffset
            private boolean destroyed
            
            private static timer timerQueue = CreateTimer()
        ////////////////////////////////////////////////////////

        private static method expires takes nothing returns nothing
            local thistype this = thistype(0).nextTimer
            local real offset
            local thistype next
            local thistype upd = 0
            loop
                if (not destroyed) then
                    //code
                    ////////////////////////////////////////////////////////
                    
                    call expire()
                    
                    ////////////////////////////////////////////////////////
                endif
                
                set next = nextTimer
                if (destroyed) then
                    set nextTimer = recycle
                    set recycle = this
                    set destroyed = false
                else
                    set lastTimer.nextTimer = this
                    set lastTimer = this
                    if (upd == 0) then
                        set upd = this
                    endif
                endif
                exitwhen end
                set this = next
            endloop
            set end = false
            set this = next
            set thistype(0).nextTimer = this
            
            if (this == 0) then
                set remainingTime = INTERVAL
                set lastTimer = 0
            else
                set lastTimer.end = true
                set lastTimer.nextTimer = 0
                if (this == upd) then
                    set remainingTime = INTERVAL
                    set timeOffset = 0
                    set lastTimer.end = true
                    call TimerStart(timerQueue, INTERVAL, false, function thistype.expires)
                else
                    set offset = timeOffset
                    set remainingTime = remainingTime+offset
                    call TimerStart(timerQueue, offset, false, function thistype.expires)
                    if (upd != 0) then
                        set upd.timeOffset = remainingTime-offset
                        set remainingTime = remainingTime-upd.timeOffset
                    endif
                endif
            endif
        endmethod
        
        static method allocate takes nothing returns thistype
            local thistype this
            local real offset
            if (recycle == 0) then
                set instanceCount = instanceCount + 1
                set this = instanceCount
            else
                set this = recycle
                set recycle = recycle.nextTimer
            endif
            
            if (lastTimer == 0) then
                call TimerStart(timerQueue, INTERVAL, false, function thistype.expires)
                set timeOffset = 0
            else
                set offset = remainingTime-TimerGetRemaining(timerQueue)
                if (offset == 0) then
                    set lastTimer.end = false
                else
                    set remainingTime = remainingTime-offset
                    set timeOffset = offset
                endif
            endif
            
            set lastTimer.nextTimer = this
            set lastTimer = this
            set nextTimer = 0
            set end = true
            
            return this
        endmethod
        
        method deallocate takes nothing returns nothing
            set destroyed = true
        endmethod
        
        private static method onInit takes nothing returns nothing
            set thistype(0).end = true
        endmethod
    endmodule
endlibrary
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
Faster than even plain old timers with nothing in them

You mean to tell me that this is faster than a timer using just the [ljass]CreateTimer[/ljass], [ljass]TimerStart[/ljass], and [ljass]DestroyTimer[/ljass] methods? I hardly think so, considering this uses those very same methods...
 

Nestharus

o-o
Reaction score
84
Well, when you start getting into multiple timers with the same period (start time doesn't matter), it gets faster and faster compared to regular timer natives. This is because it runs all of these timers on one timer, so it's not the same =).

As can be seen, only one timer is being set to run ^_-
Code:
createTimer(15) --runTimer1(15)
--8 seconds later--
createTimer(15) --runs 7 seconds after first timer
--3 seconds later--
createTimer(15)  --runs 4 seconds after second timer

--4 seconds later-
timer1 expires
runTimer2(7)

--7 seconds later--
timer2 expires
runTimer3(4)

edit
Trying to work on a dynamic time version =)
 

Romek

Super Moderator
Reaction score
964
> Names messed with speed a lot... it was originally slower before the name changes, but doubled in speed after...
I was afraid you'd say something like that. :(

How does this compare to T32?
 

Nestharus

o-o
Reaction score
84
It would be a quite a lot slower... using T32 for super small timers is still your best bet ^_-.

This was made more to run timers that required accuracy really fast. With the new updates (smaller var names, rofl rofl), this can so far handle 2000+ timers on a .03125 interval at 60 FPS (stopped at 2000 ^_^).

edit 2.0.0.0
Fixed a bug having to do with single timer instances

Added Tqg


Just tested 4000 timers running 32x a second and 24-28 FPS!

Standard timers with a donothing in them ran the same test and had <1 FPS!


T32x: 57-60
 

Troll-Brain

You can change this now in User CP.
Reaction score
85
The world needed this.

On a serious note, just to be sure, you tested to call this wonderfull function DoNothing() in each kind of Timer callback (your script, native Timer callbacks and T32x), right ?
Now in a real case scenario, where timer callback contains at very least some lines of code, can you notice any difference between all these timer callbacks ?
 

Nestharus

o-o
Reaction score
84
->Now in a real case scenario, where timer callback contains at very least some lines of code, can you notice any difference between all these timer callbacks ?

Very real difference yes...


And for the tests

For T32x, it was period.
For TimerQueue, it was expire.
For regular timers, it was an empty function called donothing

JASS:

function donothing takes nothing returns nothing
endfunction
////////////////////
call TimerStart(CreateTimer(), .03125, true, function donothing)
 

Nestharus

o-o
Reaction score
84
Vex's optimizer doesn't work with native declarations in map from what i heard, so there isn't an optimizer right now that'll actually work, hence my own optimization application.

I'm currently commenting the code so that I can read it : P. I get frustrated trying to make sense of all of the variables : D.
 

Jesus4Lyf

Good Idea™
Reaction score
397
Vex's optimizer doesn't work with native declarations in map from what i heard, so there isn't an optimizer right now that'll actually work, hence my own optimization application.
So based on heresay you thought you have two options:
1. Wrap the Optimizer in a simple script to remove native declarations and then re-add them later after the Optimizer runs over the map.
2. Reinvent the Optimizer.

You chose: Reinvent the Optimizer.
I'd say you're reinventing the horse. The wheel will come later..

Ok, so the theory of this system is only have one time running at any one time, and then have that timer kick off new ones later, is that it? True, it might be theoretically faster for a very specific application, it's just so unrealistic to actually happen.
 

Nestharus

o-o
Reaction score
84
Ok, so the theory of this system is only have one time running at any one time, and then have that timer kick off new ones later, is that it? True, it might be theoretically faster for a very specific application, it's just so unrealistic to actually happen.

1 timer for a timer queue. As timers are added to it, their timeouts are based on the current remaining time on the timer queue (total time of all timeouts in it).

As the timer expires, it loops through the current set of expires timers and runs their methods. If they are destroyed, it recycles them. If they aren't, it adds them back into the queue.

At the end, it updates the remaining time on the entire queue through a simple operation and then starts the timer back up using the offset of the next set of timers.

Because TimerStart calls are <= the normal TimerStart calls with regular timers, this is actually more optimal even when 0 timers are merged.

I'm currently documenting the code as I said and am finding some extra sets in there as I do it : P. I'll have 2.0.1.0 up fully documented tonight so that you can really read through the code ; P.

edit
Commented code and removed 2 extra boolean sets (won't make much of a dif)


edit
Not planning any future updates. Ready to go.
 

Jesus4Lyf

Good Idea™
Reaction score
397
Here's a tutorial for you, too. That should be enlightening.
By the way, I had the idea for this a long time ago, and figured it would be faster than TU. I figured it wasn't worthwhile. I think I was calling it "drop timers" or something, the theory of "dropping" the code and the data at the same point in time, so when they land they're side by side... anyway, isn't this going to be inaccurate if it adds up a whole bunch of durations? WC3 rounding errors gonna hit you..?
 

Nestharus

o-o
Reaction score
84
Here's a tutorial for you, too. That should be enlightening.
well, the code has hardcore comments and the short variable names drastically mess with the speed of the script ;o.

"drop timers"
The correct term for this is actually a timer queue, lol. Let's try and stay away from using our own unique terms =), like Vexorian's Awesome Timers or w/e.

anyway, isn't this going to be inaccurate if it adds up a whole bunch of durations

Acceptably accurate for periods of .1 or greater =). And no, it's not wc3 rounding but rather the spec on how floats work (like the thing I posted about .031250000 on the T32 thread).

Yes, it'll lose a little accuracy, but the speed benefits are pretty great. If you look at timer queues in regular programming (like c++), those lose accuracy too at the same rate this does, so this is done in a pretty standard fashion.

The tutorial is pretty darn helpful for understanding how this works and how much accuracy loss can be expected.

Here is a quote from the tutorial for your convenience.
The one problem with timer queues is the accuracy. Reals don't have great accuracy (.9999 isn't .9999, but rather .9998 with a bundle of decimals). This means that as timers expire, they will not expire at the exact time they were meant to (remember that the concept works with reals for retrieving the differences between expiring timers). The good thing is that timers will always be within 1% (more likely .1%) of when they were supposed to expire relative to when they last expired (not first; if basing relative to first, the error will be cumulative, meaning as the timer expires, it's error will be .1%, then .2%, then .3%, etc).
 

tooltiperror

Super Moderator
Reaction score
231
Speed in JASS isn't such an issue.

Using TimerUtils, people will probably never notice a difference. In the rare circumstance that TU would be less efficient, T32 does fine.

Adding in another system for .0000001 seconds efficiency is pointless.
 

Nestharus

o-o
Reaction score
84
AttackIndexer ran up to 8191 timers with 60 second timeouts (quite a large overhead with the handles no?). The Timer Queue drastically reduces the overhead and increases the speed (this script was actually written initially for use in attack indexing).

Old unit decaying was done by assigned a timer to each and every unit. With timer queue, one timer can be used (although unit decaying can just be done with UnitEvent now).

Most abilities run on a set of constant times and could greatly benefit from using timer queue groups.

The overhead is less (less handles) and the speed is greater (less timers running, less dynamic code, and no need for hashtable + GetHandleId).

All of these are covered in the tutorial. I'm not saying timer queues should be used for everything, but some things could improve with them.

Really, a map should probably have TimerUtils, TimerQueue, and T32 in it (pick the technique that's best for what you are doing). Each technique has its pluses and its negatives.

Also, I just saw a documentation error. The method for destruction is deallocate, not destroy ;D. I'll fix that right now.
 

tooltiperror

Super Moderator
Reaction score
231
Timer2 by Bribe seems to employ a queue in a more elegant way than this, and T32 and TU are better for other circumstances.

I'm going to graveyard this, unless Nestharus can give me a well formulated argument as to why it should live.
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • The Helper The Helper:
    I am great and it is fantastic to see you my friend!
    +1
  • The Helper The Helper:
    If you are new to the site please check out the Recipe and Food Forum https://www.thehelper.net/forums/recipes-and-food.220/
  • Monovertex Monovertex:
    How come you're so into recipes lately? Never saw this much interest in this topic in the old days of TH.net
  • Monovertex Monovertex:
    Hmm, how do I change my signature?
  • tom_mai78101 tom_mai78101:
    Signatures can be edit in your account profile. As for the old stuffs, I'm thinking it's because Blizzard is now under Microsoft, and because of Microsoft Xbox going the way it is, it's dreadful.
  • The Helper The Helper:
    I am not big on the recipes I am just promoting them - I use the site as a practice place promoting stuff
    +2
  • Monovertex Monovertex:
    @tom_mai78101 I must be blind. If I go on my profile I don't see any area to edit the signature; If I go to account details (settings) I don't see any signature area either.
  • The Helper The Helper:
    You can get there if you click the bell icon (alerts) and choose preferences from the bottom, signature will be in the menu on the left there https://www.thehelper.net/account/preferences
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?
  • The Helper The Helper:
    Happy Thursday!
    +1
  • Varine Varine:
    Crazy how much 3d printing has come in the last few years. Sad that it's not as easily modifiable though
  • Varine Varine:
    I bought an Ender 3 during the pandemic and tinkered with it all the time. Just bought a Sovol, not as easy. I'm trying to make it use a different nozzle because I have a fuck ton of Volcanos, and they use what is basically a modified volcano that is just a smidge longer, and almost every part on this thing needs to be redone to make it work
  • Varine Varine:
    Luckily I have a 3d printer for that, I guess. But it's ridiculous. The regular volcanos are 21mm, these Sovol versions are about 23.5mm
  • Varine Varine:
    So, 2.5mm longer. But the thing that measures the bed is about 1.5mm above the nozzle, so if I swap it with a volcano then I'm 1mm behind it. So cool, new bracket to swap that, but THEN the fan shroud to direct air at the part is ALSO going to be .5mm to low, and so I need to redo that, but by doing that it is a little bit off where it should be blowing and it's throwing it at the heating block instead of the part, and fuck man
  • Varine Varine:
    I didn't realize they designed this entire thing to NOT be modded. I would have just got a fucking Bambu if I knew that, the whole point was I could fuck with this. And no one else makes shit for Sovol so I have to go through them, and they have... interesting pricing models. So I have a new extruder altogether that I'm taking apart and going to just design a whole new one to use my nozzles. Dumb design.
  • Varine Varine:
    Can't just buy a new heatblock, you need to get a whole hotend - so block, heater cartridge, thermistor, heatbreak, and nozzle. And they put this fucking paste in there so I can't take the thermistor or cartridge out with any ease, that's 30 dollars. Or you can get the whole extrudor with the direct driver AND that heatblock for like 50, but you still can't get any of it to come apart
  • Varine Varine:
    Partsbuilt has individual parts I found but they're expensive. I think I can get bits swapped around and make this work with generic shit though
  • Ghan Ghan:
    Heard Houston got hit pretty bad by storms last night. Hope all is well with TH.
  • The Helper The Helper:
    Power back on finally - all is good here no damage
    +2
  • V-SNES V-SNES:
    Happy Friday!
    +1
  • The Helper The Helper:
    New recipe is another summer dessert Berry and Peach Cheesecake - https://www.thehelper.net/threads/recipe-berry-and-peach-cheesecake.194169/

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top