Skip to content

[CoreAI] Add a coreai_compute_unit backend option - #24375

Merged
metascroy merged 4 commits into
mainfrom
coreai-expose-compute-unit
Nov 4, 2026
Merged

metascroy merged 4 commits into
mainfrom
coreai-expose-compute-unit

Conversation

@metascroy

Copy link
Copy Markdown
Contributor

Adds a coreai_compute_unit backend option for the Core AI delegate, set the same way as coreai_cache_dir. It selects which compute units Core AI may use when it specializes a model on the device.

Stacked on #24374.

Option values

Each value is exactly one of the configurations the Core AI SDK provides through SpecializationOptions:

Value Core AI SpecializationOptions Allowed compute units
default (or absent) .default CPU, GPU, Neural Engine
cpu_only .cpuOnly CPU
prefer_cpu init(preferredComputeUnitKind: .cpu) CPU, GPU, Neural Engine
prefer_gpu init(preferredComputeUnitKind: .gpu) CPU, GPU, Neural Engine
prefer_neural_engine init(preferredComputeUnitKind: .neuralEngine) CPU, GPU, Neural Engine

The option mirrors the SDK and adds nothing of its own:

  • cpu_only is the only restriction. A preference maximizes use of that unit but falls back to any allowed unit for operations it can't run.
  • No other combinations. The SDK can't express ones like "CPU and Neural Engine only", because its allowed units can't be set.
  • No separate allowed/preferred settings. One string covers every configuration the SDK can build.
BackendOptions<2> options;
ET_CHECK_OK_OR_RETURN_ERROR(options.set_option("coreai_compute_unit", "cpu_only"));
LoadBackendOptionsMap map;
ET_CHECK_OK_OR_RETURN_ERROR(map.set_options("CoreAIBackend", options.view()));
ET_CHECK_OK_OR_RETURN_ERROR(module.load(map));

Implementation

  • coreai_backend.mm reads the option during init, next to coreai_cache_dir. Unknown values, non-string values and null fail with InvalidArgument before a loader is created. The error lists the valid values.
  • ETCoreAIBridge.h adds an ETCoreAIComputeUnit enum, and ETCoreAICreateModelLoader now takes it. ETCoreAIModelLoader.swift maps each value to the matching SpecializationOptions.
  • Caching: the cache key already includes the loader's specialization identity, so each value gets its own bookmark, source directory and lock stripe. default keeps its existing identity, so bookmarks from before this change still restore. clear_cache_for_pte still removes every variant of a PTE.
  • Creating a loader only to evict a bookmark uses default, because eviction doesn't depend on specialization.
  • The README documents the option.

Test plan

  • coreai_host_test passes. A new delegate test checks that each value reaches the loader factory, that a missing option means default, and that unknown and non-string values are rejected before a loader is created.
  • coreai_swift_bridge_test passes. A new Swift test checks that each value builds exactly the expected SpecializationOptions, that the five values have five different cache identities, and that default keeps the "default" identity.
  • Manual multithreaded check, using a standalone runner that loads a two-delegate fp16 PTE with the five values assigned round-robin across threads:
    • Cold, 10 threads × 50 inferences: all outputs matched the reference, with one cache key per compute unit.
    • Warm, 50 threads × 200: all 10,000 outputs matched, with no new specializations; every load restored its own compute unit's bookmark.

@metascroy
metascroy requested a review from shoumikhin as a code owner October 31, 2026 00:28
@pytorch-bot

pytorch-bot Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/24375

Note: Links to docs will display an error until the docs builds have been completed.

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 31, 2026
@shoumikhin

shoumikhin commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

@claude review effort=max

Expose the Core AI SDK's SpecializationOptions configurations as a runtime
backend option: default, cpu_only, prefer_cpu, prefer_gpu and
prefer_neural_engine. The selection feeds the loader's specialization
identity, so each value is specialized and cached under its own key.
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

…ers collide

testComputeUnitsMapToSDKSpecializationOptions fails on CI runners with fewer
distinct identifiers than compute units, but passes locally with the same
macOS and Xcode. Include each unit's options and identifier in the failure
message to show which units collide.
The SDK limits allowed compute units to the hardware, so on CPU-only CI
runners .default and .cpuOnly resolve to the same options and correctly
share an identifier. Instead of expecting one distinct identifier per
compute unit, require two identifiers to match exactly when their options
do.
Renames the Core AI delegate manifest's `packaging` values so they name
the embedded format instead of where it's stored.

Stacked on #24375.

## Why

`"inline"` and `"aot_compiled_inline"` describe storage (embedded in the
`.pte`), which is the same for both formats. They don't say what is
actually different: a portable `.aimodel` versus AOT-compiled
`.aimodelc` bundles. The runtime also used a third vocabulary,
`"source"` / `"aot"`, in logs and the cache identity. "source" already
meant something else there: the bundle copy the runtime writes to the
cache for the SDK, which happens for AOT bundles too.

## Changes

| Where | Before | After |
|---|---|---|
| Manifest `packaging` | `"inline"` / `"aot_compiled_inline"` |
`"aimodel"` / `"aimodelc"` |
| Python `AssetPackaging` (`compiler/preprocess.py`) | `INLINE` /
`AOT_COMPILED_INLINE` | `AIMODEL` / `AIMODELC` |
| Runtime manifest check (`runtime/coreai_manifest.mm`) | accepts the
old values; AOT detected by an `aot_compiled_` prefix | accepts only the
new values; AOT means `== "aimodelc"` |
| Specialization log (`coreai_load_coordinator.mm`) | `kind=source` /
`kind=aot` | `kind=aimodel` / `kind=aimodelc` |
| Cache identity (`coreai_cache_key.mm`) | `"source"` / `"aot"` |
`"aimodel"` / `"aimodelc"` |

Test fixtures, test labels and the README follow the new names. In the
README, "source" now only means the copy written for the SDK.

## Compatibility

* **Old PTEs:** this is a manifest format change. PTEs exported with the
old names fail to load with `Unsupported Core AI packaging; expected
aimodel or aimodelc` and must be re-exported.
* **Existing caches:** the cache identity changes, so existing bookmarks
aren't found and each model specializes again on its first load. Old
entries stay on disk unused.

## Test plan

* `test_preprocess.py`: 25/25 passed, including the real `coreai-build`
AOT test.
* Native: all 6 CTest targets pass (`coreai_host_test`,
`coreai_pte_export_test`, `coreai_swift_bridge_test`,
`coreai_sdk_source_lifetime_test`, `coreai_runtime_smoke`,
`coreai_multithread_test`). The native fixtures are regenerated and
carry `packaging: "aimodel"`.
* Manual end-to-end with a standalone runner on an M4 Max:
  * Portable PTE: `kind=aimodel`, outputs match the reference.
* AOT PTE: first load `kind=aimodelc` with exact outputs; second load
restores from the cache bookmark.
  * A PTE exported before this change is rejected with the error above.
* ufmt, flake8 and clang-format are clean.
@metascroy
metascroy merged commit c2f7511 into main Nov 4, 2026
241 of 248 checks passed
@metascroy
metascroy deleted the coreai-expose-compute-unit branch November 4, 2026 19:37

This branch was previously deployed

1 inactive deployment
cadence — 4566bedf Deployed Nov 4, 2026 by metascroy via hifi5s-op-test / hifi5s #36617
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants