ID:2055964
 
So I think I'm gonna start doing these when I'm high and up all night, unfortunately I'm missing the first bit, so give me a moment to get things rolling.

At /tg/Station13, we learn a lot of lessons in performance. Between the 9,917 obj types, 223 mob types, 20,557 procs, and 316,952 lines of .dm code totalling 10mb (of just code!). There is a lot going on, and a lot to optimize.

Under 510, we've basically gotten rid of lag because of one simple var, So i wanted to show you how to harness its power.

First off, let's clarify what I mean by lag. There are technically two things that players will generally consider to be "lag":

Network lag, as in when the network isn't fast enough, or has a bit of a shutter, and this causes things to be delayed.

Processing lag/cpu lag, when something causes byond miss a tick and not send out client updates. If you code is running, byond isn't sending out client updates.

This post is about the latter kind of lag.

Now, you might be thinking, "but but, we don't do that, byond never misses a tick in our code", to which I say, BULLSHIT. a lot of projects on here run at high enough world.fps or low enough world.tick_lag that byond missing a tick or two isn't that noticeable, but it still happens. don't believe me? put this in your project and watch the missed ticks add up:

var
worldstarttime = 0
worldstarttimeofday = 0
client
Stat()
if (!worldstarttime || worldstarttimeofday)
worldstarttime = world.time
worldstarttimeofday = world.timeofday
var/tickdrift = (world.timeofday - worldstarttimeofday) - (world.time - worldstarttime) / world.tick_lag
statpanel("Tick Drift", "[round(tickdrift)] Missed ticks")
..()


in a perfect world, world.timeofday and world.time will increase at the same rate, 1 for every 1ds, or 10 for every second. Any missed tick will prevent world.time from ticking up on time, but won't prevent world.timeofday from ticking up.

So, the key things we want to look at, are any cases where a for/while loop could take longer than 15% of a tick to run. why 15% and not 100%? Because the for/while loop could start 85% of a tick thru, (it could start 99% of a tick thru as well, but that encompuses all loops and isn't productive.

"But, but, oh great stoner one, how does world.tick_usage help us?"

world.tick_usage lets us see what percentage of the way into a byond tick we are. If its at 100%, we have used of the entire tick, and anything else is byond running late for the next tick.

So you can just do something as simple as: if (world.tick_usage > 90) sleep(world.tick_lag)

If the loop is about to expand to the next tick, we just sleep for a tick

You can make that buffer be anything you want, have it stop at 75% of the way thru a tick, or 85%. All up to you, at /tg/station we use 85%, but avoid starting costly loops before 75%, sleeping first.

You can also make it a bit more advance:
proc
lagstopsleep()
var/tickstosleep = 1
do
sleep(world.tick_lag*tickstosleep)
tickstosleep *= 2 //increase the amount we sleep each time since sleeps are expensive (5-15 proc calls)
while(world.tick_usage > 75 && (tickstosleep*world.tick_lag) < 32) //stop if we get to the point where we sleep for seconds at a time

Then just add if (world.tick_usage > 90) lagstopsleep() to your expensive forloop.

When adding these checks it is best to do these at the end of the loop's block, not the start, as it ensures that it gets to run at least once each cycle, and doesn't stall out when a lot is going on and contending for runtime. (it also lowers issues with things disappearing and become null):

proc
lagglyforloop()
for (var/atom/thing in world)
dostuff()
if (world.tick_usage > 90) lagstopsleep()


"But wait!" You say, "We use sleeps in our loops, so we are covered!"

sleep(-1) isn't perfect, it tends to not kick in until after byond misses a tick, it also has a high enough overhead that replacing every sleep(-1) with world.tick_usage checks will give you a noticeable improvement to the speed and performance of these expensive loops, and you will notice less lag while it's running.

set background = 1 just adds sleep(-1) to every for/while loop run, so see above

sleep(1) or things like if (i % 100 = 0) sleep(1) are imperfect, you are assuming you started at the beginning of the tick, when your code could have been the last thing ran, you are also assuming things about how many times you can run in the loop without making byond miss a tick, tick_usage allows you know exactly how long your loop can run without making things miss a tick. So you aren't slowing your code down too much, and you aren't risking not slowing it down enough. That's the key point of this, it allows you to slow down the code's processing exactly as much as is needed to prevent lag, no more, no less.

I'd just like to thank goonstation's volundr for the idea and feature request around world.tick_usage, and most of the ideas around how to use it. as well as goonstation's docsingh for reminding me about it so I could code it for /tg/station13. (also some assholes at /vg/ might have been involved)

And if you code this for your project and it removes lag, please remind your users to send some money byond's way. This single variable is one of the best improvements in byond since I started coding for byond. (ok only if your a function over form type and care more about backend over frontend like I do)
Good post. I just started using tick_usage last night, so this is right on time.

Favorited and thanks for sharing.
I'm glad you've gotten more involved with the forums in the last few months, Stoned One. You're a smart guy.
Most of my stuff is utilities or turn based, but this was an interesting read and I'll keep it in mind for the future. Thanks for sharing.
In response to Kumorii
Kumorii wrote:
I'm glad you've gotten more involved with the forums in the last few months, Stoned One. You're a smart guy.

A lot of us from tgstation13 Have been getting more involved here.
While feature requests and bug reports are quite simple it's a lot more than what we've done in the past.
A lot of us from tgstation13 Have been getting more involved here.
While feature requests and bug reports are quite simple it's a lot more than what we've done in the past.

It's kind of a godsend, honestly. It's given Lummox the ability to understand the direction you guys are pushing with the engine and it's led to a lot of neat improvements for all of us.

You guys kind of have a unique perspective on BYOND. You guys do a lot of stuff in some interesting ways, and most of you come from background where you have entirely different skillsets than most of the community. That diversity has been and will continue to be good for the engine.
In response to Ter13
Ter13 wrote:
It's kind of a godsend, honestly. It's given Lummox the ability to understand the direction you guys are pushing with the engine and it's led to a lot of neat improvements for all of us.

You guys kind of have a unique perspective on BYOND. You guys do a lot of stuff in some interesting ways, and most of you come from background where you have entirely different skillsets than most of the community. That diversity has been and will continue to be good for the engine.

I disagree with you. I think they're all idiots that don't drift far from the avg. mentality of other community members. Sure they work with other technologies and incorporate more professional workflows, but they're still doing it just as badly and incorrect as anything else.

Retards come in different flavors... BYOND = high-level programming retards, SS13 = more Linux oriented retards. They both do things just as bad.

I'm sure they helped Lummox a lot in terms of financing the engine, giving it popularity, and driving him to improve BYOND, but that's Lummox's shortcoming. Even in this very post, there are a lot of determinants not being taken into account at all, and it's not entirely accurate.
There was a troll post but it disappeared when I logged in so I'll just say you guys are pretty neat too.
but it disappeared when I logged

Godsend #2
In response to Flick
Flick wrote:
Most of my stuff is utilities or turn based, but this was an interesting read and I'll keep it in mind for the future. Thanks for sharing.

make a turn based lib please
In response to Ganite
I tend to use a lot of ACWraith's librarys. They can be a little complicated to figure out, but they work really well.
In response to Ter13
Ter13 wrote:
It's kind of a godsend, honestly. It's given Lummox the ability to understand the direction you guys are pushing with the engine and it's led to a lot of neat improvements for all of us.

You guys kind of have a unique perspective on BYOND. You guys do a lot of stuff in some interesting ways, and most of you come from background where you have entirely different skillsets than most of the community. That diversity has been and will continue to be good for the engine.