HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

"AwesomeTimeEvent"

04-25-2008, 02:54 AM#1
Vexorian
Features:
- Ridicules vJass syntax by exploiting it to the max.
- Requires 2 textmacros from user. So it is extremely unfriendly.
- Only works when delay time is constant
- Can only pass one integer variable, although if you implement the method manually instead of using textmacros you can pass a lot more.
- Pending name, will change it someday.
- It's low level, if you want to make your code look ugly, this is the way!
- cannot use return inside an AwesomeTimeEvent block (Really, don't do it)

Disadvantages:
- Uses only 2 timers and is MUI, instead of all those superior systems that require hundreds of timers. Or those that require thousands...
- No function calls at all during timer expire + it only uses O(1) array operations, comparisons . Unlike other systems that require at least an H2I function call or use logarithmic evaluation time or interfaces ( TriggerEvaluate)

Thanks to Strilanc for reminding me that I don't need Mod for circular queues.

Expand script:

Expand sampleusage:
04-25-2008, 04:30 AM#2
zen87
So... how do we use this thing O_o
04-25-2008, 05:51 AM#3
Strilanc
It looks sortof like a call queue, except the calls are spaced by some constant amount if you queue them too fast.
04-25-2008, 06:17 AM#4
TheDamien
Awesome idea, but it won't be a true system until:
  • Cohadar has spammed the thread with ABC links.
  • You have explained why it is noob friendly and why all alternatives suck.
  • It is given a nonsensical abbreviation-esque title. I would suggest something like XYZ or SXC-TMRS.
  • Some spurious benchmarks are posted.
  • It is linked to in your signature.
  • A massive comments block is injected into the code.

I have even come up with a benchmarking script for you:

Collapse JASS:
//! textmacro UberBenchmark takes NAME
library $NAME$
    // Call $NAME$_Run() to run the benchmark.
    public function Run takes nothing returns nothing
        local integer execs
        if ("$NAME$" == "AwesomeTimeEvent") then
            set execs = 1337
        else
            set execs = GetRandomInt(20, 100)
        endif
        call BJDebugMsg("$NAME$: "+I2S(execs)+" executions per millisecond.")
    endfunction
endlibrary
//! endtextmacro

//! runtextmacro UberBenchmark("AwesomeTimeEvent")
//! runtextmacro UberBenchmark("CompetingSystem")

NB: Preceding text is intended purely as satire.
04-25-2008, 02:08 PM#5
Vexorian
Quote:
Originally Posted by zen87
So... how do we use this thing O_o
1. Don't, at least until we get a better name and the way to call the textmacro is decided, I am not so sure about passing 8190 all the time...

2. It's easy as long as you got an screwed up mind


Collapse JASS:
// This library creates a special effect and destroys it after 14 seconds, this is not the
// best application for AwesomeTimeEvent because of the constant timeout, but it should
// be good for explaining how to use it
library TimedFX //you must run the textmacro inside a scope or library.

    //
    private struct data
        effect fx
    endstruct

    //begin TimeEvent handling block
    //                                      [name of time event] [instance limit] [timeout] 
    //! runtextmacro AwesomeTimeEvent_Begin("DoDestroyEffect"     ,"8190"          ,"14.0")
      //inside this block you can use the integer argument 'value' to recognize which
      //event just fired
      local data D=data(value) //notice we can use our own locals
         call DestroyEffect(D.fx)
         set D.fx=null
         call D.destroy()
    //! runtextmacro AwesomeTimeEvent_End("DoDestroyEffect")

    function TimedFX takes string fxpath, real x, real y returns nothing
     local data D=data.create()
        set D.fx = AddSpecialEffect(fxpath,x,y)

        //we use push to create a new instance of the TimeEvent
        call DoDestroyEffect.push( integer(D) )
        // So, after 14.0 seconds, that 'function' above will execute with
        // the index of the instance as value argument.
    endfunction


endlibrary


---
I think I'll remove the waittime argument from the macro and add it to push instead. The rule still remains as "always use constant timeouts" but I think the code will make more sense to more people if push had the timeout.

There is also the fact that timeout can be variable, with few conditions:
- all active instances should have the same timeout.
- all active instances before your push call should timeout before the new action.

Anyways, for normal people it should remain as "always use constant timeouts".


Edit:
I have tried with things like 0.04 seconds. It indeed lags less yet, I have noticed a huge issue, there were less ticks than when you used a normal timer with attachments.

It looks to me TimerGetElapsed is not precise, at all. I made an isolated test, a timer that is started at map init and has a long timeout, + another timer that expires every 0.04 seconds, and outputs the elapsed time for the first, the output repeats itself after three tics, it is lame, but that's how it seems to be working.

Perhaps it is something blizz made as an optimization, the thing is that TimerGetElapsed is not accurate, thus dooming this system and any other based on TimerGetElapsed into long timeouts (looks like longer than 0.5s is safe)


Edit II:

In order to test this on 0.04 seconds cases, I did this (lame)

Collapse JASS:
library AwesomeTimeEvent initializer init
    globals
        public real NOW=0
        private constant real tick =0.001
    endglobals

    private function dot takes nothing returns nothing 
        set NOW=NOW+tick
    endfunction
    private function init takes nothing returns nothing
        call TimerStart(CreateTimer(),tick,true, function dot)
    endfunction

endlibrary


Anyways, I test it against a timer that uses this for attachments:

Collapse JASS:
    private function arrg takes nothing returns nothing
     local obj fx=xefx( ATTACH[H2I(GetExpiredTimer())-0x100000 ]   )


ATTACH is actually a global declared like this:

Collapse JASS:
  globals
     private obj array ATTACH[60000]
endglobals
   

So, it is the typical dual function call attach system.

Anyways, the results were dissapointing, after 200 instances AwesomeTimers drops fps to 18~ , while the Attach system using simultaneous timers moves it to 24ish.

The good news is that I later tested the deal with an array + loop one, in that, after 200 instances the fps is around 30ish...

(I moved the camera away to some black area to avoid graphics-caused fps-drop in the equation)

From what it looks like this thing is slower than the dual function attach one, that's not true however since I had to use the above library hack to have precision, I am yet to think of a good way to test performance on a setting with longer delays so I don't have to use that lame thing and keep using TimerGetElapsed instead.

Edit II: If anyone is wondering, changing the ATTACH array so it got size 8000 (and thus becomes a normal array, and this method is equivalent to the one that preloads 6000 timers) the fps after 200 instances is 28ish instead of 24ish (when we have the function call) array loop still beats it, but it is faster than normal attach systems that use 2 function calls.

I just don't get why this time event thing is much slower than other things that use so many funciton calls, but I think that the 0.001 seconds timer helps... Wonder how else could I fix the TimerGetElapsed pecision issue...


edit III: Changing tick constant to 0.01 raises fps after 200 instances to 22 ish, still too slow , it also looks like it is not precise enough anymore.

Edit IV: If I change ATTACH size to 400000 - thus making it robust as heck - the fps is still 28ish, great news?

--
There is something I do not consider when testing attach-based methods, and it is that you are supposed to recycle the timer eventually.

--
Edit V: Without that method to increase precision, and using TimerGetElapsed, TimedEvent for periodic 0.04 seconds delay is imprecise as heck, as a matter of fact the peasants moved slower than with the other methods, but the fps is around 30ish which means it is fast, but it is also useless for such low timeouts. :(

--
Edit VI: Has anyone seen a map in which handle ids got as large as "17177216"?
04-25-2008, 03:55 PM#6
Vexorian
So, I made a version that allows periodic events and to get rid of the TimerGetElapsed issue using a timer stack instead. No longer needs that library.

Still no function calls required.
Unfortunately, it uses a timer per instance, which blows.

Collapse JASS:
//! textmacro AltAwesomeTimeEvent_Begin takes name, max, waittime

    private struct $name$[8190]
        private static constant real WAIT_TIME=$waittime$
        public static constant integer MAX=$max$

        private static $name$ begin=0
        private static $name$ end=0

        private integer v
        private static real next
        private static  timer array V[$max$]
        private static  integer N=0

        private static method onexpire takes nothing returns $name$
         local integer value=.begin.v
         local boolean stop=false
            //please don't use return inside AwesomeTimeEvent blocks...

//! endtextmacro
//! textmacro AltAwesomeTimeEvent_End takes name
            set .begin=$name$(integer(.begin)+1)
            if (.begin==.MAX) then
                set .begin=0
            endif
            if (not stop) then
                set .end.v=value
                set .end=$name$(integer(.end)+1)
                if(integer(.end)==.MAX) then
                    set .end=0
                endif
                return 0
            endif
            call PauseTimer(GetExpiredTimer())
            if(.N==.MAX) then
                debug call BJDebugMsg("Warning: No space in stack for timer, so we are just letting it go and leak...")
            else
                set .V[.N]=GetExpiredTimer()
                set .N=.N+1
            endif
         return 0
        endmethod


So, the mystery here is why it drops fps to 25ish, when just using the timers altogether and some attach system that requires a function call is 28ish, I think I need an actual benchmark cause fps seems to be a little ambiguous...
04-25-2008, 09:04 PM#7
grim001
very interesting investigations