vJASS & Zinc Documentation
For the latest documentation about how it works vJASS and Zinc language layers for Warcraft III, please follow these links:
Jasshelper documentation - Zinc documentation - WC3 Optimizer documentation

One map, one Timer

rvonsonsnadtz · 17686

0 Members and 1 Guest are viewing this topic.

One map, one Timer
on: February 01, 2013, 04:47:41 AM

Not sure if this should be here or in Jass Theory. But anyway.

I have tried to see if it is possible to use just one timer for one map (in my case; all: attackfunctions, timed effects, spells, calculation for time before a projectile reaches its destination etc with just one timer) and got inspired by the (not as efficient as they say) KT2. Now, KT2 have been successfully used in a number of things, but it could not handle the attacked-triggered functions well at all. So I tried my own way to make something using the KT2 ideas (tick-periods with a period time of 0.00125 seconds and register functions as triggers in initialisations and so on) However, My idea was using a loop inside the function triggered by the timer to find the first function that should ran, then let that function call a continuer to check for next etc until al functions that should run at the moment have been executed. Check code

Code: jass
  1.  
  2. /*Don't use/check this code, I got a WAY faster one further down in this thread*/
  3. library TimeralaRV
  4. globals
  5.     constant timer SMALLTIMER = CreateTimer()
  6.     constant timer LARGETIMER = CreateTimer() /*This timmer is added due to experiements with one low-freqency timer and one high-freqency timer*/
  7.     constant integer SMALLTIMERARRAY = 80
  8.     constant integer LARGETIMERARRAY = 180
  9.     constant real SMALLTIMEOUT = 0.00125
  10.     constant real LARGETIMEOUT = 0.2
  11.     integer SmallTimerWhichArray = 0
  12.     integer LargeTimerWhichArray = 0
  13.     boolean array SmallTimerArrayUsed[SMALLTIMERARRAY]
  14.     integer array SmallTimerArrayTicksLeft[SMALLTIMERARRAY]
  15.     trigger array SmallTimerArrayTrigger[SMALLTIMERARRAY]
  16.     unit array SmallTimerArrayDealer[SMALLTIMERARRAY]
  17.     unit array SmallTimerArrayReci[SMALLTIMERARRAY]
  18.     boolean array LargeTimerArrayUsed[LARGETIMERARRAY]
  19.     integer array LargeTimerArrayTicksLeft[LARGETIMERARRAY]
  20.     trigger array LargeTimerArrayTrigger[LARGETIMERARRAY]
  21. endglobals
  22.  
  23. function TimeRunSmall takes nothing returns nothing
  24. local integer i = 0
  25. local boolean b = FALSE
  26. if SmallTimerArrayTicksLeft[0] == 1 then
  27.     call TriggerExecute(SmallTimerArrayTrigger[0])
  28.     set SmallTimerArrayTicksLeft[0] = 0
  29.     set SmallTimerArrayUsed[0] = FALSE
  30. else
  31.     if SmallTimerArrayTicksLeft[0] != 0 then
  32.         set SmallTimerArrayTicksLeft[0] = SmallTimerArrayTicksLeft[0] - 1
  33.     endif
  34.     loop
  35.         exitwhen b == TRUE
  36.         set i = i + 1
  37.         if SmallTimerArrayTicksLeft[i] == 1 then
  38.             set SmallTimerWhichArray = i
  39.             call TriggerExecute(SmallTimerArrayTrigger[i])
  40.             set SmallTimerArrayTicksLeft[i] = 0
  41.             set SmallTimerArrayUsed[i] = FALSE
  42.             set b = TRUE
  43.         else
  44.             if SmallTimerArrayTicksLeft[0] != 0 then
  45.                 set SmallTimerArrayTicksLeft[i] = SmallTimerArrayTicksLeft[i] - 1
  46.             endif
  47.             if i > SMALLTIMERARRAY then
  48.                 set b = TRUE
  49.             endif
  50.         endif
  51.     endloop
  52. endif
  53. endfunction
  54.  
  55.  
  56. function NextFunction takes nothing returns nothing /* A bit simplified, in reality I would need a simple way of determine if it should use the small or the large timer, but it is not relevant for the moment*/
  57. local integer i = SmallTimerWhichArray + 1
  58. local boolean b
  59. if SmallTimerArrayTicksLeft[i] == 1 then
  60.     call TriggerExecute(SmallTimerArrayTrigger[0])
  61.     set SmallTimerArrayTicksLeft[i] = 0
  62.     set SmallTimerArrayUsed[i] = FALSE
  63. else
  64.     if SmallTimerArrayTicksLeft[i] != 0 then
  65.         set SmallTimerArrayTicksLeft[i] = SmallTimerArrayTicksLeft[i] - 1
  66.     endif
  67.     loop
  68.         exitwhen b == TRUE
  69.         set i = i + 1
  70.         if SmallTimerArrayTicksLeft[i] == 1 then
  71.             set SmallTimerWhichArray = i
  72.             call TriggerExecute(SmallTimerArrayTrigger[i])
  73.             set SmallTimerArrayTicksLeft[i] = 0
  74.             set SmallTimerArrayUsed[i] = FALSE
  75.             set b = TRUE
  76.         else
  77.             if SmallTimerArrayTicksLeft[0] != 0 then
  78.                 set SmallTimerArrayTicksLeft[i] = SmallTimerArrayTicksLeft[i] - 1
  79.             endif
  80.             if i > SMALLTIMERARRAY then
  81.                 set b = TRUE
  82.             endif
  83.         endif
  84.     endloop
  85. endif
  86. endfunction
  87.  
  88. endlibrary
  89.  
