> For the complete documentation index, see [llms.txt](https://postica.gitbook.io/binding-system-3/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://postica.gitbook.io/binding-system-3/project-tools/performance/optimized-accessors.md).

# Build-Time Optimized Accessors

Collapse a whole bound path into one generated method at build time.

The [accessor layer](/binding-system-3/project-tools/performance.md#reflection-is-used-to-find-the-member-never-to-read-it) already turns a bound path into a chain of delegate calls, one per segment, with direct access for fields. Fast, but not free.

For paths that are fully known before the game runs, the build can do better. The optimizer scans the project, finds those paths, and writes a **single direct static method** for each one. At runtime the accessor layer calls that method instead, so the whole path costs one call and nothing about it is resolved at runtime at all.

Off by default. Turn it on in **Project Settings ▸ Binding System ▸ Configuration ▸ Build-Time Optimized Accessors**.

<figure><img src="https://3048705056-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLUfBXR02sJ5i0MxwYbaV%2Fuploads%2FxIvAK3HimoIkq7qStu3d%2FScreenshot%202026-09-29%20at%2013.58.41.png?alt=media&amp;token=8ded204f-9edb-45ce-ac8d-989251ea4bc9" alt="" width="563"><figcaption><p>The optimized accessors manager, with its status card</p></figcaption></figure>

## What qualifies

A binding is a candidate when everything about it is deterministic:

<table><thead><tr><th width="290">Requirement</th><th>Why</th></tr></thead><tbody><tr><td><a href="/binding-system-3/overview/sources.md#value">Value source mode</a></td><td>A direct object reference. Every other mode resolves at runtime, so the source type is not known at build time.</td></tr><tr><td>Every path segment is public</td><td>A public field, a public auto-property, a public method, or a public indexer.</td></tr><tr><td>No provider syntax</td><td><a href="/binding-system-3/reference/extending/accessor-providers.md">Accessor providers</a> resolve their own paths.</td></tr><tr><td>No open generics</td><td>The generated method needs a concrete type.</td></tr><tr><td>Parametric paths</td><td>Indexers and methods qualify only when the source is a reference type and the parameters are constants.</td></tr><tr><td>Long enough to be worth it</td><td>Below <strong>Min Path Pieces</strong> the runtime cost is already negligible and codegen is skipped.</td></tr></tbody></table>

A qualifying binding shows an **optimized badge** on its bind row, with the tooltip *This binding will be replaced by a generated direct-access method at build time*. That is how you find out per binding, without opening a window.

## The Optimized Accessors window

Every candidate the scanner finds across the project: build scenes, non-build scenes, currently open scenes, Resources and their dependencies.

<figure><img src="https://3048705056-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLUfBXR02sJ5i0MxwYbaV%2Fuploads%2FOroPOIbK0PtX6b6CS7EF%2FScreenshot%202026-09-28%20at%2018.17.31.png?alt=media&amp;token=cede6899-d600-4e3a-bbe8-168061c9163f" alt="" width="563"><figcaption><p>The Optimized Accessors window</p></figcaption></figure>

Rows are grouped by `(source type, path)`, since the same pair often appears in many places, and each one expands into its occurrences. Clicking an occurrence resolves it back to the live object and pings it, so you land on the actual bound field.

**Build only** narrows the list to scenes that are in the build, which is the set that actually matters for a release.

## Settings

<table><thead><tr><th width="290">Setting</th><th>What it does</th></tr></thead><tbody><tr><td><strong>Generate Optimized Accessors</strong></td><td>The master switch. Off by default.</td></tr><tr><td><strong>Min Path Pieces</strong></td><td>Paths with fewer pieces than this skip codegen. Indexers count as one piece.</td></tr><tr><td><strong>On Stale Build</strong></td><td>What happens when the build starts and the generated accessors would change: <strong>Ignore</strong>, <strong>Warn</strong> (default), or <strong>Fail Build</strong>.</td></tr><tr><td><strong>Keep Generated File For Inspection</strong></td><td>Keeps the file after the build instead of removing it. Off by default.</td></tr><tr><td><strong>Generated Accessors Path</strong></td><td>Where the file is written. Must be inside <code>Assets/</code>.</td></tr><tr><td><strong>↻ Generate Now</strong></td><td>Runs the scan and writes the file immediately, without building.</td></tr></tbody></table>

The status card above the settings says where things stand: *Never generated*, *Up to date*, *Out of date*, *Scanning project assets in the background*, or *Disabled*. The scan runs in the background and the settings page never blocks on it.

## What happens during a build

1. Before player compilation, the scanner runs and the writer emits the file.
2. The result is hashed and compared against the last recorded hash. A mismatch triggers the **On Stale Build** behaviour.
3. The player compiles with the generated accessors in it.
4. After the build, the file is removed, unless **Keep Generated File For Inspection** is on.

A build aborted between the pre and post steps is cleaned up on the next editor load, so a cancelled build does not leave generated code in your project.

{% hint style="info" %}
Because the file is generated and removed per build, it is not something to commit. **Generated Accessors Path** points inside `Assets/` because the file has to be compiled with your code, not because it is meant to live there.
{% endhint %}

## Choosing On Stale Build

<table><thead><tr><th width="200">Behaviour</th><th>What it does</th></tr></thead><tbody><tr><td><strong>Warn</strong></td><td>The default, and right for local builds.</td></tr><tr><td><strong>Fail Build</strong></td><td>Right for CI. A stale set means the build machine is optimizing a different set of bindings from the one the project now has.</td></tr><tr><td><strong>Ignore</strong></td><td>When you know the difference is irrelevant and want silence.</td></tr></tbody></table>

## Is it worth turning on

<table><thead><tr><th width="330">Project</th><th>Is it worth it</th></tr></thead><tbody><tr><td>Many bindings with deep paths, in built scenes</td><td>Yes. This is exactly the case it was built for.</td></tr><tr><td>Bindings mostly one or two segments deep</td><td>Little to gain. <strong>Min Path Pieces</strong> already skips those.</td></tr><tr><td>Bindings mostly using variable, tag or pattern sources</td><td>No. Those cannot be pre-resolved.</td></tr><tr><td>Tight CPU budget on a mobile target</td><td>Yes, and set <strong>On Stale Build</strong> to <strong>Fail Build</strong>.</td></tr></tbody></table>

Open the window before deciding. If it lists twelve candidates, the answer is no.

## Related pages

* [Paths and Parameters](/binding-system-3/overview/paths.md): what a path can contain.
* [Phased Bindings](/binding-system-3/project-tools/performance/phased-bindings.md): the other half of the runtime story.
* [Benchmarks](/binding-system-3/project-tools/performance/benchmarks.md): measuring the effect.
