System Timer32

Jesus4Lyf

Good Idea™
Timer32​
Version 1.06​

Requirements:
- Jass NewGen

JASS:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Timer32 ~~ By Jesus4Lyf ~~ Version 1.06 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//  What is Timer32?
//         - Timer32 implements a fully optimised timer loop for a struct.
//         - Instances can be added to the loop, which will call .periodic every
//           PERIOD until .stopPeriodic() is called.
//
//    =Pros=
//         - Efficient.
//         - Simple.
//
//    =Cons=
//         - Only allows one period.
//         - The called method must be named ".periodic".
//
//    Methods:
//         - struct.startPeriodic()
//         - struct.stopPeriodic()
//
//         - private method periodic takes nothing returns nothing
//
//           This must be defined in structs that implement Periodic Module.
//           It will be executed by the module every PERIOD until .stopPeriodic() is called.
//           Put "implement T32x" BELOW this method.
//
//    Modules:
//         - T32x
//           Has no safety on .stopPeriodic or .startPeriodic (except debug messages
//           to warn).
//
//         - T32xs
//           Has safety on .stopPeriodic and .startPeriodic so if they are called
//           multiple times, or while otherwise are already stopped/started respectively,
//           no error will occur, the call will be ignored.
//
//         - T32
//           The original, old version of the T32 module. This remains for backwards
//           compatability, and is deprecated. The periodic method must return a boolean,
//           false to continue running or true to stop.
//
//  Details:
//         - Uses one timer.
//
//         - Do not, within a .periodic method, follow a .stopPeriodic call with a
//           .startPeriodic call.
//
//  How to import:
//         - Create a trigger named T32.
//         - Convert it to custom text and replace the whole trigger text with this.
//
//  Thanks:
//         - Infinitegde for finding a bug in the debug message that actually altered
//           system operation (when in debug mode).
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library T32 initializer OnInit
    globals
        public constant real PERIOD=0.03125
        public constant integer FPS=R2I(1/PERIOD)
        public integer Tick=0 // very useful.
        
//==============================================================================
        private trigger Trig=CreateTrigger()
    endglobals
    
    //==============================================================================
    // The standard T32 module, T32x.
    //
    module T32x
        private thistype next
        private thistype prev
        
        private static method PeriodicLoop takes nothing returns boolean
            local thistype this=thistype(0).next
            loop
                exitwhen this==0
                call this.periodic()
                set this=this.next
            endloop
            return false
        endmethod

        method startPeriodic takes nothing returns nothing
            debug if this.prev!=0 or thistype(0).next==this then
            debug   call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
            debug endif
            set thistype(0).next.prev=this
            set this.next=thistype(0).next
            set thistype(0).next=this
            set this.prev=thistype(0)
        endmethod
        
        method stopPeriodic takes nothing returns nothing
            debug if this.prev==0 and thistype(0).next!=this then
            debug   call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had stopPeriodic called while not running!")
            debug endif
            // This is some real magic.
            set this.prev.next=this.next
            set this.next.prev=this.prev
            // This will even work for the starting element.
            debug set this.prev=0
        endmethod
        
        private static method onInit takes nothing returns nothing
            call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
        endmethod
    endmodule
    
    //==============================================================================
    // The standard T32 module with added safety checks on .startPeriodic() and
    // .stopPeriodic(), T32xs.
    //
    module T32xs
        private thistype next
        private thistype prev
        private boolean runningPeriodic
        
        private static method PeriodicLoop takes nothing returns boolean
            local thistype this=thistype(0).next
            loop
                exitwhen this==0
                call this.periodic()
                set this=this.next
            endloop
            return false
        endmethod

        method startPeriodic takes nothing returns nothing
            if not this.runningPeriodic then
                set thistype(0).next.prev=this
                set this.next=thistype(0).next
                set thistype(0).next=this
                set this.prev=thistype(0)
                
                set this.runningPeriodic=true
            endif
        endmethod
        
        method stopPeriodic takes nothing returns nothing
            if this.runningPeriodic then
                // This is some real magic.
                set this.prev.next=this.next
                set this.next.prev=this.prev
                // This will even work for the starting element.
                
                set this.runningPeriodic=false
            endif
        endmethod
        
        private static method onInit takes nothing returns nothing
            call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
        endmethod
    endmodule
    
    //==============================================================================
    // The original T32 module, for backwards compatability only.
    //
    module T32 // deprecated.
        private thistype next
        private thistype prev
        
        private static method PeriodicLoop takes nothing returns boolean
            local thistype this=thistype(0).next
            loop
                exitwhen this==0
                if this.periodic() then
                    // This is some real magic.
                    set this.prev.next=this.next
                    set this.next.prev=this.prev
                    // This will even work for the starting element.
                    debug set this.prev=0
                endif
                set this=this.next
            endloop
            return false
        endmethod

        method startPeriodic takes nothing returns nothing
            debug if this.prev!=0 or thistype(0).next==this then
            debug   call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
            debug endif
            set thistype(0).next.prev=this
            set this.next=thistype(0).next
            set thistype(0).next=this
            set this.prev=thistype(0)
        endmethod
        
        private static method onInit takes nothing returns nothing
            call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
        endmethod
    endmodule
    
    //==============================================================================
    // System Core.
    //
    private function OnExpire takes nothing returns nothing
        set Tick=Tick+1
        call TriggerEvaluate(Trig)
    endfunction
    
    private function OnInit takes nothing returns nothing
        call TimerStart(CreateTimer(),PERIOD,true,function OnExpire)
    endfunction
