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
963
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
963
> 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:
    So what it really is me trying to implement some kind of better site navigation not change the whole theme of the site
  • Varine Varine:
    How can you tell the difference between real traffic and indexing or AI generation bots?
  • The Helper The Helper:
    The bots will show up as users online in the forum software but they do not show up in my stats tracking. I am sure there are bots in the stats but the way alot of the bots treat the site do not show up on the stats
  • Varine Varine:
    I want to build a filtration system for my 3d printer, and that shit is so much more complicated than I thought it would be
  • Varine Varine:
    Apparently ABS emits styrene particulates which can be like .2 micrometers, which idk if the VOC detectors I have can even catch that
  • Varine Varine:
    Anyway I need to get some of those sensors and two air pressure sensors installed before an after the filters, which I need to figure out how to calculate the necessary pressure for and I have yet to find anything that tells me how to actually do that, just the cfm ratings
  • Varine Varine:
    And then I have to set up an arduino board to read those sensors, which I also don't know very much about but I have a whole bunch of crash course things for that
  • Varine Varine:
    These sensors are also a lot more than I thought they would be. Like 5 to 10 each, idk why but I assumed they would be like 2 dollars
  • Varine Varine:
    Another issue I'm learning is that a lot of the air quality sensors don't work at very high ambient temperatures. I'm planning on heating this enclosure to like 60C or so, and that's the upper limit of their functionality
  • Varine Varine:
    Although I don't know if I need to actually actively heat it or just let the plate and hotend bring the ambient temp to whatever it will, but even then I need to figure out an exfiltration for hot air. I think I kind of know what to do but it's still fucking confusing
  • The Helper The Helper:
    Maybe you could find some of that information from AC tech - like how they detect freon and such
  • Varine Varine:
    That's mostly what I've been looking at
  • Varine Varine:
    I don't think I'm dealing with quite the same pressures though, at the very least its a significantly smaller system. For the time being I'm just going to put together a quick scrubby box though and hope it works good enough to not make my house toxic
  • Varine Varine:
    I mean I don't use this enough to pose any significant danger I don't think, but I would still rather not be throwing styrene all over the air
  • The Helper The Helper:
    New dessert added to recipes Southern Pecan Praline Cake https://www.thehelper.net/threads/recipe-southern-pecan-praline-cake.193555/
  • The Helper The Helper:
    Another bot invasion 493 members online most of them bots that do not show up on stats
  • Varine Varine:
    I'm looking at a solid 378 guests, but 3 members. Of which two are me and VSNES. The third is unlisted, which makes me think its a ghost.
    +1
  • The Helper The Helper:
    Some members choose invisibility mode
    +1
  • The Helper The Helper:
    I bitch about Xenforo sometimes but it really is full featured you just have to really know what you are doing to get the most out of it.
  • The Helper The Helper:
    It is just not easy to fix styles and customize but it definitely can be done
  • The Helper The Helper:
    I do know this - xenforo dropped the ball by not keeping the vbulletin reputation comments as a feature. The loss of the Reputation comments data when we switched to Xenforo really was the death knell for the site when it came to all the users that left. I know I missed it so much and I got way less interested in the site when that feature was gone and I run the site.
  • Blackveiled Blackveiled:
    People love rep, lol
    +1
  • The Helper The Helper:
    The recipe today is Sloppy Joe Casserole - one of my faves LOL https://www.thehelper.net/threads/sloppy-joe-casserole-with-manwich.193585/

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top