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

Ask about ACS, DECORATE, ZScript, or any other scripting questions here!

Moderator: GZDoom Developers

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!)
User avatar
Nash
 
 
Posts: 17439
Joined: Mon Oct 27, 2003 12:07 am
Location: Kuala Lumpur, Malaysia
Contact:

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

Post by Nash »

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.
User avatar
Fishytza
Posts: 781
Joined: Wed Feb 23, 2011 11:04 am
Preferred Pronouns: No Preference
Contact:

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

Post by Fishytza »

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.
User avatar
Nash
 
 
Posts: 17439
Joined: Mon Oct 27, 2003 12:07 am
Location: Kuala Lumpur, Malaysia
Contact:

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

Post by Nash »

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.
User avatar
krokots
Posts: 266
Joined: Tue Jan 19, 2010 5:07 pm

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

Post by krokots »

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!
User avatar
Arctangent
Posts: 1235
Joined: Thu Nov 06, 2014 1:53 pm
Contact:

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

Post by Arctangent »

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.
User avatar
Matt
Posts: 9696
Joined: Sun Jan 04, 2004 5:37 pm
Preferred Pronouns: They/Them
Operating System Version (Optional): Debian Bullseye
Location: Gotham City SAR, Wyld-Lands of the Lotus People, Dominionist PetroConfederacy of Saudi Canadia
Contact:

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

Post by Matt »

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?)
User avatar
Matt
Posts: 9696
Joined: Sun Jan 04, 2004 5:37 pm
Preferred Pronouns: They/Them
Operating System Version (Optional): Debian Bullseye
Location: Gotham City SAR, Wyld-Lands of the Lotus People, Dominionist PetroConfederacy of Saudi Canadia
Contact:

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

Post by Matt »

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;
    }
} 
User avatar
Xaser
 
 
Posts: 10772
Joined: Sun Jul 20, 2003 12:15 pm
Contact:

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

Post by Xaser »

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.]
User avatar
Matt
Posts: 9696
Joined: Sun Jan 04, 2004 5:37 pm
Preferred Pronouns: They/Them
Operating System Version (Optional): Debian Bullseye
Location: Gotham City SAR, Wyld-Lands of the Lotus People, Dominionist PetroConfederacy of Saudi Canadia
Contact:

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

Post by Matt »

22684 ms/f. :shock:
Kotti
Posts: 86
Joined: Tue Dec 27, 2016 4:08 am

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

Post by Kotti »

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.
Post Reply

Return to “Scripting”