Client Task Scheduler fails to parallelize sequential Lua execution, causing persistent single-thread dependency stalls

System Specifications:
Device: MSI Thin GF63 Laptop

Intel Core i5-12450H (Validated: 200W/32W power limit tests confirm consistent bottleneck regardless of power availability; zero thermal/power throttling observed).
GPU: NVIDIA GeForce RTX 3050 Laptop GPU
Memory: 16GB Dual-Channel RAM
OS: Windows 10

Description of the Issue:
The Roblox client experiences chronic frametime instability, as shown by high-frequency micro-stutters. MicroProfiler analysis proves this is a pure CPU-bound Task Scheduler bottleneck. The GPU consistently reports < 1ms rendering times, confirming the hardware is not the limit. The issue stems from a structural failure in the engine’s Task Scheduler: sequential Lua execution workloads (including Knit frameworks, RunService.Heartbeat, RenderStepped, and native Roblox CoreScripts) are serialized and pinned to a single background worker thread (e.g., RBX Worker A or Worker C). This core reaches its full computational saturation and spills past the frame budget, forcing the RBX Main thread into a synchronous dependency stall (the timeline shows RBX Main as empty while waiting for the saturated worker to return).

Why i believe this is the case:
Thread Dependency Stalls: Heavy sequential workloads consistently pin a single worker core while peripheral cores (Worker J, L, F, G) remain entirely unutilized.

Control Test Validation Testing on an empty baseplate (zero user scripts) targeting 120 FPS 8.32 ms budget reproduces the identical saturation issue using native CoreScripts (PlayerList, SettingsHub, ExperienceChat). This confirms the bottleneck is intrinsic to the engine’s Task Scheduler.

Power-Limit Invariance Testing with elevated power limits 200 W produces no change in frametime behavior, confirming the issue is architectural and not a result of thermal or power throttling.

Frame-Budget Sensitivity: Performance “stutter” scales linearly with frame-budget constraints; toggling between 60 FPS 16.6 ms budget and 120 FPS 8.32 ms budget demonstrates that the single-threaded execution limit is the primary cause of the instability

Reproduction Steps:

  1. Enable the MicroProfiler during high intensity frame periods.,

  2. Monitor thread workload distribution; observe a single pinned worker core vs. idle peripheral cores.,

  3. Observe the RBX Main timeline during saturated execution; note the synchronous stall (empty timeline).,

  4. Toggle target framerate 60 vs 120FPS to observe direct correlation between budget constraints and frametime spikes.

Attached Files:
Jailbreak clip (Place ID: 606849621) - Watch Jailbreak | Streamable
Jujutsu Shenanigans (Place ID: 9391468976) Watch ROBLOX_9593df57-f085-4412-b345-2428ab04678f | Streamable
Baseplate clip - Watch ROBLOX_649cf32d-6c82-4c97-a068-d867e631d4cd | Streamable

Example:

Expected behavior

What should be expected behaviour:

The engine’s Task Scheduler should dynamically distribute non-interdependent client-side sequential frame operations across available multi-core worker threads to prevent synchronous dependency stalls and optimize CPU utilization.

A private message is associated with this bug report

1 Like

Is there any roblox staff available to investigate this issue? Frametimes are horrible on my device despite my hardware being completely optimal. Please verify if this issue exists using your telemetry.

1 Like

Turning off Hyper Threading slightly improved frametimes but this seems to be a scheduler problem with the engine. I still get horrible performance.

I think this has to do with the E Core and P core that my CPU Architecture uses the “big.LITTLE” design. 4 P (Performance Cores) and 4 E (Efficiency Cores). The problem is the scheduler doesn’t know where to throw the big tasks to the correct thread that would handle all the complex big chunked jobs. Please have your team investigate using telemetry to investigate all other cpu architecture similar to mine.

I also tried using project lasso to force Roblox to only use my P core threads and it only made the problem worse by making the P core threads more congested.

Since im on Windows 10 i dont have thread director but that doesnt matter anyway since the engine scheduler wouldnt be able to figure out where to throw all the heavy jobs on the correct thread.

1 Like

Update:

Updating to windows 11 has improved the frametime significantly but i am still getting severe stutter every few seconds.




You will notice in all of these screenshots (Except the second one) that Replicator Process Packets are repeated in a single thread and the other threads remain unutilized. As they wait for the thread to finish it stalls.

This repeats continuously. Please investigate this using the latest dump below.
Latest Dump.zip (3.3 MB)

This bug was reported in Opportunistic Streaming conflicts with Ugc packets, causing performance degradation - #4 by dear_gee and is yet to be properly addressed by roblox.

Hello,

Thanks for the detailed debugging info!

I took a look, and there’s a few things going on here:

  1. Most of your screenshots show a significant amount of replicator traffic (the ones with all of the processPacket scopes). This indicates that there’s either a network issue, or an experience-level issue such as e.g. too many RemoteEvent instances being spawned in a short amount of time. We’ve asked our networking team to take a look, but I doubt this is an engine issue.
  2. In a couple cases, there are very long Script scopes - these are typically related to the code in an experience, and not the engine itself (i.e. the experience’s Lua code is doing too much in each frame). In particular, there’s one screenshot where it shows that the experience scripts are calling GetDescendants an excessive amount of times.
  3. Due to the high numbers of small scopes (the tiny little slivers of time under e.g. processPacket or $SCRIPT), note that having the MicroProfiler active will result in a significant performance penalty. Closing the MicroProfiler will improve performance in these games.

On engine parallelism: the design of our engine relies upon exclusive locking of the DataModel for most tasks. This is why you see low parallelism in most cases. We rely on individual jobs to do their own parallelism internally (e.g. Physics does a good job at this). Luau is not parallelizable unless you are specifically using Parallel Luau, because non-parallel Lua scripts always have exclusive access to the entire DataModel.

Let me know if all of that makes sense. Thanks!