endlibrary


Demonstration:
JASS:
scope test initializer dotest
    private struct teststruct
        private integer endTick
        private method periodic takes nothing returns nothing
            call BJDebugMsg(I2S(this))
            
            if this.endTick == T32_Tick then // no more this.ticksLeft stuff and incrementing.
                call this.stopPeriodic() // never follow with a .startPeriodic() call
                call this.destroy()      // of any sort (when in the .periodic method).
            endif
        endmethod
        implement T32x
        static method create takes real duration returns thistype
            local thistype this = thistype.allocate()
            set this.endTick = T32_Tick + R2I(duration / T32_PERIOD)
            return this
        endmethod
    endstruct
    private function dotest takes nothing returns nothing
        // Creates structs which display their integer id 32 times a second for x seconds.
        call teststruct.create(3.0).startPeriodic()
        call teststruct.create(5.0).startPeriodic()
    endfunction
endscope
Should be faster than a struct/stack loop.
Actually, it should be faster than anything. The only downside is it only allows one period. So this can't make multi-period timer systems redundant.

Updates:
- Version 1.06: Deprecated T32 module, added T32x module with .stopPeriodic instead of return true/false stopping. Added T32xs module, which is similar to T32x but has .stopPeriodic/.startPeriodic safety, meaning these can be called regardless of the state of the struct, and if it is already stopped/started respectively, the call will be ignored.
- Version 1.05: Fixed a minor bug that occurred in debug mode (returning true would terminate the struct type's loop for that tick).
- Version 1.04: Added T32_FPS constant and a debug message for calling startPeriodic twice on a struct.
- Version 1.03: Optimised loop algorithm. The fastest struct looping ever.
- Version 1.02 (Release): Cleaned up code a lot. Simplified implementation.
- Version 1.01 (BETA): Finished drafting functionality.
- Version 1.00 (BETA): Release.
 

Gwypaas

hook DoNothing MakeGUIUsersCrash
If this faster than struct stack loop that might be right but how much load does this add? To me it seems like this somehow processes more code than a struct stack but the call goes of earlier? So, this is "faster" but takes more load on the CPU because of other shit being done after the call was done.
 

Viikuna

No Marlo no game.
Pretty cool. If this is faster than struct stack, ill probably use it, since I only need .025 period anyways.

( + TimerStack or TimerUtils. Ive learned to like one timer per instance style after I studied these slow time spell thingies. )

Remember to post some benchmarking when you finish the code.
 

Jesus4Lyf

Good Idea™
>What actually makes this different from TimedLoop?
I don't know what that is.

>this is "faster" but takes more load on the CPU because of other shit being done after the call was done.
Err. Faster basically means less load on the CPU. You lost me.

>How can we be sure about this?
Well, at it's core, this is a fully optimized struct/stack loop, but instead of running on a timer per instance, each loop is a condition on a core trigger. So it blends KT2 and the original struct/stack loop principle to make things that bit faster (which is useful if you have a lot of 1 instance things).

>similar working style with KT2...
Yepp.

>Remember to post some benchmarking when you finish the code.
Yeah, I intend to get around to that.
 

Jesus4Lyf

Good Idea™
>This one still uses 1 timer + trigger.
In total. Not per struct like whatever else.

>Faster or slower than KT2 ?
Faster. Faster than fast. Faster than everything. If anything is faster than this, this is pointless.

The point of this was basically to produce something that can't be beaten in speed. So things that use this will be written on the lightest timer system possible. :thup:

Edit: Version 1.01 (BETA) released. This actually should be finished, I think. But I haven't tested it enough to put my word to it being stable. But by all means test it, and bug report to your heart's content should anything arise. I'll thank you for it. :p
 

Jesus4Lyf

Good Idea™
Edit:
Some benchmarks:
KT2 and Timed Loop: 36,700 execs/sec (same speed, so there's no point using timed loop as it doesn't allow period).
Timer32: 48,000 execs/sec (about 31% faster).

So that's where we're headed. I'm not really happy until I see about 52k execs/sec though. Even if that is a fairly small gain.

TU Red, by the way, would still be around 31,000 execs/sec, but I didn't remeasure it for this test. Just stating for reference.

All tests were run using 10 instances of each of 10 different pieces of code running on one period (obviously one period, lol).

Edit: The thing that's annoying me is that PeriodicModule is still faster (50,000 execs/sec). Perhaps this idea is flawed. :(

Edit: Nup. Tested PM ported over to this idea, at best it gains 1% speed!... Meh, perfect is perfect. I'm going to have to reinvestigate this, I think. Oh, and I'm sure that speed gain is much higher for 1 instance or something, too. :)

