perf(net-five): reduce CloneManager per-frame overhead, stagger scene update culling, add clone creation budget#4065
Open
ShamZy9 wants to merge 1 commit into
Open
Conversation
ShamZy9
force-pushed
the
perf/onesync-clone-manager
branch
from
July 13, 2026 18:50
27e387f to
a4a0a49
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR: perf(net-five): reduce per-frame CloneManager overhead and clone creation hitches
Goal of this PR
Reduce client main-thread cost of the OneSync clone manager, which scales poorly with the number of synchronized entities (most noticeably peds). Three symptoms addressed:
CloneManagerLocal::WriteUpdates/Updatefor every tracked object, even when no data is sent.How is this PR achieving the goal
Scene update culling (
CloneManagerLocal::Update):fwSceneUpdatefor entities beyond 424m were all happening on the same 2 out of every 50 frames (frameCount % 50 < 2), causing a synchronized periodic spike. Re-adds are now staggered per object ID.removedFlagsis now astd::unordered_map(thefwSceneUpdate__AddToSceneUpdate/RemoveFromSceneUpdatetracking hooks run for every scene update list mutation in the game, so lookups are hot).game_sceneUpdateThrottling(default off): remote entities beyond 150m that are not on screen (checked via the existing_isSphereVisibleForLocalPlayerhelper, cached ~10 frames staggered) get scene updates 1 out of every 4 frames. Visible entities are never throttled, since skipping scene updates also skips network position blending and causes visible warping.CloneManagerLocal::WriteUpdatesper-object overhead:packetStubbuffer was allocated for every local object every frame; it is now only allocated when the object actually syncs (syncType != 0)._isSphereVisibleForLocalPlayer+_isSphereVisibleForAnyRemotePlayer, the latter iterating all remote physical players) ran per object per frame just to choose a 50ms vs 250ms sync latency; the result is now cached inExtendedCloneDataand refreshed every ~100-164ms, staggered by object ID.msec()(high_resolution_clock::now()) hoisted out of the per-object callback.m_extendedDatawas looked up by key up to 4 times per object per frame; now looked up once.Bookkeeping data structures:
m_savedEntityVecremoval waserase(std::remove(...))(O(n) per removal, O(n²) aggregate under OneSync population churn); now O(1) swap-and-pop via an index map. Note: this makesGetObjectList()ordering arbitrary instead of insertion-ordered; no current consumer relies on ordering.DestroyNetworkObjectiterated all 256m_netObjectslists; it now erases from the owner's list and list 31 directly (the only lists an object is ever inserted into), with a full-sweep fallback if not found.Clone creation budget (
HandleCloneCreate):onesync_maxCloneCreatesPerFrame(default 5, 0 = unlimited/previous behavior). Clone creations over the per-frame budget returnfalse, which routes them through the existingrecreateListNAck path — the server resends them on a later frame. This turns the single multi-second hitch when many entities enter scope simultaneously into a few frames of staggered creation. The deferral path is already exercised in the wild (it is the same path used when a player entity is not yet available, and forCanApplyToObjectfailures).This PR applies to the following area(s)
FiveM (OneSync client)
Successfully tested on
Game builds: 3258
Platforms: Windows
Tested on a local OneSync server with two clients and 150 networked wandering peds:
game_sceneUpdateThrottling falseandonesync_maxCloneCreatesPerFrame 0reproduce previous behavior.Checklist