How to figure out where high server CPU usage is coming from

Roblox recently gave me this warning for high cpu usage in my games (for the server):


I checked the game’s performance stats and even the developer console (although maybe I’m not looking in the right place) and I couldn’t find the issue or even what a server’s CPU usage even was.

This is in the scripting support section since I might have something wrong with my scripts.

Can someone help me know where I can get more information about my game, code, and the servers so I can fix this issue easier?

Two separate questions tangled together here: what is expensive, and which script is doing it. Different tools for each, and it’s worth doing them in that order.

Which script — Script Profiler. This is the one you actually want first. In Studio it’s under the View tab → Script Profiler. Start a recording, let the place run 30-60 seconds under something like realistic load, stop it. You get a per-function breakdown of CPU time grouped by script, so instead of guessing you get a ranked list with the worst offender at the top.

Important: run it against a server, not Play Solo’s client. Use Test → Start Server (with a couple of players if you can), because server CPU and client CPU are completely separate problems and the warning you got was about the server.

What is expensive — MicroProfiler. Ctrl+F6 in Studio. It’s a frame-by-frame flame graph of every job the scheduler runs, not just Luau. This is how you find out the cost isn’t your code at all but physics or replication — which is a very common outcome and one the Script Profiler alone will never show you.

You can make it dramatically more readable by labelling your own code:

local function updateEnemies()
	debug.profilebegin("updateEnemies")
	-- ... the work ...
	debug.profileend()
end

Anything wrapped like that appears as a named bar in the graph. Put it around the three or four loops you suspect and the answer usually falls out of a single recording.

On a live server, press F9 for the Developer Console and switch to the server view. The script listing there gives you rough per-script activity, and it’s the quickest way to spot one script sitting at a far higher number than everything else — useful when the problem only appears with 20 real players and won’t reproduce in Studio.

Things that usually turn out to be the cause, roughly in order of how often they come up:

  • A RunService.Heartbeat connection iterating a table that has quietly grown. 200 NPCs re-evaluated every frame is about 12,000 iterations a second, and it looks completely innocent in the source.
  • while true do ... end with no task.wait() in the body — or with one that an early continue/branch skips.
  • workspace:GetDescendants() called inside a loop. It costs O(everything in your game) and it gets called per-frame surprisingly often.
  • PathfindingService — a Path:ComputeAsync() per NPC per retarget with no throttling will flatten a server long before the movement code does.
  • Unanchored parts. Physics cost never appears in your scripts, which is exactly why MicroProfiler matters: if the big bar is physics or stepping, no amount of script optimisation is going to move it.

One last thing — once the profiler names a script, confirm it by disabling that single script and re-checking. A profiler tells you where the time went; disabling it tells you it was actually the cause. Those are not the same claim, and chasing the wrong one costs a lot of hours.

If you paste the top few rows of the Script Profiler output here, someone can probably tell you what to change from that alone.

1 Like