ID:2829531
 
BYOND Version:514
Operating System:Windows 7 Home Basic 64-bit
Web Browser:Chrome 106.0.0.0
Applies to:Dream Seeker
Status: Open

Issue hasn't been assigned a status value.
Descriptive Problem Summary:
I am currently trying to pin down some client performance issues with the stat() panel on an ss13 server. To this end I've been trying to make test cases that might illustrate the issues & provide something that can be debugged.

However, one hurdle I've come across is that quantifying the lag produced by the panel itself is difficult. The CPU & time spent on a client/stat() call appears to be disproportionate to the effect it has on client performance; it's punching well above its weight on the profiler. This makes it difficult to test different things, as I have to rely on largely qualitative performance changes (ie: does this feel more or less choppy than before?).

Numbered Steps to Reproduce Problem:
1. Run the test project from the code snippet.
2. Profile 100 calls on status panel static.
3. Profile 100 calls on status panel large.
4. Compare results.

Code Snippet (if applicable) to Reproduce Problem:
var/global/number = 0
var/global/name = "Points"
var/global/looping = 1
var/global/lastoutput = 0
var/global/timedifference = 0

world
tick_lag = 0.1


New()
simulate_game()

proc/simulate_game()
var/counter = 0
while(looping)
for (var/i = 1 to 100)
for (var/j = 1 to 100)
for (var/k = 1 to 100)
counter = counter + 1
sleep(5)
increment_number()
timedifference = (world.timeofday-lastoutput) / 10
world << "The game loop finished in [timedifference] real seconds"
lastoutput = world.timeofday


proc/increment_number()
number = number + 1

client
Stat()
if(statpanel("Static"))
stat("Value","Number")
sleep(1)

if(statpanel("Small"))
stat("Value","Number")
stat("[name]","[number]")
stat("[name]","[number]")
stat("[name]","[number]")
sleep(1)

if(statpanel("Large"))
stat(name, number)
var/counter2
for (var/l = 1 to 100)
counter2 = counter2 + 1
stat(name, counter2)
stat(null,"")
stat("The quick brown fox", "jumped over the lazy dog [number] times")
stat(null,"")
stat("The quick brown fox", "jumped over the sleepy dog [number + 2] times")
stat(null,"")
stat("The quick brown fox", "flew over the sleepy dog [number / 2] times")
stat(null,"")
stat("The quick brown ghost", "flew over the sleepy dog [number ^ 2] times")
stat(null,"")
stat("The quick brown ghost", "flew through the sleepy dog [number + number] times")
stat(null,"")
stat("The quick friendly ghost", "flew through the sleepy dog [1 - number] times")
stat(null,"")
stat("The mischevious friendly ghost", "flew through the sleepy dog [(1 - number)^2] times")
stat(null,"")
stat("The mischevious friendly ghost", "flew through the sleepy town [(1 + number)^0.5] times")
stat(null,"")
stat("A mischevious friendly ghost", "flew through the sleepy town [number - 6] times")
stat(null,"")
stat("A mischevious friendly ghost", "flew through my sleepy town [number/8 + 100] times")
stat(null,"")
stat("A mischevious friendly ghost", "flew through the sleepy town [number - 6] times")
stat(null,"")
stat("The mischevious friendly ghost", "flew through the sleepy town [(1 + number)^0.5] times")
stat(null,"")
stat("The mischevious friendly ghost", "flew through the sleepy dog [(1 - number)^2] times")
stat(null,"")
stat("The quick friendly ghost", "flew through the sleepy dog [1 - number] times")
stat(null,"")
stat("The quick brown ghost", "flew through the sleepy dog [number + number] times")
stat(null,"")
stat("The quick brown ghost", "flew over the sleepy dog [number ^ 2] times")
stat(null,"")
stat("The quick brown fox", "flew over the sleepy dog [number / 2] times")
stat(null,"")
stat("The quick brown fox", "jumped over the sleepy dog [number + 2] times")
stat(null,"")
stat("The quick brown fox", "jumped over the lazy dog [number] times")
sleep(1)


Expected Results:
I would expect that any lag caused by the stat() command is proportionate to the delay that is displayed on the profiler.

Actual Results:
With the static panel open (which should be relatively performant), I got the following results profiled:

client/stat():
- Self CPU: 0.000
- Total CPU: 0.000
- Real time: 11.812
- Real time (avg): 0.117
- Calls: 101

/increment_number()
- Calls: 33