Edit: Release 1.02 (no longer BETA). So the code got simpler. MEH!
Don't worry, I'll make it complicated again some time probably. This is probably 50,600 execs/sec, making it faster than fast. Problem is, the "timer" for each struct is always running and doesn't get paused. Not that it matters much, but I hope to fix this some time. 38% faster than KT2 and that fail TimedLoop thing. 1% faster than Periodic Module (joy). Probably grows a lot more efficient in comparison when you have less instances of a struct time firing periodically (the reverse is not true).
 

Kenny

Back for now.
KT2 and Timed Loop: 36,700 execs/sec (same speed, so there's no point using timed loop as it doesn't allow period).
LOL! Massive fail by Timed Loop. A system with that many drawbacks is still only equal in speed to KT2.

Anywho, this is looking mad. Hope you get that extra optimisation you wanted. But right now it seems lightning fast. No competiton.

EDIT:

Hahaha, I just saw the 1.02 release, back to VERY simple methods.

Oh and why Timer32? Does it have anything to do with 0.03125 as the period? :p
 

Jesus4Lyf

Good Idea™
>LOL! Massive fail by Timed Loop. A system with that many drawbacks is still only equal in speed to KT2.
Actually, it's worse than that... For spells (which is what it is written for) KT2 will actually be faster for low numbers of instances. So that thing is TOTALLY useless. T32 doesn't suffer the same symptoms, because it uses a single trigger. No timers at all, right now! :D

>No competiton.
Yeah. The idea of this is to finally create something final in terms of speed, really.

