← Back to Tutorials
Advanced~17 min readUE 5.5 / 5.6

Networking & Replication Performance

Push model alone takes a 4-client server's NetBroadcastTickTime from 167 ms to 76 ms (>2× speedup). Iris at 100 players takes NetBroadcastTickTime from 66 ms to 45 ms (~31% server FPS uplift). Replication Graph runs Fortnite. This tutorial walks the full optimization ladder: dormancy → push model → conditional rep → Replication Graph → Iris.

1

The authority cost model

Server frame budget at 30 Hz: 33.3 ms. NetBroadcastTickTime can take more than half on a busy server. The senior framing: "the frame is for replication." Everything else competes for what's left.

Reference: Fortnite Battle Royale ships 100 connected players with ~50,000 replicated actors per match. That's the canonical scale that motivated Replication Graph.

2

Property comparison — the default bottleneck

Naive DOREPLIFETIME(MyClass, MyVar) re-checks every replicated property every frame on every connection. With dozens of properties on hundreds of actors with dozens of clients, the comparison cost dominates. Push model is the fix.

3

Push model (5.1+)

DefaultEngine.ini + Target.cs
[/Script/Engine.GameNetworkManager]
[SystemSettings]
net.IsPushModelEnabled=1

// In *.Target.cs — required for shipping cooks
bWithPushModel = true;
Per-property opt-in
UPROPERTY(ReplicatedUsing=OnRep_Health, meta=(IsPushBased=true))
float Health;

// When mutating:
MARK_PROPERTY_DIRTY_FROM_NAME(AMyClass, Health, this);

Skips dirty-check polling on properties marked push-model. Community-measured 4-client test: NetBroadcastTickTime 167.4 ms → 76.5 ms (>2× speedup) just from flipping push model on.

bWithPushModel=true in *.Target.cs is mandatory for shipping cooks — the runtime CVar alone does nothing in packaged builds.

4

Conditional replication

DOREPLIFETIME_CONDITION with ELifetimeCondition values:

  • COND_None=0 — default; replicates to all.
  • COND_InitialOnly=1 — replicate once, on relevancy.
  • COND_OwnerOnly=2 — only the owning client.
  • COND_SkipOwner=3 — everyone except owner (mirror corrections).
  • COND_SimulatedOnly=4 — only simulated proxies.
  • COND_AutonomousOnly=5 — only the autonomous proxy.
  • COND_SimulatedOrPhysics=6, COND_InitialOrOwner=7, COND_Custom=8.

For replicated subobjects: net.SubObjects.DefaultUseSubObjectReplicationList=1 opts new actors into the SubObjectList path (required for Iris; cheaper than overriding ReplicateSubobjects()).

5

Dormancy as a first-class optimization

Per Epic: dormancy "potentially saves multiple milliseconds of server CPU time per frame." See the Dedicated Server tutorial for the full dormancy workflow. Highlights:

  • DORM_DormantAll for static interactables.
  • DORM_Initial for spawn-once objects (skips even initial property send to newly relevant connections).
  • Always FlushNetDormancy() before mutating replicated props on dormant actors.
  • Blueprint-replicated properties bypass dormancy — actor stays DORM_Initial but BP variables still send (per Vorixo).
6

NetCullDistanceSquared & relevancy

Default NetCullDistanceSquared = 225,000,000 cm²15,000 cm (150 m) relevancy radius. Lower for actors that don't matter at distance — pickups (5,000 cm), debris (3,000 cm), background NPCs (10,000 cm).

NetUpdateFrequency default is 100 Hz on AActor / 100 on Pawn / 5–10 on most placed actors. net.UseAdaptiveNetUpdateFrequency=1 oscillates between NetUpdateFrequency and MinNetUpdateFrequency=2 based on whether properties actually changed.

7

Fast Array Serializer

Raw UPROPERTY(Replicated) TArray<FFoo> serializes the entire array on any change. FFastArraySerializer + FFastArraySerializerItem serializes only changed elements with client-side Add/Remove/Change callbacks.

