• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[System] Timer Tools

Level 31
Joined
Jul 10, 2007
Messages
6,306
Makes sense when you put it in this way, thanks. Your stuff is too complex to understand!

Yeah Filter(), if you didn't know, is a super duper slow function call (like adding the weight of 100 function calls if I recall correctly). So it makes sense to eliminate it if you know you'll call it more than once.

I'll add some comments later. This thing's operation is pretty cool : ).
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Improved merging algorithm further by handling two types of TimerLists, constant merging and smart merging. This ensures that small timeouts will have very fast creation and destruction. This gets rid of the need for specialized libraries like T32x as the speed gain for proper creation and destruction will be negligible.

I removed CLTQ because it was kinda crappy... it was around the same speed as normal timers... I coded a specialized thing that was similar to TimerUtils but much faster, but the problem with preloading timers is that more timers = less performance for all timers... so I scrapped the idea.

The rankings were
CLTQ = Standard
TimerUtils (far, far behind...)

In my specialized thing
Tt 1 Shot Timers
Standard (far behind)
TimerUtils (far, far behind...)

The 1 shot timers I made could handle around 4500 timers with .02 timeouts at 64 fps.TimerUtils ofc froze up at around 2200. Standard froze up at around 2800 or so.


What does this tell you? Using standard timers is smart and better than using TimerUtils >.>, lolz.
 
I felt the need to make a double post because of some very mind-blowing results I got while benchmarking this against TimerUtils.

I used this spell:
http://www.hiveworkshop.com/forums/spells-569/epicenter-v2-1-0-0-a-204120/

During the tests, I removed the special effect to maximize FPS.

Code:
10 casts:
    TimerUtils: 2 FPS Drop
    TimerTools: 1 FPS Drop
20 casts:
    TimerUtils: 9-11 FPS Drop
    TimerTools: 2-4 FPS Drop
60 casts:
    TimerUtils: 24-28 FPS Drop
    TimerTools: 6-8 FPS Drop
150 casts:
    TimerUtils: 50-52 FPS Drop
    TimerTools: 12-15 FPS Drop

Ultimate stress test:

With 1000 casts and special effects enabled, TimerUtils died.
TimerTools on the other hand was able to run at 5 FPS.

Yes, with 2500 special effects per second, TimerTools was incredibly reliable.

Good day to you haters.
 
