RB
RB Studios
DocsScriptsSubscriptionsToolsBlog
RB
RB Studios

Premium FiveM scripts engineered for serious servers. Made by a tiny team that ships.

Product
  • Scripts
  • Subscriptions
  • Tools
  • Roadmap
  • Changelog
Developers
  • Docs
  • GitHub
Studio
  • Team
  • Blog
  • About
© 2026 RB Studios·Not affiliated with Rockstar Games or Cfx.re
All systems operational·build 1.0.0-rc.4
BlogPerformanceEngineering for a 0.02ms idle budget.
PerformanceFeb 27, 2026·3 min read

Engineering for a 0.02ms idle budget.

How rb-mechanic stays under 0.02ms idle on a 64-slot server — event batching, cache hierarchies, and the threads we never spin up.

MH
Mira Hoang
RB Studios

When we set the performance budget for rb-mechanic, the number we wrote on the whiteboard was 0.02ms idle, 0.4ms peak, on a 64-slot server. That number has shaped almost every architectural decision since. Here's how we hit it.

Why idle matters more than peak

Most FiveM resources measure themselves by their peak frame cost — the spike when a player opens a UI or finishes a job. Peak is easy to win: defer the work, batch it, or just shove it onto the server.

Idle is harder. Idle is what your script costs when nothing is happening. If you have a hundred resources on the server and each idles at 0.1ms, you've burned a tenth of your frame budget on absolutely nothing. The player feels it as input lag and stutter.

The single biggest performance win we've ever shipped wasn't a faster algorithm. It was deleting a Citizen.CreateThread loop that nobody had measured.

The three rules

We landed on three rules that everything else flows from:

  1. No threads that aren't doing work. Every long-running thread on the client must be measurably necessary. If you spin one up for "polling," you'd better have benchmarked the event-driven version first and proven it doesn't scale.
  2. Cache hierarchies, not cache misses. L1: in-memory on the client. L2: server-side per-player. L3: database. Most reads end at L1. The ones that don't are pre-fetched on player join, in a single batched query.
  3. Events have a budget too. Every server-to-client event we fire goes through a batcher. If two events would fire in the same frame, they get coalesced. This sounds obvious until you go look at how many resources fire a playerStateChanged event ten times per second per player.

What the profile actually looks like

Pulled from a 48-player server, sampled over five minutes of normal RP:

[rb-mechanic]
  client idle:        0.014 ms (avg)
  client peak:        0.31 ms  (UI open)
  server tick cost:   0.08 ms / second
  events / minute:    142 (87% batched)
  db queries / hour:  24 (all batched joins)

The peak is well under our budget. The idle is the number we're proudest of — and it's the one that took the most discipline to hold. Every release, the first thing the reviewer asks is: did this regress idle? If the answer is yes, the patch goes back, no matter how good the feature.

The threads we never spun up

The list of work we explicitly do not do on a background thread is longer than the list of work we do:

  • Vehicle proximity checks. Driven by OnEntityPlayerEntered instead. Free.
  • Job-state polling. Server pushes state changes. Client just listens.
  • Heading / coordinate tracking. We sample inside the existing EVENT_VEHICLE_MOVED callback. No new thread.
  • Phone/UI heartbeats. None. The UI lives in the NUI process and the server is the source of truth.

There's a reflex, especially in Lua, to reach for CreateThread the moment you need anything to happen "regularly." Resist it. Most of the time the thing you want is already happening — you just need to subscribe to it.

What this costs us

Honesty section: this approach is more work. The event-driven version of any feature is harder to write, harder to debug, and harder to onboard new engineers onto. We've held back at least two features this year because we couldn't figure out how to ship them inside the budget.

But this is the deal we made with the people running 64-slot servers. They don't have headroom to spare. A 0.02ms idle isn't a marketing number — it's the difference between their server feeling snappy and feeling miserable.

We'd rather ship slower than ship something that makes their players' frames worse.

Thanks for reading. Catch us in Discord if you want to talk shop.
All posts
More from the studio

Keep reading

All posts

Ship log — March 2026.

Six releases, two new scripts, and the first public look at rb-housing. Plus the bug we held back from v1.4.2 and why.

RS
RB Studios
Apr 02, 2026

Obfuscation isn't security — and what we do instead.

We get the question every release: why aren't your scripts open source? Here's how we actually protect server stability without leaking attack surface.

RB
Reid Bennett
Mar 18, 2026

Migrating from QBCore to QBox without downtime.

A field guide to the upgrade path: schema diffs, the bridge layer, and the four edge cases that will bite you on go-live night.

RB
Reid Bennett
Feb 09, 2026