However, when the NextFunction code is executed, the game shuts of. I don't know if it just is a protection against looping or if it actually creates an infinitive-loop here somewhere..

If someone have another way of using a singletimer for everything, or could tell me that their efficiency ain't much to talk about, I would like to hear ^^ (:

This topic isn't really a superimportant one for me, it's more a theory-question in that aspect.
« Last Edit: February 16, 2013, 08:47:09 AM by rvonsonsnadtz »



One map, one Timer
Reply #1 on: February 02, 2013, 06:22:49 AM

This approach is very good in fact, because you reduce the handle count considerably. But this can bring issues in relation to the chances to have running at the same time several elements.

For example in my project PoC,  I did this approach and I had in some cases lags due too many instances happening at the same time (AI vs AI attacking each other with full army) this implied to use several timers (3 - 6) timers running at the same time distributing the load of the custom spells.

I separated them in this way:

1 timer for stuff with low frequency (period of 0.5s)
1 timer for hero abilities (high frequency)
1 timer for unit abilities (high frequency)

The load of the game was reduced.


One map, one Timer
Reply #2 on: February 02, 2013, 11:20:12 AM

Hmm, I dunno man, but don't you think it will just cause the game to be slow?

Chronicles of Darkness
by: SonofJay

A BlizzMod Hosted Project

They can hate, let them hate, make them hate.


One map, one Timer
Reply #3 on: February 02, 2013, 11:31:46 AM

No, I run in my map 6 timers during all the game. Waht it makes slow the game is the amount of code running, like looping a big aount of data.

I remember when I used timerutils and from time to time the game got slowed, that's because it generates a lot of timers running at the same time. Timerutils works fine in AOS or games where custom spells and system not exceed more than 100 events or codes running at the same time.


One map, one Timer
Reply #4 on: February 03, 2013, 12:32:53 PM