Fast Array pattern
USTRUCT()
struct FInventoryItem : public FFastArraySerializerItem
{
    GENERATED_BODY()
    UPROPERTY() FString ItemId;
    UPROPERTY() int32 Count;

    void PostReplicatedAdd(const struct FInventoryArray& InArray);
    void PostReplicatedChange(const struct FInventoryArray& InArray);
};

USTRUCT()
struct FInventoryArray : public FFastArraySerializer
{
    GENERATED_BODY()
    UPROPERTY() TArray<FInventoryItem> Items;
};

FastArrays automatically split across the 64 KB net.MaxConstructedPartialBunchSizeBytes bunch limit; raw TArray doesn't — oversize TArrays just fail.

8

Replication Graph (Fortnite-scale)

Replication Graph replaces the default per-actor relevancy check with a spatial 2D grid (UReplicationGraphNode_GridSpatialization2D) plus AlwaysRelevantNode and ConnectionGraphNode. Mature, Fortnite-proven, the right call for any title with 50+ players on UE 5.0–5.4.

Enable: derive a UReplicationGraph subclass and set ReplicationDriverClassName in DefaultEngine.ini. Debug with Net.RepGraph.PrintGraph (provided by AReplicationGraphDebugActor).

Reference: Epic's Replication Graph Overview tech blog.

9

Iris (5.5 experimental, 5.6+)

Iris replaces the legacy NetDriver replication path with NetTokens + ReplicationFragments + a different bandwidth model.

DefaultEngine.ini
[SystemSettings]
net.Iris.UseIrisReplication=1
net.Iris.PushModelMode=1
net.SubObjects.DefaultUseSubObjectReplicationList=1

BorMor's published 100-player benchmarks (per Iris: 100 Players in One Place): FEngineLoop::Tick 83.9 ms → 60.1 ms (−28.3%); NetBroadcastTickTime 66.2 ms → 45.8 ms (−30%); server FPS 10–15 → 15–20.

Iris buffer growth is monotonic Once peak replicated-object count blows the buffer, it stays expanded for the lifetime of the server process. Spikes during boss fights become permanent RAM cost.
10

RPC budgets & saturation

Reliable RPCs on tick overflow the per-channel reliable buffer and disconnect clients. Don't.

Multicast RPCs ignore relevancy for join-in-progress and culled clients — despite intuition, multicast does not equal "everyone."

The IsNetReady queue-clog — per Steve Streeting's May 2025 writeup, IsNetReady() aborts an entire replication tick when QueuedBits + SendBuffer.GetNumBits() > 0, producing 45–60 second "replication freeze" on level streams. Gate RPCs on Conn->IsNetReady(false).

Bandwidth ceilings: defaults ConfiguredInternetSpeed=100000, MaxDynamicBandwidth=40000. Streeting raised both to 200000/40000 respectively to cut RPC-induced replication stalls by ~50%.

Profiling workflow

  • stat net — overall NetDriver costs (NetTickTime, NetBroadcastTickTime).
  • NetworkProfiler enable or -networkprofiler=true — captures .nprof for the standalone NetworkProfiler tool.
  • ShowDebug Net / ShowDebug NetDormancy — on-screen overlay.
  • net.Debug.ActorClassNameTypeCSV 1 (5.6+) — per-class CSV stats for ForceNetUpdate / FlushNetDormancy.
  • Networking Insights (-trace=net,frame) — UE 5.5/5.6 visualizes per-connection replication frames.

PerfGuard "Replication Budget" scenario template captures baseline NetBroadcastTickTime, NetTickTime, Replication.Properties CSV stats, per-class RPC byte counts, FlushNetDormancy rate from a 30-second multi-client soak. Regression rules: NetBroadcastTickTime >8 ms (yellow) / >16 ms (red) on 60 Hz; >5% growth in replicated properties/frame; >20% bytes/sec for any single class — surfaces accidental UPROPERTY(Replicated) additions or missing MARK_PROPERTY_DIRTY_FROM_NAME.