Performance
The architectural guarantees behind WSRF cost, the measured registration, reset and persistence numbers, and how to profile your own project.
Architectural guarantees
These are properties of the implementation, not tuning advice:
- No permanent Actor Tick. Neither the state component nor the subsystem enables tick.
- No per-frame world scan. Registration captures once; resets visit indexed records.
- Indexed lookup. Records are keyed by Stable ID; regions have their own index.
- Weak Actor references. Records hold weak pointers and pure data, never strong
AActorreferences. - Minimal snapshots. The automatic snapshot is a bounded, explicitly listed set of properties.
- Bounded reset queue. Nested reset requests are FIFO and limited to 64 executions per drain — a safety guard against a completion callback that requests another reset forever, not a throughput target.
- No editor tick. The Inspector scans only on explicit refresh or validation.
A region reset therefore costs roughly what the matching records cost, not what the world contains.
Measured numbers
All figures below were measured on the development machine — AMD Ryzen 9 9950X, UE 5.7 Development Editor, NullRHI — using editor automation. They are measurements, not shipping-platform guarantees; times vary by machine and Actor complexity.
Capture, restore and persistence, by Actor profile
Every row uses real PIE Actors, real registration and capture, a real USaveGame, and a real
SaveGameToMemory / LoadGameFromMemory round trip.
| Profile | Count | Capture | Restore | WSRF export | Serialize | Deserialize | Binary bytes |
|---|---|---|---|---|---|---|---|
Bare AActor | 1,000 | 0.089 ms | 0.029 ms | 2.243 ms | 35.183 ms | 42.158 ms | 5,964,282 |
AStaticMeshActor, two materials | 1,000 | 0.934 ms | 0.326 ms | 3.348 ms | 73.934 ms | 90.426 ms | 11,877,282 |
| Typical enemy Character: capsule, skeletal mesh, one material, AnimBP | 1,000 | 2.389 ms | 0.784 ms | 4.534 ms | 105.653 ms | 128.034 ms | 17,132,282 |
| Full acceptance Character, all seven component families | 1,000 | 6.643 ms | 5.831 ms | 9.631 ms | 211.146 ms | 251.105 ms | 33,319,282 |
| Full acceptance Character, all seven component families | 10,000 | 78.394 ms | 72.199 ms | 122.363 ms | 2,291.327 ms | 2,569.992 ms | 333,172,282 |
The full acceptance rows are a deliberate worst case
That profile carries every component family the framework captures at once — skeletal mesh, capsule, child actor, audio, Niagara, static mesh and scene components. The typical-enemy row — capsule, skeletal mesh, one material and an Animation Blueprint, no Child Actor, Audio or Niagara — is the representative comparison for ordinary skeletal enemies. Read the full rows as a ceiling, not an average.
Export is no longer measured by default
The WSRF export column uses normal capture with serialized-size measurement disabled. Before this optimization, the four comparable profiles measured 37.862 ms, 75.671 ms, 221.428 ms and 2,247.548 ms — removing the unconditional measurement reduced export time by 94.08% to 95.65%. Enable Measure Serialized Size only for profiling; explicit measurement stays within 0.04% of the real SaveGame bytes.
Persistence
Uncompressed on purpose
These are raw, uncompressed tagged Unreal SaveGame payloads, so the numbers reflect the data itself rather than a codec. Compression measured on the full fixtures reduced payload size by roughly 75–85%, but WSRF does not impose a proprietary compression format — applying your own compression to the stored SaveGame is expected.
The tagged SaveGame archive repeats property names and type metadata, so its file-size cost is much larger than the in-memory typed component payload. Version and section-presence metadata also increase the simple-Actor record compared with an unversioned format; that cost is paid to distinguish legacy-absent sections from intentionally empty sections safely.
The practical conclusion: capture persistence at an explicit save boundary — a bonfire, a menu save, a checkpoint — not on a frame-critical path. Applying a snapshot is far cheaper than producing one, because import resolves records through indexed effective identities rather than scanning the world; compare the Restore and WSRF export columns in the table above.
Profiling your own project
| Function | What it tells you |
|---|---|
GetApproximateSnapshotMemoryBytes | Estimated serialized snapshot footprint of the captured records — the 1.x name is retained for compatibility |
GetManifestMemoryBytes | World Manifest memory |
GetApproximatePersistentSnapshotMemoryBytes | Estimated serialized footprint of a persistent snapshot — the 1.x name is retained for compatibility |
GetApproximatePersistentSnapshotSerializedBytes | Explicit tagged-serialization measurement — at most 0.04% drift from the real binary across the measured profiles |
GetRegisteredActorCount | How many records exist |
FWSRFResetReport::DurationMilliseconds | Real cost of a reset, per operation |
The reset report is the most useful of these during development: log its duration and counts on every reset and you get a per-checkpoint cost profile without any extra instrumentation.
The World State Inspector surfaces snapshot memory, manifest memory, refresh and validation timings, and the last reset report in one panel.
What drives cost
| Factor | Effect |
|---|---|
| Number of managed Actors in the reset region | Dominant factor for reset cost |
| Number and type of components per Actor | Snapshot size and restore work — see Automatic component capture |
| Custom State entry count | Payload size and restore work |
| Exported record count | Persistence export cost and SaveGame size |
| Region granularity | Smaller regions mean smaller resets |
Region design is the main lever. A single region covering an entire level makes every checkpoint pay for the whole map; regions matching the encounter or the room keep resets proportional to what the player actually changed.
Scale guidance
| Scale | Expectation |
|---|---|
| A room: tens of managed Actors | Reset cost is negligible; use whatever region granularity is convenient |
| A level: hundreds of managed Actors | Split into regions per area or encounter |
| A large streamed world: thousands of records | Rely on the manifest and Pending Restore; do not force cells to load |
| Persistence at 10,000 records | Export itself is about 122 ms even on the worst-case profile; the SaveGame serialization round trip dominates (about 2.3–2.6 s and 333 MB uncompressed on that same worst-case fixture) — save-boundary work only |
Measure in your own project
Actor complexity, component counts, platform and build configuration all move these numbers. Use Unreal Insights together with the reset report and the memory functions above rather than assuming the figures on this page transfer to your content.
Last updated: 2026-09-04