I would really like to see the code of that function, cause, as said in another thread by me, I am using a jass-scripted attackfunction, which means that it have to handle the max-number of units on the map hitting a target at the same time (10players with a max of 12units inc. summons + 2computerplayers in dota or ts style, to be sure that I never reach the max-amount, I estimate about 200 timers would be needed JUST FOR THE ATTACKS in my current approach. With that, I estimated a max of about 150-200 timers for the spells/timed effects/some other misc stuff. Now, it have to be a very massive thing when all timers would be active at the same time, but still, 300-400 timers running can't be good on performance...

Now, as I said, by some reason, the wc3 wants to shut down if I register more than one attackfunction to the timer, probably cause it recognises some kind of loop, cause it SHOULD not run the exact same function with the exact same data in all infinity, but, well, it sure looks so..
You PoC aren't protected right? Would it be ok if I downloaded it and took some inpiration from your time-functions? (:

Off-topic question; It seems to me that you, Moyack, uses struct quite a lot, now, what I wonder is, are they in some way faster than array-variables, or do u just use them to simplifie code for you?



One map, one Timer
Reply #5 on: February 06, 2013, 06:58:34 AM

I would really like to see the code of that function, cause, as said in another thread by me, I am using a jass-scripted attackfunction, which means that it have to handle the max-number of units on the map hitting a target at the same time (10players with a max of 12units inc. summons + 2computerplayers in dota or ts style, to be sure that I never reach the max-amount, I estimate about 200 timers would be needed JUST FOR THE ATTACKS in my current approach. With that, I estimated a max of about 150-200 timers for the spells/timed effects/some other misc stuff. Now, it have to be a very massive thing when all timers would be active at the same time, but still, 300-400 timers running can't be good on performance...
Definitely. This is the code I use in my project PoC to distribute the game load:

Code: jass
  1. library Indexor2 initializer init requires MicroTable, Alloc
  2. // the Alloc requirement is just to keep properly ordered the libraries, actually this library
  3. // does not need Alloc, but the dependant libraries does.
  4. globals
  5.     private constant key A
  6.     private constant integer Max = 5
  7. endglobals
  8.  
  9. struct Indexor2 extends array
  10.     timer t
  11.     trigger tr
  12.     real period
  13.     integer count
  14.     thistype next
  15.     static integer counter = 1
  16.     private static method evaluate takes nothing returns nothing
  17.         call TriggerEvaluate(thistype(GetData(GetExpiredTimer(), A)).tr)
  18.     endmethod
  19.    
  20.     static method create takes real period returns thistype
  21.         local thistype this = thistype(thistype.counter)
  22.         set thistype.counter = thistype.counter + 1
  23.         set this.t = CreateTimer()
  24.         set this.tr = CreateTrigger()
  25.         set this.period = period
  26.         set this.count = 0
  27.         call StoreData(this.t, A, integer(this))
  28.         call TimerStart(this.t, period, true, function thistype.evaluate)
  29.         return this
  30.     endmethod
  31.    
  32.     method AddCode takes code c returns nothing // this code should return a boolean to keep consistency
  33.         if .count > Max then
  34.             if .next == 0 then
  35.                 set .next = thistype.create(.period)
  36.             endif
  37.             call .next.AddCode(c)
  38.         else
  39.             set .count = .count + 1
  40.             call TriggerAddCondition(.tr, Condition(c))
  41.             debug call BJDebugMsg( "Indexor2 index: " + I2S(this) + " events added: " + I2S(.count))
  42.         endif
  43.     endmethod
  44. endstruct
  45.  
  46. globals // here we'll set the indexor we'll use in the game.
  47.     Indexor2 UnitIndexer // Manages units effects
  48.     Indexor2 HeroIndexer // Manages Heroes effects
  49.     Indexor2 TEIndexer   // Manages Timed Effects
  50. endglobals
  51.  
  52. private function init takes nothing returns nothing
  53.     set UnitIndexer = Indexor2.create(GetRandomReal(0.060, 0.069))
  54.     set HeroIndexer = Indexor2.create(GetRandomReal(0.060, 0.069))
  55.     set TEIndexer   = Indexor2.create(0.1)
  56. endfunction
  57.  
  58. endlibrary

Quote
Now, as I said, by some reason, the wc3 wants to shut down if I register more than one attackfunction to the timer, probably cause it recognises some kind of loop, cause it SHOULD not run the exact same function with the exact same data in all infinity, but, well, it sure looks so..
You PoC aren't protected right? Would it be ok if I downloaded it and took some inpiration from your time-functions? (:
it seems like an infinite loop you got there :P

Quote
Off-topic question; It seems to me that you, Moyack, uses struct quite a lot, now, what I wonder is, are they in some way faster than array-variables, or do u just use them to simplifie code for you?
Structs are arrays with nicer representation. Doing a struct with 3 components is compiled as an array set.


One map, one Timer
Reply #6 on: February 15, 2013, 12:01:45 PM

I have created a efficient way of getting either a integer or a struct (atleast, it should work with a struct since they are just integers)

Code: jass
  1. library TickTack
  2.  
  3. /*Timefunction "TickTack" by RVonSonSnadtz
  4.  
  5. This is a simple, yet very effective timerfunction. However, it can't by itself handle things longer than 2seconds, so that is something you should learn to make in the function next. Give a PM at [url=http://www.wc3jass.com]www.wc3jass.com[/url] if you need help with that.
  6.  
  7. Pros: Very fast, safe and stable, it doesn't change much even if you would throw it ALOT of stuff to do.
  8.  
  9. Cons: All functions ran with this function needs to be pre-registered as a condition to a trigger. Best is to use a constant trigger for all functions that you know might run in this script. Also, it can't by itself handle stuff longer than 2secs, but on the other hand is that a simple issue to come around (:
  10.  
  11. This is version 1.0, hopefully I found this effective enough (: */
  12.  
  13. globals
  14.     constant timer HIGHQ_TIMER = CreateTimer() //Needs to be started at MapInit using the HIGHQ_TIMEOUT, TRUE to periodic and the function HIGHQ_CALL
  15.     constant real HIGHQ_TIMEOUT = 0.00125 //highq stands for high-freqency, with other words, the short intervall functions
  16.     constant integer HIGHQ_MAXTICKS = 1601 //1601 works for everything <= 2secs, 801 for <=1sec. A higher value lessens the CPU-usage as the long-time functions doesn't need to change their calculations-integer as often, but it increases the RAM-usage because it will store more variables in the hashtable. Nulling the values would be quite ineffective.
  17.     constant real HIGHQ_MAXROOF =  HIGHQ_TIMEOUT * I2R(HIGHQ_MAXTICKS) //Should NEVER be changed unless you know what you are doing. Only using for debugging purpose.
  18.     constant hashtable HIGHQ_TABLE = InitHashtable() //The very heart of this system
  19.     private integer array highq_nroffunctions[HIGHQ_MAXTICKS]
  20.     private integer highq_whichchild = 0
  21.     private integer highq_tick = 0
  22.    
  23.  
  24. endglobals
  25.  
  26. function HighQ_Call takes nothing returns nothing //Needs to be used with the timer on startup
  27. if highq_nroffunctions[highq_tick] > 0 then
  28.     call TriggerEvaluate(LoadTriggerHandle(HIGHQ_TABLE, highq_tick, 0))
  29.     set highq_whichchild = 0
  30. endif
  31. if highq_tick >= HIGHQ_MAXTICKS then
  32.     set highq_tick = 0
  33. else
  34.     set highq_tick = highq_tick + 1
  35. endif
  36. endfunction
  37.  
  38. public function GetData takes nothing returns integer
  39. local integer i = highq_whichchild
  40. local integer data
  41. if i < highq_nroffunctions[highq_tick] then
  42.     set highq_whichchild = i + 1
  43.     call TriggerEvaluate(LoadTriggerHandle(HIGHQ_TABLE, highq_tick, highq_whichchild))
  44.     set highq_nroffunctions[highq_tick] = highq_nroffunctions[highq_tick] - 1
  45. else
  46.     set highq_nroffunctions[highq_tick] = 0
  47. endif
  48. return LoadInteger(HIGHQ_TABLE, highq_tick, i)
  49. endfunction
  50.  
  51.  
  52. public function Register takes real time, trigger func, integer data returns nothing
  53. local integer ticks
  54. local integer i
  55. if time < HIGHQ_MAXROOF then //This If/then/else - function is solely used for debugging. If you are CERTAIN that ALL functions using this is below or at the 2sec-limit, then you could remove this if and other stuff marked as For Debugging
  56.     set ticks = R2I(time / HIGHQ_TIMEOUT) + highq_tick
  57.     if ticks > 1600 then
  58.         set ticks = ticks - HIGHQ_MAXTICKS
  59.     endif
  60.     set i = highq_nroffunctions[ticks]
  61.     call SaveTriggerHandle(HIGHQ_TABLE, ticks, i, func)
  62.     call SaveInteger(HIGHQ_TABLE, ticks, i, data)
  63.     set highq_nroffunctions[ticks] = i + 1
  64. else //For debugging
  65.     debug call BJDebugMsg("A function passed the 2sec limit, please check through the code") //For debugging
  66. endif //For debugging
  67. endfunction
  68. endlibrary
  69.  
The good thing here is that it seems fast and skips all loop-stuff, for highest possible speed. Second thing, that is both good and bad, is that it can't call ordinary functions, it needs to use already-made triggers with the function attached as a condition. And one last con: when calling GetData, you need to use the trigger that the executed function is attached to.
It seems fast to me, but there are probably more here who are better on this area of expertise


I realised that it is possible to just use one timer for everything, with the use of integers to recall the function after a second or 2 until it is time to expire, so this is the timer I think I will be going for (:

Any feedback on this would be nice (:

PS: I belive one of the biggest losses on performance coming from the KT2 are all extra trigger code.. So for me I have to create the functions that should be called as already made triggers so that I don't need to declare them more than once at the begining of the map.

(example:

Code: jass
  1. globals
  2.         constant trigger EFFECTEXPIRE = CreateTrigger()
  3. endglobals
  4.  
  5. function EffectExpire takes nothing returns nothing
  6. local SomeStruct data = TickTack_GetData() /* or just use the integer from it directly*/
  7. /*do the stuff*/
  8. endfunction
  9.  
  10. //===================================
  11. //At the map-init, I will link the function with the trigger:
  12.  
  13. function InitTrig_Melee_Initialization takes nothing returns nothing
  14. /* the ordinary mapinit-stuff and then:*/
  15.     call TriggerAddCondition( EFFECTEXPIRE,Condition( function EffectExpire))
  16. endfunction
  17.  

Sure, this will lead to a small list, but in terms of RAM, this should actually be lesser than the peaks if you do this every time you needs to use a function (as one function then could be attached to several, temporary triggers that also are stored in variables = more process required)

This might be a bit overanalytic for many, but I want this map to work for alot of users, and then it needs it's speed ^^'

PS: The first code is not very fast, BUT still faster than KT2 when alot of things is going on, but on the other hand.. TimerUtils seems faster than KT2 when alot of things is going on..
« Last Edit: February 16, 2013, 11:08:59 AM by rvonsonsnadtz »



One map, one Timer
Reply #7 on: February 17, 2013, 09:16:44 AM

I have created a efficient way of getting either a integer or a struct (atleast, it should work with a struct since they are just integers)

Code: jass
  1. library TickTack
  2.  
  3. /*Timefunction "TickTack" by RVonSonSnadtz
  4.  
  5. This is a simple, yet very effective timerfunction. However, it can't by itself handle things longer than 2seconds, so that is something you should learn to make in the function next. Give a PM at [url=http://www.wc3jass.com]www.wc3jass.com[/url] if you need help with that.
  6.  
  7. Pros: Very fast, safe and stable, it doesn't change much even if you would throw it ALOT of stuff to do.
  8.  
  9. Cons: All functions ran with this function needs to be pre-registered as a condition to a trigger. Best is to use a constant trigger for all functions that you know might run in this script. Also, it can't by itself handle stuff longer than 2secs, but on the other hand is that a simple issue to come around (:[/quote]So you need to set all the conditions at map init?? well I think it's not an issue.
  10.  
  11. [quote]This is version 1.0, hopefully I found this effective enough (: */
  12.  
  13. globals
  14.     constant timer HIGHQ_TIMER = CreateTimer() //Needs to be started at MapInit using the HIGHQ_TIMEOUT, TRUE to periodic and the function HIGHQ_CALL
  15.     constant real HIGHQ_TIMEOUT = 0.00125 //highq stands for high-freqency, with other words, the short intervall functions
  16.     constant integer HIGHQ_MAXTICKS = 1601 //1601 works for everything <= 2secs, 801 for <=1sec. A higher value lessens the CPU-usage as the long-time functions doesn't need to change their calculations-integer as often, but it increases the RAM-usage because it will store more variables in the hashtable. Nulling the values would be quite ineffective.
  17.     constant real HIGHQ_MAXROOF =  HIGHQ_TIMEOUT * I2R(HIGHQ_MAXTICKS) //Should NEVER be changed unless you know what you are doing. Only using for debugging purpose.
  18.     constant hashtable HIGHQ_TABLE = InitHashtable() //The very heart of this system
  19.     private integer array highq_nroffunctions[HIGHQ_MAXTICKS]
  20.     private integer highq_whichchild = 0
  21.     private integer highq_tick = 0
  22.    
  23.  
  24. endglobals
  25.  
  26. function HighQ_Call takes nothing returns nothing //Needs to be used with the timer on startup
  27. if highq_nroffunctions[highq_tick] > 0 then
  28.     call TriggerEvaluate(LoadTriggerHandle(HIGHQ_TABLE, highq_tick, 0))
  29.     set highq_whichchild = 0
  30. endif
  31. if highq_tick >= HIGHQ_MAXTICKS then
  32.     set highq_tick = 0
  33. else
  34.     set highq_tick = highq_tick + 1
  35. endif
  36. endfunction
  37.  
  38. public function GetData takes nothing returns integer
  39. local integer i = highq_whichchild
  40. local integer data
  41. if i < highq_nroffunctions[highq_tick] then
  42.     set highq_whichchild = i + 1
  43.     call TriggerEvaluate(LoadTriggerHandle(HIGHQ_TABLE, highq_tick, highq_whichchild))
  44.     set highq_nroffunctions[highq_tick] = highq_nroffunctions[highq_tick] - 1
  45. else
  46.     set highq_nroffunctions[highq_tick] = 0
  47. endif
  48. return LoadInteger(HIGHQ_TABLE, highq_tick, i)
  49. endfunction
  50.  
  51.  
  52. public function Register takes real time, trigger func, integer data returns nothing
  53. local integer ticks
  54. local integer i
  55. if time < HIGHQ_MAXROOF then //This If/then/else - function is solely used for debugging. If you are CERTAIN that ALL functions using this is below or at the 2sec-limit, then you could remove this if and other stuff marked as For Debugging
  56.     set ticks = R2I(time / HIGHQ_TIMEOUT) + highq_tick
  57.     if ticks > 1600 then
  58.         set ticks = ticks - HIGHQ_MAXTICKS
  59.     endif
  60.     set i = highq_nroffunctions[ticks]
  61.     call SaveTriggerHandle(HIGHQ_TABLE, ticks, i, func)
  62.     call SaveInteger(HIGHQ_TABLE, ticks, i, data)
  63.     set highq_nroffunctions[ticks] = i + 1
  64. else //For debugging
  65.     debug call BJDebugMsg("A function passed the 2sec limit, please check through the code") //For debugging
  66. endif //For debugging
  67. endfunction
  68. endlibrary
  69.  
The good thing here is that it seems fast and skips all loop-stuff, for highest possible speed. Second thing, that is both good and bad, is that it can't call ordinary functions, it needs to use already-made triggers with the function attached as a condition. And one last con: when calling GetData, you need to use the trigger that the executed function is attached to.
It seems fast to me, but there are probably more here who are better on this area of expertise
I've seen that you use a lot of public. I suggest to use public to genenate function names with suffixes. If you want a function public, just avoid any private or public keywords, and for private functions, just use private. Additionally the usage of hashtables is nice but abusing of them can get slow with huge amount of data. I haven't been to test it precisely, but I think we could do some tests about it. BTW, could you avoid the usage of tables using a trigger array?? I can ensure you arrays are way faster!!!


Quote
I realized that it is possible to just use one timer for everything, with the use of integers to recall the function after a second or 2 until it is time to expire, so this is the timer I think I will be going for (:

Any feedback on this would be nice (:
Definitely anything can be managed by one timer, the only issues is that this apporach is not effective in any kind of maps. For example in altered melee or footmen frenzy maps the huge amount of units and custom abilities can be a serious load and one timer moving hundrends of events at the same time can kill the process (that was my case in PoC). In AoS, RPG or Arenas, the usage of timer utils can be more than enough. So yes, saying this way is faster is true in some circumstances, and that's the reason why the best option is to do the code for your map based on public resources.

Quote
PS: I belive one of the biggest losses on performance coming from the KT2 are all extra trigger code.. So for me I have to create the functions that should be called as already made triggers so that I don't need to declare them more than once at the begining of the map.

(example:

Code: jass
  1. globals
  2.         constant trigger EFFECTEXPIRE = CreateTrigger()
  3. endglobals
  4.  
  5. function EffectExpire takes nothing returns nothing
  6. local SomeStruct data = TickTack_GetData() /* or just use the integer from it directly*/
  7. /*do the stuff*/
  8. endfunction
  9.  
  10. //===================================
  11. //At the map-init, I will link the function with the trigger:
  12.  
  13. function InitTrig_Melee_Initialization takes nothing returns nothing
  14. /* the ordinary mapinit-stuff and then:*/
  15.     call TriggerAddCondition( EFFECTEXPIRE,Condition( function EffectExpire))
  16. endfunction
  17.  

Sure, this will lead to a small list, but in terms of RAM, this should actually be lesser than the peaks if you do this every time you needs to use a function (as one function then could be attached to several, temporary triggers that also are stored in variables = more process required)

This might be a bit overanalytic for many, but I want this map to work for alot of users, and then it needs it's speed ^^'

PS: The first code is not very fast, BUT still faster than KT2 when alot of things is going on, but on the other hand.. TimerUtils seems faster than KT2 when alot of things is going on..
As I said before, that depends on the scenario you put the code. the more triggers you need to run at once in your maps, the better is to use one or few of them managing the whole system.
« Last Edit: February 17, 2013, 09:28:24 AM by moyack »



One map, one Timer
Reply #8 on: February 18, 2013, 11:34:48 AM

Ok, hmmm, I got some ideas about how to make it with just arrays, but I need to further investigate and figure about it..
Perhaps I could store the integer-data with just 2 integerarrays, instead of the hashtable. Or I could store all integer-data in a string-array, but since the cache-way of strings seems to be a bit "special" I think I should try to succed with the other version.

Edit: Perhaps the smartest way is again to use 2 timers, one high-freqent and one a bit lower (still quite high like 0.0125), and let them go for 400ticks (the high-freqency would go for a half-sec and keep 20funcs per tick, but it still feels for me like my system must be able to handle more, even if it wouldn't run that many functions in the average at every tick, 200units COULD (with a proxamity below 1%) hit their target at the same time.
« Last Edit: February 18, 2013, 12:30:10 PM by rvonsonsnadtz »



One map, one Timer
Reply #9 on: February 18, 2013, 03:52:04 PM

Manage structs that extends array and it will work very nice for you. struct data extends array + Alloc is a very nice way to do the things and very efficient.

I've added to the resources Microtable so attaching structs is a piece of cake.



One map, one Timer
Reply #10 on: February 19, 2013, 09:02:46 AM

Hmmm, still many things that are obscured to me, like the "extends array"-thing.
And textmacro? What is that?
Btw, I got one other way to try to, so I will be back with one more try (:

EDIT: I was looking for a native function called "split", which I now saw was a custom-made function, and the struct-allocation seems faster than that...
« Last Edit: February 19, 2013, 10:54:18 AM by rvonsonsnadtz »



One map, one Timer
Reply #11 on: February 19, 2013, 09:25:38 AM

Hmmm, still many things that are obscured to me, like the "extends array"-thing.
And textmacro? What is that?
Btw, I got one other way to try to, so I will be back with one more try (:
Entends array is a nice thing. The structs are compiled with a huge amount of data, which ha proven to be inefficient and slow. LEt me find a sample so you can understand.


One map, one Timer
Reply #12 on: February 19, 2013, 11:08:18 AM

Ok, thanks! (:



One map, one Timer
Reply #13 on: February 23, 2013, 05:07:03 PM

I did some research about the extend array- thing, and found a nice tutorial (by Nestharus at HiveWorksShop), which not only helped me understand what struct-extend array is, but also how to use it and most importantly; why it is so much better. I used the background-code generated by his way of approaching structs to store triggers and the data-integer in array-codes rather than the hashtable (however, the hashtable is still not out of the system, it is a easy way to store- and load the right index)

If you are right about the hashtable-thing, then this SHOULD be alot faster than the previous system (:  (if you would insist that I should remove the hashtable completely, then I will have some more work to do, but in the mean-time, I hope this works! (:  )


EDIT: No, way, I'm gonna remove the table completely, The code will be temporary gone, back soon with update ^^'
EDIT2: Here we go; no hashtables:
Code: jass
  1. library ChainTimer //By RVonSonSnadtz
  2.  
  3.  
  4. globals
  5.     constant timer HIGHQ_TIMER = CreateTimer() //Needs to be started at MapInit using the HIGHQ_TIMEOUT, TRUE to periodic and the function HIGHQ_CALL
  6.     constant real HIGHQ_TIMEOUT = 0.00125 //highq stands for high-freqency, with other words, the short intervall functions
  7.     constant integer HIGHQ_MAXTICKS = 1600 //The HighQ works for all functions below 2 sec
  8.     private integer array highq_nroffunctions[HIGHQ_MAXTICKS]
  9.     integer highq_whichchild = 0
  10.     integer highq_tick = 0
  11.     private trigger array highq_func
  12.     private integer array highq_data
  13.     private integer array highq_nextcall
  14.     private integer array highq_whichcall
  15.     integer TickTack_instanceCount= 0
  16.     integer TickTack_recycle= 0
  17.     integer array TickTack_recycleNext
  18.     integer array TickTack_WhichtoCall
  19.  
  20. endglobals
  21.  
  22.  
  23.  
  24. function HighQ_Call takes nothing returns nothing
  25. if highq_nroffunctions[highq_tick] > 0 then
  26.     call TriggerEvaluate(highq_func[highq_whichcall[highq_tick]])
  27. endif
  28. if highq_tick >= HIGHQ_MAXTICKS then
  29.     set highq_tick = 0
  30. else
  31.     set highq_tick = highq_tick + 1
  32. endif
  33. endfunction
  34.  
  35. function GetData takes nothing returns integer
  36. local integer i = highq_data[highq_whichcall[highq_tick]]
  37. local integer i2 = highq_nroffunctions[highq_tick] - 1
  38.     set highq_whichcall[highq_tick] = highq_nextcall[highq_whichcall[highq_tick]]
  39.     set TickTack_recycleNext[i] = TickTack_recycle
  40.     set TickTack_recycle = i
  41.     if i2 > -1 then
  42.         set highq_nroffunctions[highq_tick] = i2
  43.         call TriggerEvaluate(highq_func[highq_whichcall[highq_tick]])
  44.     endif
  45. return i
  46. endfunction
  47.  
  48.  
  49. function Register takes real time, trigger func, integer data returns nothing
  50. local integer ticks = R2I(time / HIGHQ_TIMEOUT) + highq_tick
  51. local integer index
  52.     if ticks > HIGHQ_MAXTICKS then
  53.         set ticks = ticks - HIGHQ_MAXTICKS
  54.     endif
  55. set highq_nroffunctions[ticks] = highq_nroffunctions[ticks] + 1
  56.     if ( TickTack_recycle == 0 ) then
  57.         set TickTack_instanceCount=TickTack_instanceCount + 1
  58.         set index=TickTack_instanceCount
  59.     else
  60.         set index=TickTack_recycle
  61.         set TickTack_recycle=TickTack_recycleNext[TickTack_recycle]
  62.     endif
  63.     set highq_nextcall[highq_whichcall[ticks]] = highq_whichcall[ticks]
  64.     set highq_whichcall[ticks] = index
  65.     set highq_func[index] = func
  66.     set highq_data[index] = data
  67. endfunction
  68.  
  69. endlibrary
  70.  
(Added a testmap incase you want to check it out) I wonder if this really is efficient.. I mean, the doubble-array-codes might be a bit.. overkill? Well, I would like to know what you think Moyack ^^' )
« Last Edit: February 26, 2013, 06:05:45 AM by rvonsonsnadtz »



One map, one Timer
Reply #14 on: February 26, 2013, 06:06:09 AM

Updated the code ^^'



 

Chaos Realm - The world of Game modders and wc3 addicts     WC3JASS.com - The JASS Vault   Jetcraft - A Starcraft II mod