[BUSTED] Making an ultra light Tick() in ZScript

Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.

Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)

Post a reply

Smilies
:D :) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :geek: :ugeek: :!: :?: :idea: :arrow: :| :mrgreen: :3: :wub: >:( :blergh:
View more smilies

BBCode is OFF
Smilies are ON

Topic review
   

Expand view Topic review: [BUSTED] Making an ultra light Tick() in ZScript

Re: [BUSTED] Making an ultra light Tick() in ZScript

by Kotti » Tue Feb 20, 2018 2:39 am

Graf said some interesting things about VM performance here: viewtopic.php?p=958645#p958645

I think the conclusion should be clear that using the VM to improve performance will never work. The regular Tick function will be very fast unless it needs to do some horizontal movement. This alone is what costs so much time in the Doom engine. Just run Nuts.wad and type 'stat think' at the start without shooting if you want proof. The time is relatively reasonable, it will only get slow once the monsters start moving and shooting.
Matt wrote: - I still want to know if the virtual machine overhead applies to anonymous functions - i.e., whether putting a complicated repeating code into a looping spawn state and then using +nointeraction would make more sense.*
I think the answer should be obvious. For the VM a function is a function is a function. If you look at the disassembly the compiler can create you will see that the only difference between a named and an anonymous function is that anonymous functions have a synthesized name.


@Xaser:

If you want to know, start GZDoom with the '-dumpdisasm' command line option and look if it produces code for that intermediate function or not.

Re: [BUSTED] Making an ultra light Tick() in ZScript

by Matt » Tue Feb 20, 2018 12:19 am

22684 ms/f. :shock:

Re: [BUSTED] Making an ultra light Tick() in ZScript

by Xaser » Mon Feb 19, 2018 9:31 am

In the Stated actor, the A_Test function call is wrapped inside an anonymous function, like so:

Code: Select all

TNT1 A 1 nodelay{A_Test();}
Unless GZD is smart enough to notice the outer function wrapper is unnecessary, this is two function calls per tick, not just one; wouldn't this negatively affect performance? I'm curious to know if it gets any better if you take the braces out.

[Not in a position to test it myself at the moment; just curiously snooping about while researching something.]

Re: [BUSTED] Making an ultra light Tick() in ZScript

by Matt » Fri Feb 16, 2018 2:11 am

So I did my usual benchmark.

Turns out if you do have a big bunch of things you need to do every tick, you're better off with a Tick override rather than +nointeraction and putting all that in the spawn state, even if you're calling extra instructions to advance the actor states.

Code: Select all

/*
vid_fps 1;summon ticky;
23150 ms/f
vid_fps 1;summon stated;
23800 ms/f
*/
class ticky:idledummy{
    default{missiletype "ticky_";}
    override void postbeginplay(){
        super.postbeginplay();
        for(int i=0;i<1000;i++){
            spawn(missilename,pos);
        }
        destroy();
    }
}
class stated:ticky{
    default{missiletype "stated_";}
}
class stated_:actor{
    void A_Test(){
        for(int i=0;i<1000;i++){
            setorigin((
                pos.x+frandom(-0.0001,0.0001),
                pos.y+frandom(-0.0001,0.0001),
                frandom(floorz,ceilingz)
            ),true);
            stamina=random(0,99999);
            accuracy=random(0,99999);
            double yargh=pos.length();
            if(!stamina)A_LogFloat(yargh);
        }
    }
    states{
    spawn:
        TNT1 A 1 nodelay{A_Test();}
        wait;
    }
}
class ticky_:stated_{
    override void Tick(){
        clearinterpolation();
        A_Test();
        if(CheckNoDelay()){
            if(tics>0)tics--;    
            while(!tics){
                if(!SetState(CurState.NextState)){
                    return;
                }
            }
        }
    }
    states{
    spawn:
        TNT1 A -1;
    }
} 

Re: [BUSTED] Making an ultra light Tick() in ZScript

by Matt » Wed Feb 14, 2018 11:48 am

Back in early 2017 (or possibly even earlier) I changed the debris actors in Hideous Destructor from being bouncing projectiles to using a custom Tick() override and I remember getting significant performance improvements doing so. I have not tested this recently so it might have gotten worse with more features added to ZS, but ever since I made the switch it has never been a problem.


EDIT: Just a couple thoughts:

- yeah, +nointeraction is definitely way less overhead than any possible tick override.

- The main Tick() itself is actually a lot smaller than I had been expecting.

- I'm pretty sure calling Super.Tick() in an override would still leave you calling the virtual machine every tick, so it should be guaranteed have that extra overhead if there's any override at all no matter what's in it.

- I still want to know if the virtual machine overhead applies to anonymous functions - i.e., whether putting a complicated repeating code into a looping spawn state and then using +nointeraction would make more sense.*


*EDIT: For what it's worth, relying on +nointeraction and relying on an anonymous function would shave off the ClearInterpolation() and frame-advancing stuff, all of which add to the VM overhead. As far as I can tell the only good reasons for a tick override are:

1. Something that must happen continuously on an actor regardless of its states (e.g., it's burning and smoke is coming out of it) and it's stupidly unmaintainable to explicitly call it in every single frame

2. You're overriding the basic movement code so that "vel" should not be applied directly (+nointeraction will still have the actor move according to its own internal velocity; however, looking at the code again this might be avoided with +noblockmap?)

Re: [BUSTED] Making an ultra light Tick() in ZScript

by Arctangent » Sat Feb 10, 2018 12:18 pm

Yeah, the thing is that monsters, especially a lot of them, are going to cause a significant increase in processing anyway through A_Look and A_Chase alone. Overriding Tick() will cause an increase in that, but it'll only be more than a mild increase if you're doing some significantly intense stuff in it that should probably be pushed into other contexts in the first place.

Re: [BUSTED] Making an ultra light Tick() in ZScript

by krokots » Sat Feb 10, 2018 11:05 am

FishyClockwork wrote:
Nash wrote:And remember that think time will blow up obviously because of high actor count, which providing a Tick override just magnifies the problem.
And that's why it's discouraging. Especially if you wanted a custom monster with a Tick() override and that monster is a common foe you place around a map.
In my mod every monster in a map has got an overriden Tick() and it works just fine, so no problems!

Re: [BUSTED] Making an ultra light Tick() in ZScript

by Nash » Sat Feb 10, 2018 8:36 am

Well in my isolated test, all 1300++ actors were visible to me at once in one room, and the test actor that I used was the one I demonstrated in my OP where I attempted to bypass the engine's Tick's completely.

Perhaps there's some optimization in that massive AActor::Tick and in realistic situations, you won't see all thousands of actors at once sooooo... IDK? Custom monsters would most likely have a Super.Tick() stuck somewhere in the override so perhaps that allows the engine to optimize the performance. This is all just conjecture, I don't feel like reading all of AActor::Tick for now LOL.

Re: [BUSTED] Making an ultra light Tick() in ZScript

by Fishytza » Sat Feb 10, 2018 8:11 am

Nash wrote:And remember that think time will blow up obviously because of high actor count, which providing a Tick override just magnifies the problem.
And that's why it's discouraging. Especially if you wanted a custom monster with a Tick() override and that monster is a common foe you place around a map.

Re: [BUSTED] Making an ultra light Tick() in ZScript

by Nash » Sat Feb 10, 2018 8:05 am

The bottom line is for high-count objects (non-interactive decorative actors like grass and stones and things like that), the best solution is to use +NOINTERACTION. OP was assuming that I could make things faster by providing an empty Tick() override but that backfired. And remember that think time will blow up obviously because of high actor count, which providing a Tick override just magnifies the problem.

Re: [BUSTED] Making an ultra light Tick() in ZScript

by Fishytza » Sat Feb 10, 2018 8:02 am

Yeah, I just realized FastProjectile also overrides Tick().
I should start paying more attention and read the forums more often. >_>

Then again, a FastProjectile isn't something that exists for very long. I guess moral of the story is, be careful which functions you override? Or maybe I should stop worrying over a few milliseconds.

Re: [BUSTED] Making an ultra light Tick() in ZScript

by phantombeta » Sat Feb 10, 2018 7:33 am

No, not at all!
If that were so, GZDoom would be running extremely slow now. Quite a bit of C++ code has been exported/converted to ZScript, including entire classes. (for example, FastProjectile is completely written in ZScript)

Re: [BUSTED] Making an ultra light Tick() in ZScript

by Fishytza » Sat Feb 10, 2018 5:41 am

So, bottom line is I should never ever override Tick() unless it's, like, one or two very special actors in the entire map?
This is rather discouraging.

Re: [BUSTED] Making an ultra light Tick() in ZScript

by krokots » Sat Feb 10, 2018 4:41 am

Apeirogon wrote:
Arctangent wrote:So it's not exactly odd that calling the ZScript VM every tic, even to do nothing, causes worse performance than just letting ZDoom tell most of the code to fuck off using its own methods.
Look at this.
That just confirms, if NOINTERACTION is true, the code skips almost 500 lines of code, not counting other functions.

Re: [BUSTED] Making an ultra light Tick() in ZScript

by Apeirogon » Sat Feb 10, 2018 3:47 am

Arctangent wrote:So it's not exactly odd that calling the ZScript VM every tic, even to do nothing, causes worse performance than just letting ZDoom tell most of the code to fuck off using its own methods.
Look at this.

Top