Level 6
Joined
Oct 23, 2011
Messages
182
I would like to try using this system... (I'm using CTL and TimerUtils atm)
but its API seems too confusing for me =( too many functions
Is this system good for non-repeating timers as well?

what would i have to replace NewTimer()/ReleaseTimer() with?
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
This system is only good for repeating timers. Non repeating timers should actually run on standard timers.


Unless you need more fine tuned usage, like something for a projectile system, I suggest sticking with the modules.

If you enable debug mode, you will get messages telling you which module you should be using for what you are doing ; ). Using CTM is the safest bet. If you can get away with one of the other 2, you will get a message in-game telling you to change.

I have wanted to do something for 1 shot timers, but I haven't come up with anything good yet... well, I did come up with 1 thing good, but it is too unbalanced >.<... it could easily be bad. It ran 4500 timers just fine where standard could only handle 2800 and TimerUtils could only handle 2200, so it was extremely good... the only problem is that it requires preloading timers, and if you have a lot of timers created, the performance of all timers will go down. It was similar to TimerUtils but with fantastic performance.

I also had another version that could run 4500 timers at 64 fps, but that one was by far the most unstable ;O. Retrieving the expired timer was a scalar read (just reading some global variable). There was no destruction involved, it ran through a loop. Creation was just subtracting 1. That thing was a beast when it came to performance ;D.


For now you are stuck with Bribe's Table and standard timers for 1 shot timers ; P.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
@Magtheridon96 :

A benchmark "optimized" (i mean the jass script) testmap would be good.

Even if it's the fastest solution (i'm not saying it isn't, i just need a proof), the API is still a valid point when you choice to use a resource.
Also, if you use the spell as expected (with special effect), i suppose that the margin will become negligible.
Now, maybe if you force yourself to use the API (or even like it), i suppose there are some cases where it would be usefull.

I still don't think that in any real map you would see any difference though.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
JASS:
local TimerList list = Timer[15].list
local integer timerMethod = CreateTimerMethod(Condition(function MyFunc))
call TimerMethodAddInstance(list, timerMethod)

??

JASS:
local integer node = GetTimerFirstInstance(timerMethod)
loop
    exitwhen 0 == node
    set node = GetTimerNext(node)
endloop

If you updated your own first arrays using the functions, that'd be even faster as GetTimerFirstInstance is a hashtable read.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
With empty timers callback maybe, but will real codes in a real map i highly doubt you will ever see the difference.
You would lag way before you reach an "huge" amount of timers just because of the timer callbacks codes by themselves.
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
@Nes
But how can I remove the instance using TimerMethodRemoveInstance(instance)...

I did it like this btw, and it works but I get a double free attempt when the Instance 'a' is created again...
well it has a double free coz there's only 1 instance created...
JASS:
static method looper takes nothing returns boolean
   local thistype this = GetTimerFirstInstance(GetExpired()) //only 1 instance?, or this is a wrong approach?...   
   call BJDebugMsg("testttt "+R2S(.dur))
   if .dur > 0 then
      set .dur = .dur - 0.1
   else
      call TimerMethodRemoveInstance(this) //???wrong???
   endif
   return false
endmethod 
    
static method run takes nothing returns nothing 
    local thistype a = TimerMethodAddInstance(0.1,CreateTimerMethod(Filter(function thistype.looper)))
    set a.dur = GetRandomReal(5,7)
endmethod
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
I did it like this btw, and it works but I get a double free attempt when the Instance 'a' is created again...
well it has a double free coz there's only 1 instance created...

Because you are using both TimerMethodAddInstance and GetTimerFirstInstance incorrectly. Look at my post in response to your question to see your correct usage. Ignoring that layout and going off on your own will obviously give you errors. What you have shouldn't even be able to be saved as you are passing in a real when it takes an int.

It would only be working if you changed TimerMethodAddInstance to take a real.


Now look at this (ripped from my post right above yours)
JASS:
local integer node = GetTimerFirstInstance(timerMethod)
loop
    exitwhen 0 == node
    set node = GetTimerNext(node)
endloop

And look at what you did
JASS:
static method looper takes nothing returns boolean
   local thistype this = GetTimerFirstInstance(GetExpired()) //only 1 instance?, or this is a wrong approach?...
   call BJDebugMsg("testttt "+R2S(.dur))
   if .dur > 0 then
      set .dur = .dur - 0.1
   else
      call TimerMethodRemoveInstance(this) //???wrong???
   endif
   return false
endmethod

And then tell me what you did wrong. Read the docs on GetExpired.
JASS:
*       function GetExpired takes nothing returns integer
*           -   Gets the expiring timer. This is not the expiring timer method instance! This is read
*           -   inside of GetTimerFirstInstance, which is why timer instances can only be retrieved inside of
*           -   an expiring timer method.
*           -
*           -   Boolean expressions on the same timer will expire for that same timer. Use timer methods instead.

If you want to use GetExpired, you use it like this (ripped from first post)

JASS:
//add instance, then following code
set first[GetTimerParent()] = GetTimerFirst()
set futureFirst[GetTimerParent()] = GetTimerFutureFirst()

JASS:
local thistype this = first[GetExpired()] //first is a custom array
loop
    exitwhen 0 == this
    set this = GetTimerNext(this)
endloop
set first[GetExpired()] = futureFirst[GetExpired()] //futureFirst is a custom array
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
An issue with this system is that you use R2I(0.03125 * 100), which turns it into 0.03, but there have been reasons in the past for using 1/32, such as 0.03125 being more reasonable with math (since it is not an ugly divisible-by-3 number).

Also, passing 0.03125 into this system might cause the user to still be using the value 0.03125 while the system itself is using 0.03, which will cause some inaccuracies.

I think that CTL should stick around.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Well... what I can do is multiply the timeout by 100000 and load it from a hashtable (not a table to keep the speed up). This will lower the execution speed a bit as it'll introduce a hashtable read though..

edit
You could always use .04 or .02 instead. I don't really want to introduce a hashtable read into the expiration function.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Do you have debug mode on? Debug mode will disable the system if CTM can be a constant merging timeout and you have suggest module changes enabled. However, it shouldn't disable this until after 30 expirations and it should display a message.

edit
I just ran .265 and I see what you mean o-o. It did error out ; O. That is so strange..
 
Last edited:
Level 6
Joined
Oct 23, 2011
Messages
182
just wondering if thers any risk involved in using this system ><

* I'm having some really strange issues.. it keeps changing me to change Ctm modules into CTTC, but it still repeats the message after i changed the modules. There are also messages saying "Null timer expired on 9"; i have no clue what this means and whenver it happens it just stops every spell ingame o_O.
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
Ok, the debug stuff catches the system for errors. In the case of an error, the entire system shuts down and the thread is terminated.

Now, to make it stop telling you to change CTM to the other one, turn suggestions off. It's configurable in the lib.

JASS:
************************************************************************************
*
*    SETTINGS
*/
    /*
    *   Checks if any CTM modules appear to be constant
    */
    static if DEBUG_MODE then
        private struct DEBI extends array
            static constant boolean SUGGEST_MODULE_CHANGES = true
        endstruct
    endif

As for the null timer expired on 9, you need to give me the exact error message in order for me to help you on that ; ). The description you gave is too vague.


This is a complicated system, and while I am rather confident that I captured all of the bugs, more bugs may still be there. There are also some improvements I can make for the merging algorithm on constant merging timers (small timeouts). This improvement will slow down creation a little bit but it will make the first tick always a maximum of 50% off ^)^. It's actually 3-5 simple lines of code, but I've been working on save/load stuff, sooo ;D.
 
Top