>Hahaha, I just saw the 1.02 release, back to VERY simple methods.
Cute, isn't it?

>Oh and why Timer32? Does it have anything to do with 0.03125 as the period?
Yeah. 32 times a second. It also has a sweet acronym: T32. I believe that 0.03125 is the optimum period. It's smooth and doesn't fire too much. :thup:

I mean, it's obvious that this whole speed thing is a bit of a joke, right? Like the percentage gain evaluates to a very, very small amount of processing time (like half a native or something). But I wanted to, as well as KT2, create a perfectly fast single-period system.

Actually, the reality is that T32 is just a single case of KT2 using modules to hard code a little more than before. It just had to be done.
 

chobibo

Level 1 Crypt Lord
from this system
JASS:
call TriggerRegisterTimerEvent(Trig,PERIOD,true)


from common.j
JASS:
// Creates it's own timer and triggers when it expires
native TriggerRegisterTimerEvent takes trigger whichTrigger, real timeout, boolean periodic returns event


from creator of the system
T32 doesn't suffer the same symptoms, because it uses a single trigger. No timers at all, right now!
No timers? A trigger that creates it's own (internal) timer has no timers?

A timer uses 1 handle, A trigger with a timer event uses 3; a timer, a trigger and an event. Now if used with code funcs you add 1 more to a timer, on your case, it uses a condition func, it uses 2 handles, a boolexpr and a triggercondition.
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
A timer uses 1 handle, A trigger with a timer event uses 3; a timer, a trigger and an event.
Does it matter? It won't effect much unless you use H2I-0x10000 in your map..
 

Jesus4Lyf

Good Idea™
Lol, it's completely irrelevant, he's just saying "haha you're wrong" :D. I'll fix that.
It sounded novel at the time.

Anyway, success.

After a fresh reboot, the 1.02 algorithm gave me 51,800 execs/sec.
Here it is:
JASS:
        private static thistype array Data
        private static integer        DataMax=0
        
        private static method PeriodicLoop takes nothing returns boolean
            local integer i=.DataMax
            loop
                exitwhen i==0
                if .Data<i>.periodic() then
                    set .Data<i>=.Data[.DataMax]
                    set .DataMax=.DataMax-1
                endif
                set i=i-1
            endloop
            return false
        endmethod

        method startPeriodic takes nothing returns nothing
            set .DataMax=.DataMax+1
            set .Data[.DataMax]=this
        endmethod</i></i>

