[BUSTED] Making an ultra light Tick() in ZScript
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!)
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!)
Re: [BUSTED] Making an ultra light Tick() in ZScript
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
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.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.
Re: [BUSTED] Making an ultra light Tick() in ZScript
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.
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
In my mod every monster in a map has got an overriden Tick() and it works just fine, so no problems!FishyClockwork wrote: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.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.
- Arctangent
- Posts: 1235
- Joined: Thu Nov 06, 2014 1:53 pm
- Contact:
Re: [BUSTED] Making an ultra light Tick() in ZScript
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.
- 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
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?)
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?)
- 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
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.
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
In the Stated actor, the A_Test function call is wrapped inside an anonymous function, like so:
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.]
Code: Select all
TNT1 A 1 nodelay{A_Test();}
[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
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.
@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.
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.
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.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.*
@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.