ID:2387525
 
Code:
    Make_Empty_Mobs()
set category = "Admin Other"
if(!usr.client.holder)
alert("You cannot perform this action. You must be of a higher administrative rank!")
return
if(!usr.client.holder||usr.client.holder.level<5) return
var/amount = input("How many clientless player mobs do you want to create?") as num
if(!amount) return

log_errors("\n\n### CREATING [amount] TEST PLAYER MOBS AT: [time2text(world.realtime,"Day DD hh:mm")] ###\n\n")
src << "Creating [amount] player mobs"

for(var/i, i<amount, i++)
var/mob/player/M = new/mob/player
M.loc = locate(usr.x+i,usr.y,usr.z)
M.icon = usr.icon
M.TestChar = 1
spawn M.Update_Player()
M.name = "TESTCHAR #[i]"
Players += M.name


src << "DONE! Created [amount] player mobs"
log_errors("\n\n### DONE CREATING [amount] TEST PLAYER MOBS AT: [time2text(world.realtime,"Day DD hh:mm")] ###\n\n")


Problem description:

So, I currently have the above code. It works, it creates an empty mob, and that's it.

I currently have a lot of front end lag due to some of the in game tabs, and it seems to only occur when above a certain player count (70+).

I want to be able to test fixes without using my playerbase as the guinea pigs, and want to alter the above code to essentially mimic a player. (Have tabs, a client, use CPU like a player, etc. etc.)

Is this possible? And if so, how do I do it?

No, it isn't possible. Without a connected client, the internal processes that trigger statpanel processing and such will not occur. Also without a good picture of a typical player's usage pattern, you have no way of mimicking their CPU usage.

What will serve you best is profiling your game for an extended time (like anywhere from 5-15 minutes) at different levels of user load. That will show you things like which procs take the longest, which are called the most, etc. In general, the more total "self" time a proc has, the more you want to target it for optimization. I would also look at procs that are called the most frequently regardless of their time, to see if there are ways to improve those or if you can avoid calling them in some cases. (Any proc call comes with some startup and teardown overhead that is unavoidable.)

Profiling at different levels of user load lets you compare these profiles--comparing averages will be much easier than comparing totals--to see what's different. The most useful piece of info will probably be the call count over the time period you profiled. If both profiles take exactly 5 minutes (or close enough), then if DoThings() is called 800 times with 20 players and 2800 times with 70 players, you know that it scales about linearly with the number of players. Any time something scales beyond linear, you want to pay it extra special attention and either avoid calling it or make it as fast as you can.
I've done the debug profile, but not in the exact method you described. I will give it a shot.

Essentially, the top thing in my debug profiler is the Status tab which has a player stats, and it does have a lot of calls.

I did debug profile for about 5min (I believe)

And in average, the mob/player/Stat had 1555 calls, but with relatively no Self/Total CPU.

That profile doesn't show a heavy use pattern. Stat() is very benign, although if you can do anything at all to improve its efficiency you'll see some gain.

The most interesting procs to me are mob/Save(), and mob/Status(). The Status() proc has a relatively high time per call, so that's a good optimization target. Yes it only had two calls, but those two calls used up a lot of time. And that's just self time; total time was higher. Update_Player() also used a lot of self and total time; I'm assuming it's likely that's being called by Status(). While you should optimize Status() for its self time, if it's calling Update_Player that's a target too. In the case of Update_Player(), it looks like most of its time is spent in callees, so I'd look at those.

For Save(), that had 99 calls; even if you were profiling for 5 minutes, that seems wildly excessive even on a busy server. So reducing the number of saves is a good idea. Even if you have an autosave feature, this is just way too aggressive.

Other things that pop out as possibilities are obj/items/Transporter_Watch/Transport(), mob/Del(), and mob/Contacts(). The del case is of special concern because even that small value seems relatively high for a proc that seldom has anything to do.

But as I said, this profile does not look like a heavy usage pattern. I'm curious how heavy usage distorts these figures. However it's worth your time to look at the aforementioned targets even now and see if you can eke anything out of them. If you post them here I'll be happy to go over possibilities for streamlining. Optimization is a fun topic and the discussion will be useful for others too, I think.
We did some adjustments to the Save Code, and also took a resource heavy function from being a Tab and made it a popup box that only runs when opened.

This is the current Averages after the server has been up for 12+ hours. Lag seems a lot better, game is playable, but still have small lag spikes scattered about.

https://gyazo.com/b7cf5940c029157409387944e8b1930f

Please ignore the Destroy Planet function, as it is a verb that probably shouldn't even exist due to the amount of stress it causes in game
find_savableObjects() appears to be a hog as well, along with SaveTurfs() and SaveItems(). Those procs are all doing a lot of self work, so they're huge optimization targets. They appear to all be connected to SaveWorld(), which has a rather shocking total time. I'm convinced you can eke better performance out of your save process.

Upgrade() has a rather high self time as well.

mob/Save() is being called a lot, and while it doesn't use a ton of time you should consider looking at it. The number of calls here is simply way over the top for what I would consider reasonable even on a busy server. Also it would be best if you found a way to call it less frequently. mob/Write() also could use some attention to see if anything there is trimmable, but if you're saving mobs less often it becomes less of a concern.