A reproducible benchmark comparing AssetBundles and Content Directories — Unity 6.6's new content build & load system (the low-level backend under Addressables 3) — built on top of Unity's official URP 3D Sample (Terminal / Garden / Oasis / Cockpit).
This project accompanies the Unite Seoul 2026 session on Content Directories. The numbers on the session slides that reference this project were measured with the scripts in this repo, and everything needed to reproduce them is included.
Same project, same content, same measurement code — only the Addressables backend differs
(BundledAssetGroupSchema vs ContentDirectoryGroupSchema).
| AssetBundles | Content Directories | Δ | |
|---|---|---|---|
| Content rebuild after changing one asset | 57.4 s | 17.1 s | −70% |
| Memory left unreturned after a scene tour | 1,522 MB | 258 MB | −83% |
| Total scene load time (3-scene tour) | 3,317 ms | 1,714 ms | −48% |
Measured on Unity 6000.6.0b3 (beta) + Addressables 3.1.0 on macOS — n=5 for loading and memory, n=3 for the incremental build. Definitions, full tables, and caveats below.
- Unity 6000.6.0b3 (beta) — newer 6.6 betas should also work
- Addressables 3.1.0, Memory Profiler 1.1.12, URP 17.6
- Benchmarks were run on macOS (Apple M3 Pro); the harness targets StandaloneOSX
Environment assets are grouped by type — Bench-Textures (314 entries) / Bench-Models
(234) / Bench-Materials (154) / Bench-Prefabs (257) — each packing into a single
AssetBundle in the AB variant. This is the classic type-sliced layout in which all
environments share bundles. The three environment scenes (Garden / Oasis / Cockpit) are
addressable; Terminal stays in Build Settings as the boot scene.
| Metric | How |
|---|---|
| Content build time | Wall-clock around AddressableAssetSettings.BuildPlayerContent: no-change rebuild and incremental rebuild (one material float changed to a never-before-built value each run) |
| Scene load time | Addressables.LoadSceneAsync (additive), request → activation, per environment scene |
| Memory | Scene tour (below): baseline / peak / residual / after-UnloadUnusedAssets, sampled via Profiler.GetTotalAllocatedMemoryLong, GetTotalReservedMemoryLong, and the "System Used Memory" profiler counter. No Unity CPU profiler involved |
The runner (Assets/CDBench/Runtime/CDBenchRunner.cs):
- Boots into Terminal and loads three small "preview" assets (one per type group), holding them for the whole run — emulating a hub that keeps previews/caches pointing into shared content.
- Tours Garden → Oasis → Cockpit: additively loads each scene through Addressables (timed), lets it settle, samples memory, unloads it.
- Back at Terminal-only, samples residual memory, then samples once more after an explicit
Resources.UnloadUnusedAssetsfor reference.
Why this shows the backend difference: with AssetBundles the retained previews keep the
shared type bundles alive, and bundles unload all-or-nothing — so assets belonging to
environments you already left cannot be returned. Content Directories track content per file
and release files whose reference count reaches zero, without a global
UnloadUnusedAssets sweep.
Measured 2026-07-16 (environment above); n=5 unless noted, ranges are min–max.
| metric (MB) | AssetBundles | Content Directories | Δ |
|---|---|---|---|
| unreturned after tour (residual − baseline) | 1,522 (1,520–1,526) | 258 (237–270) | −83% |
| residual allocated back at Terminal | 2,057 | 801 | −61% |
| peak allocated during tour | 2,138 | 1,131 | −47% |
| residual "System Used Memory" | 3,970 | 1,974 | −50% |
| baseline allocated (sanity check) | 534 | 543 | +2% |
After the tour, the AssetBundles variant is still holding ~1.5 GB of assets from environments
that were fully unloaded. An explicit Resources.UnloadUnusedAssets sometimes reclaimed them
and sometimes did not (632–2,058 MB across runs); the Content Directories variant had nothing
left for it to reclaim (801 → 802 MB) — the files were already released when their reference
count hit zero.
| scene | AssetBundles (ms) | Content Directories (ms) | Δ |
|---|---|---|---|
| Garden | 1,652 (1,635–1,670) | 662 (644–675) | −60% |
| Oasis | 1,110 (1,104–1,124) | 793 (732–834) | −29% |
| Cockpit | 554 (545–565) | 259 (249–271) | −53% |
| tour total | 3,317 | 1,714 | −48% |
The per-scene spread (−29% to −60%) reflects how much of each scene's load cost is content I/O versus backend-independent work such as activation.
| scenario | AssetBundles (s) | Content Directories (s) | Δ |
|---|---|---|---|
| one material changed (n=3) | 57.4 (57.1–57.8) | 17.1 (17.1–17.3) | −70% |
| no-change rebuild (n=1) | 12.1 | 17.3 | +43% |
Changing a single material makes the AB variant rebuild the affected shared bundle(s); the CD variant rebuilds one content file and reuses the cache for everything else — its incremental time equals its no-change time (a flat pipeline cost of ~17 s at this project size). Cold (first-ever) builds are dominated by shader compilation on both backends and are not part of this comparison.
./bench.sh setup # one-time: create groups, make scenes addressable, configure builder
./bench.sh ab # switch Bench-* groups to AssetBundles (BundledAssetGroupSchema)
./bench.sh cd # switch Bench-* groups to Content Directories (ContentDirectoryGroupSchema)
./bench.sh content <label> <AB|CD> # timed content build -> bench-results/build-times.csv
./bench.sh player <AB|CD> # player build -> Builds/<variant>/
./bench.sh smoke <AB|CD> # single verified scene-tour run
./bench.sh tours <AB|CD> 5 # N tour runs -> bench-results/tour.csv
python3 aggregate.py # markdown summary of bench-results/Override the editor path with UNITY=/path/to/Unity ./bench.sh .... The first content build
compiles the full URP shader variant space and can take ~50 minutes; later builds reuse the
shader cache. Every phase runs under a hard timeout and verifies its CSV output, so failures
surface immediately instead of hanging.
Assets/CDBench/Editor/CDBenchSetup.cs— group setup and AB↔CD switching (-executeMethod)Assets/CDBench/Editor/CDBenchBuild.cs— timed content/player buildsAssets/CDBench/Runtime/CDBenchRunner.cs— scene-tour runner (timings, memory, watchdog)bench.sh— orchestrator with hard timeouts and CSV verificationaggregate.py— result aggregation
- The Content Directories variant requires the "Schema Driven Build" data builder
(configured by
setup). The legacy Default Build Script silently skips CD groups. - Content Directory archiving is disabled so the CD output stays as individual granular
files — the intended shape for small patches. In this beta, archiving is controlled by an
internal editor setting; the
setupstep handles it, and this is likely to change in later releases. Archived output trades disk size for a fixed per-build archiving cost. With loose files the built app is larger (1.3 GB vs 1.0 GB here) because bundles are LZ4-compressed. - The memory delta is scenario-dependent: it reproduces the structural difference between bundle-granularity and file-granularity unloading under a shared-content layout with retained references. Scene-sliced groups would shrink the AB residual (and increase duplication instead — the classic layout trade-off).
- Results are point-in-time measurements on beta software and one machine; treat the numbers as illustrating the mechanism, not as universal constants.
- Harmless editor-only warnings from the base sample's scripts may appear during scene dependency calculation; the base sample is intentionally left unmodified.
This repository is provided as-is for reference and reproduction of the session benchmarks; external contributions (issues/PRs) are not being accepted.
Based on Unity's URP 3D Sample, © Unity Technologies. Licensed under the Unity Companion License — see LICENSE.md.