Nestharus
o-o
- Reaction score
- 84
JASS:
library T32s /* v2.0.1.0
*************************************************************************************
*
* Safe version of T32 that helpsavoids op limit. Loop is same speed as T32x when THRESH is
* 8191. Timer that expires 32 times a second.
*
*************************************************************************************
*
* module T32s
*
* Interface (declare these in your struct)
* private static constant integer THRESH
* - Determines how much to segment timer. Lower segment means
* - less chance to hit op limit but lower speeds. Higher segments
* - is higher chance to hit op limit, but higher speeds. T32x segment
* - is 8191.
* private method expire takes nothing returns nothing
* - Called whenever timer expires
*
* method start takes nothing returns thistype
* - adds current instance to timer list (start timer)
* - returns the instance for easy local thistype this = create().start()
* method stop takes nothing returns nothing
* - removes current instance from timer list (stop timer)
*
************************************************************************************/
globals
private timer t = CreateTimer() //regular timer
private trigger tr = CreateTrigger() //expiration trigger
private triggercondition array rp //condition to be destroyed
private integer rc = 0 //recycler count
private integer ic = 0 //instance count
endglobals
private function Run takes nothing returns nothing
call TriggerEvaluate(tr)
endfunction
//recycler function
private function Rec takes nothing returns nothing
loop
set rc = rc - 1
call TriggerRemoveCondition(tr, rp[rc])
set ic = ic - 1
set rp[rc] = null
exitwhen rc == 0
endloop
if (ic > 0) then
//run expiration
call TriggerEvaluate(tr)
//start the regular timer back up
call TimerStart(t, .031250000, true, function Run)
endif
endfunction
module T32s
private static triggercondition array a //periodic code
private static thistype array n //next
private static thistype array p //previous
private static thistype e = 0 //current expired
private static integer c = 0 //current instance count
private static thistype array w //next first
private static thistype array q //previous first
private static boolean array f //first
private static integer array li //list id
private static integer lc = 0 //list count
private static integer array ni //node id
private static integer array an //absolute next
private static integer array ap //absolute previous
//runs mini lists (runs all the code each period eventually)
//this can be called multiple times based on segment
//timer list is split into lists that are each on their own
//trigger condition
private static method run takes nothing returns boolean
loop
call e.expire()
set e = n[e]
exitwhen f[e]
endloop
return false
endmethod
method start takes nothing returns thistype
local integer i
//ensure timer isn't already allocated and isn't null
debug if (n[this] == 0 and this != 0) then
//if need to add a new segment to list
if (c-c/THRESH*THRESH == 0) then
set a[c] = TriggerAddCondition(tr, function thistype.run)
if (ic == 0) then
call TimerStart(t, .031250000, true, function Run)
endif
set ic = ic + 1
//add to segment list
set w[q[0]] = this
set q[this] = q[0]
set w[this] = 0
set q[0] = this
//mark as a first node
set f[this] = true
set li[lc] = this
set lc = lc + 1
endif
set ni[this] = lc
//if nothing in the list, just make current head
if (c == 0) then
//update current expired
set e = this
//add to segmented list
set n[this] = this
set p[this] = this
set n[0] = this
set f[this] = true
//add to absolute list
set an[0] = this
set ap[0] = this
set an[this] = 0
set ap[this] = 0
//otherwise add to list
else
//add to segmented list
set n[p[0]] = this
set p[this] = p[0]
set n[this] = n[0]
set p[n[0]] = this
//add to absolute list
set an[ap[0]] = this
set ap[this] = ap[0]
set an[this] = 0
set ap[0] = this
endif
//always update last
set p[0] = this
set c = c + 1
debug else
//if null, display error
debug if (this == 0) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "T32s ERROR: ATTEMPT TO START NULL TIMER")
//if already allocated, display error
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "T32s ERROR: ATTEMPT TO START TIMER MULTIPLE TIMES")
debug endif
debug endif
return this
endmethod
//stops a timer instance
method stop takes nothing returns nothing
local integer i
local integer i2
local integer l
//ensure timer is allocated and isn't null
debug if (n[this] != 0 and this != 0) then
//update instance list (actual list of timers)
//exclude 0 to keep loop as fast as possible
set c = c - 1
if (c != 0) then
//remove node from segmented list
set n[p[this]] = n[this]
set p[n[this]] = p[this]
//update first/last
if (n[0] == this) then
set n[0] = n[this]
elseif (p[0] == this) then
set p[0] = p[this]
endif
//remove from absolute list
set an[ap[this]] = an[this]
set ap[an[this]] = ap[this]
//update current expired
if (e == this) then
set e = an[this]
endif
endif
debug set n[this] = 0
//if need to remove a segment, update the last node
if (c-c/THRESH*THRESH == 0) then
//send segment to recycler for recycling
//has to go on a timer since can't remove trigger conditions while the
//trigger is running
call TimerStart(t, TimerGetRemaining(t), false, function Rec)
set rp[rc] = a[c]
set a[c] = null
set rc = rc + 1
if (THRESH > 1) then
//get last segment
set i = q[0]
set f<i> = false //unmark
set li[ni<i>] = 0
//go to the previous node
set i = q<i>
//make 0 point to last node and make
//last node point to 0
set q[0] = i
set w<i> = 0
set lc = lc - 1
endif
endif
//if list isn't empty, update segments
if (c != 0) then
if (f[this]) then
set f[this] = false
if (THRESH == 1) then
//do a simple removal
set w[q[this]] = w[this]
set q[w[this]] = q[this]
set lc = lc - 1
else
set i = an[this]
if (i != 0) then
set f<i> = true
set li[ni[this]] = i
set w[q[this]] = i
set q[w[this]] = i
set w<i> = w[this]
set q<i> = q[this]
endif
endif
endif
if (THRESH > 1) then
//update all list segments
set i2 = ni[this] //current segment id
set l = li[i2] //current list segment
set i = w[l] //next list segment
set l = ni<i> //next line segment id
set this = an<i> //next node in next list segment
loop
//while segments left to update
exitwhen i == 0
set f<i> = false
exitwhen this == 0
if (this != 0) then
//swap current segment head out for
//node
set w[q<i>] = this
set q[w<i>] = this
set w[this] = w<i>
set q[this] = q<i>
set f[this] = true
set li[l] = this
set ni<i> = i2
endif
//next
set i2 = ni<i>
set i = w<i>
set l = ni<i>
set this = an<i>
endloop
endif
endif
debug else
debug if (this == 0) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "T32s ERROR: ATTEMPT TO STOP NULL TIMER")
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "T32s ERROR: ATTEMPT TO STOP TIMER MULTIPLE TIMES")
debug endif
debug endif
endmethod
endmodule
endlibrary
</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>
JASS:
struct Tester extends array
private static constant integer COUNT = 2500
private static constant integer THRESH = 1
private method expire takes nothing returns nothing
endmethod
implement T32s
private static thistype i = COUNT
private static method run takes nothing returns boolean
loop
exitwhen i == 0
call i.start()
set i = i - 1
endloop
return false
endmethod
private static method onInit takes nothing returns nothing
local integer c = COUNT/500
local trigger t = CreateTrigger()
local conditionfunc b = Condition(function thistype.run)
loop
exitwhen c == 0
call TriggerAddCondition(t, b)
set c = c - 1
endloop
if (COUNT-COUNT/500*500 > 0) then
call TriggerAddCondition(t, b)
endif
call TriggerEvaluate(t)
call TriggerClearConditions(t)
call DestroyTrigger(t)
set t = null
call DestroyCondition(b)
set b = null
endmethod
endstruct
stress tests
Instance Count, Thresh: fps
2300, 8191: 59
8000, 8191: 25
8000, 1: 0
2300, 1: 14
2300, 60: 59
8000, 60: 14