- Game loop range: 0.5 - 0.6 seconds

With the large panel open (which should be bad for performance), I got the following results profiled:

client/stat():
- Self CPU: 0.000
- Total CPU: 0.000
- Real time: 12.222
- Real time (avg): 0.119
- Calls: 103

/increment_number()
- Calls: 33

- Game loop range: 0.7 - 0.8 seconds


Given that:
- Increment number calls 33 times in both.
- Each increment number call is taking 0.1 seconds longer (conservative)
- The total real time delay should be 3.3 seconds

However:
- The total real time delay accounted by the profiler is 0.4 seconds.
or
- Measured real time on the proc has increased 3%, but in-game lag has increased 16%

Hope someone can help!

Does the problem occur:
Every time? Or how often? Every time
In other games? At least one other
In other user accounts? Presumably
On other computers? Not sure

When does the problem NOT occur?
Not sure

Did the problem NOT occur in any earlier versions? If so, what was the last version that worked? (Visit http://www.byond.com/download/build to download old versions for testing.)
Not sure

Workarounds:
Don't know - is there some neat trick I can use to assign a performance metric to a stat panel & shine a light on how long some of the background processes are taking?
If the issue is just that there's some kind of panel render proc that isn't shown in the client profiler I'm happy for this to be closed.

(though advice on assigning a cost to stat() elements still welcome!)
Yes, I'm positive all of the issues you're seeing with stat() are client-side. Server performance would show up in either the profiler (for stat() calls) or in overall CPU and tick usage. The fact that you're seeing very little change there says this is a client problem.

I believe there's a bug report open for this, although client performance problems for the statpanel are incredibly hard to pin down. Among other problems, I'm not sure if there's a test case for this that doesn't involve huge world or client startup costs like SS13. I might have a test project with a ton of verbs somewhere, but if so I forget where. I think a huge list of verbs, possibly but not necessarily spanning multiple categories, is a major part of this and would have to be part of any test case.
i will bet you a cookie that it doesn't lag if you do stat(null, "[key]: [value]") instead of stat("[key"], "[value]")
Thank you for your responses!

I have updated my test project with a little skeleton called Jeff who runs around a compound:
https://github.com/MetricDuck/jeffsquest/

The new test project has 10 status tabs, for which I've listed the effect of displaying on game performance below. Game performance this time has been defined as the average time it took to complete a game loop over 30+ calls whilst moving jeff back and forth.

Static
- Displays a single string
- 0.51 seconds per loop.
- Baseline case

Medium
- Displays a whole bunch of counters. No scrollbar.
- 0.53 seconds per loop.

Large
- Displays a whole bunch of counters as before, only a few more than Medium. Has a small scrollbar.
- 0.65 seconds per loop.

Large Sleepy
- Same as Large, but updates 1/10th as often
- 0.53 seconds average per loop.

Large Moo
- Same as Large, but implements ShortCow's suggestion
- 0.55 seconds average per loop.
- This was a neat optimisation trick, thank you for the suggestion!

Space
- Displays only a single counter, but adds a bunch of empty lines above.
- 0.54 seconds average per loop.

Spaaace
- Same as Space, but adds even more empty lines to make a scroll bar.
- 0.66 seconds average per loop.

Dreaming of spaaaace
- Same as Spaaaace, but we update 1/100th as often.
- O.53 seconds average per loop
- Notable in that there were spikes for a few game loops every time the panel updated, then things dropped down to the baseline.

Spaaace reversed
- Same as Spaaaace case, but we keep counter at the top and just display a static value below the scrollbar.
- 0.70 seconds average per loop.

Spaaace static
- Same as nothing there 2 but only display static values.
- 0.55 seconds average per loop.
- Spikes when scrolling the status panel.

Note: I checked to see if widening the window to make the scroll bar disappear would help, it does not.

Summing up then it looks like my takeaway from all this is:
- Updating the status panel when it is large enough to need a scroll bar is incredibly costly.
- It's ok to have a long status panel if there nothing being updated or it's just static values, as long as you don't mind a little stutter when you scroll.
- stat(null, "[key]: [value]") is a very neat optimisation.

I guess that was a lot of words to write something that's probably common knowledge on these forums ðŸËœ„.
The original bug has been reidentified as: having a status panel large enough to need a scroll bar causes client performance issues.

Happy to open as a different issue for bookkeeping if needed

I didn't actually check to see the effect of verbs... I can look at that too