And the 1.03 algorithm... (which I'm very proud of, actually managing to optimise the above.)
JASS:
        private thistype next
        private thistype prev
        
        private static method PeriodicLoop takes nothing returns boolean
            local thistype this=thistype(0).next
            loop
                exitwhen this==0
                if this.periodic() then
                    // This is some real magic.
                    set this.prev.next=this.next
                    set this.next.prev=this.prev
                    // This will even work for the starting element.
                    call DisableTrigger(Trig) // For benching.
                endif
                set this=this.next
            endloop
            return false
        endmethod

        method startPeriodic takes nothing returns nothing
            set thistype(0).next.prev=this
            set this.next=thistype(0).next
            set thistype(0).next=this
            set this.prev=thistype(0)
        endmethod

Yep. 55,000 execs/sec. I removed a single subtraction. (That's how much of a joke the speed thing is at this point. That gave a 6% gain. In other words, this code is one of the most lightweight JASS things you'll ever see.)

I'll bring out 1.03 in a sec, probably. :)

Edit: Released 1.03 after testing.

>Huh? Much like TT now...
TT used the algorithm developed in KT1, basically. Cohadar helped me optimise it a lot when KT1 first came out. But in version 1.03 of T32 I've changed my implementation significantly (different underlying data structure), and I'm nearly convinced that it can no longer be made faster. :)
And speedwise this is something like twice as fast as TT.
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • The Helper The Helper:
    You would probably have to take a loan to get the Rice cert it is like 14l
  • The Helper The Helper:
    I was hoping you were going to say you wanted to be a cook and wished to open up a Food Truck there are so many opportunities for that here
  • The Helper The Helper:
    you can only open the bar if you sell food and a food truck in the parking lot counts
  • The Helper The Helper:
    here in houston
  • The Helper The Helper:
    most bars are struggling to build food prep and even some I know are doing there own food trucks
  • The Helper The Helper:
    but yeah man you should come to Houston there is so much more opportunity here than San Antonio. Just look at all the homeless people on the Riverwalk and downtown there you cannot even walk down the street without getting mobbed them
  • The Helper The Helper:
    check out this place apparently it is the best roommate site out there roommates.com lol
  • Varine Varine:
    The homeless people reminded me of New York
  • Varine Varine:
    I'll check it out, my lease expires in like a few months so I need to figure something out.
  • Varine Varine:
    I tried to open a food truck in Sandpoint a few years ago but the city said no, then like two years later they passed new laws to encourage it and gave them all fucking tax breaks and shit. And at that point I didn't have enough money to go buy one so I was way too late to the game once other cities realized they could just bring their existing truck there, or sell their brick and mortar.
  • Varine Varine:
    I do like food trucks though, they're way more fun than restaurants and usually I also get some of that tip money there.
  • Varine Varine:
    Restaurants are really hard if you don't have a back up source of income to fund them sometimes, in my experience most owners either got really really lucky, had existing money that allowed them to buy an already successful one, or they have family money to fall back on for loans. It's few and far between to see very inspiring success stories where the person actually got it through hard work and perseverance. I thought about opening one lots and I always just end up not thinking it'll work. I would much rather just find someone that has that money and go work for them
  • The Helper The Helper:
    i am not talking about opening a brick and mortar I am talking about food trucks though I have seen some brick and mortar mexican places open up during covid that are doing good because there food is awesome!
  • The Helper The Helper:
    Food trucks are killing out here because Bars cannot open unless they sell food and the governor ok'd the use of a food truck in front as serving food
  • The Helper The Helper:
    Just not enough food trucks
  • The Helper The Helper:
    a buddy of mine just opened up a food truck and he is killing it but he is a great cook and has awesome food right in the middle of the pandemic too food trucks are immune to pandemic because they are take out in Texas you will always be able to get take out or delivery
  • The Helper The Helper:
    He is in the different food facebook groups in houston and posts videos of him making his food
  • The Helper The Helper:
    he is killing it
  • jonas jonas:
    @Varine with the restaurants, there surely is a lot of luck and hard work but most restaurants fail because they suck. Flair isn't right, economics not well thought through, food is mediocre or sucks, location is bad, etc. If you're thinking about opening one, make sure you're looking at the stories of those that would be playing at your level, don't let your hopes be dragged down by all the subpar restaurants out there.
  • jonas jonas:
    I'm a bit worried about the future of data science, there's an influx of incompetent people hired by incompetent managers, that can't last. But I'm sure smart data scientists will always be useful and hireable. Same as smart security people.
  • jonas jonas:
    My sister in law worked in a vegan food truck, the owners were also making a killing, added several trucks and opened two restaurants over 5 years
  • Ghan Ghan:
    The CompTIA stuff is pretty much crap unfortunately. The places that ask for those low-level certs aren't likely places you want to work. For IT you really want a degree, but in the meantime you need to figure out some way to learn the skills. Cybersecurity is really hot right now so competition is fierce.
  • tom_mai78101 tom_mai78101:
    I realized I don't have anything much to say, other than "Good luck!". Compared to other places, I'm just very lucky we didn't have a lot of surges of cases coming in.
  • The Helper The Helper:
    My daughter just got back from school at Georgetown TX, outside of Austin and said there were 100 cases in the last week but she also said nobody was wearing a mask anywhere so people mask up and stay safe!
    +1

    Members online

    No members online now.

    Affiliates

    Hive Workshop
    Top