Repository navigation
[CoreAI] Add coreai_expect_frequent_reshapes runtime option - #24522
Merged
Merged
Conversation
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/24522
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. |
metascroy
force-pushed
the
coreai-expect-frequent-reshapes
branch
from
November 3, 2026 21:15
b2fb6ae to
6e9fd39
Compare
metascroy
force-pushed
the
coreai-multifunction
branch
from
November 3, 2026 23:12
7ac946a to
2b55aa0
Compare
metascroy
force-pushed
the
coreai-expect-frequent-reshapes
branch
from
November 3, 2026 23:12
6e9fd39 to
4827a47
Compare
Exposes the SDK's SpecializationOptions.expectFrequentReshapes as a boolean backend option (default false), applied on top of the coreai_compute_unit selection. It is the only public specialization setting the backend did not expose yet; the export-time counterpart for AOT is AOTCompileConfig(expect_frequent_reshapes=True). The specialization identifier already encodes expectFrequentReshapes, so each value is specialized, cached and shared (coreai_share_multifunction_model) separately with no cache-key change. ETCoreAICreateModelLoader takes the flag next to the compute unit; cache eviction passes NO since it targets the bookmark.
metascroy
force-pushed
the
coreai-multifunction
branch
from
November 4, 2026 00:11
2b55aa0 to
216529d
Compare
metascroy
force-pushed
the
coreai-expect-frequent-reshapes
branch
from
November 4, 2026 00:11
4827a47 to
4994aef
Compare
SS-JIA
approved these changes
Nov 4, 2026
…24525) Makes Core AI exports that use `input_enumerations` load and run, including with multi-function export. Stacked on #24522. ## Problem Every enumerated-shape PTE failed at `load_method`: ``` Core AI execution error: Model has no function named 'main' (0x23) ``` `AIProgram.set_static_shape_config` erases the delegate's dynamic `main` and replaces it with one static clone per shape (`main_s31_4`, `main_s31_16`, ...). The manifest still named `main`, and the runtime binds exactly that name. On the real SDK, a specialized model exposes only the clones, each with a static input descriptor. ## Export An enumerated delegate's manifest lists its per-shape functions instead of one `function`: ```json "enumerated_functions": [ {"function": "main_s31_4", "input_shapes": [[4, 8]]}, {"function": "main_s31_16", "input_shapes": [[16, 8]]}, {"function": "main_s31_32", "input_shapes": [[32, 8]]} ] ``` `input_shapes` gives every input's shape in `input_names` order, the same order ExecuTorch passes arguments in. Delegates without enumerations keep `function: "main"`. If an enumerated value makes a derived boundary dim zero or negative (`s0 - 1` at `s0=1`), export fails with an error naming the input, the dim and the value. ## Runtime - **Manifest parsing:** a manifest must have exactly one of `function` and `enumerated_functions`. Enumerated entries must have distinct names, one shape per input, positive integer dims, and no two entries with the same shapes. Enumerated delegates carry an explicit `enumerated` flag. - **Init:** binds one session per listed function. Any bind failure fails init. - **Execute:** validates the arguments once, then picks the function whose shapes equal the inputs. This happens inside the per-model execution lock. Inputs that match none fail with `InvalidArgument` before anything runs, and the log names the shapes received. - **Sharing:** all functions come from one model, so they share weights, one specialization and one cache entry. - **Cost:** the number of functions is the product of the enumerated value counts. On an M4 Max, a warm load took 7 ms with 1 function, 41 ms with 16 and 248 ms with 64 (documented in the README). ## Multi-function export `CoreAIPartitioner(multifunction=True)` accepts `input_enumerations`. Give each method its own partitioner to enumerate only some methods, or different values per method: ```python partitioner={ "decode": [CoreAIPartitioner(multifunction=True)], "prefill": [CoreAIPartitioner(multifunction=True, input_enumerations=[{0: [64, 128]}])], } ``` - **Naming:** each enumerated partition expands into its own functions in the one shared model, `prefill_0_<key>`, plus `prefill_1_<key>` past a graph break. Unenumerated partitions stay single functions (`decode_0`). - **Binding order:** every partition is bound before any is specialized, because specializing a partition erases its function. - **Compile specs:** `input_enumerations` may differ between methods. Every other compile spec (AOT config, deployment floor) must match. - **Errors:** export fails with a clear error when some methods omit `multifunction=True`, naming those methods, and when two delegates would generate the same function name. - **Runtime sharing:** works with `coreai_share_multifunction_model`; every delegate and per-shape function shares one loaded model. ## Shared-load robustness This PR also tightens #24476's per-key shared loading: - A scope guard clears a load's in-flight key, publishes the model and wakes waiters on every exit, so a key can't stay marked as loading. - `WaiterAcquiresAfterConcurrentSharedLoadFails` confirms the second load is actually waiting before the first one fails. It uses a test-only `BeforeSharedLoadWait` fault point. ## Test plan **Python.** All 171 CoreAI tests pass. - `test_shape_config.py`: `test_manifest_lists_one_function_per_shape`, `test_unenumerated_lowering_keeps_main`, `test_derived_dim_that_reaches_zero_is_rejected`. The real-runtime test `test_runs_each_enumerated_shape_and_rejects_others` runs each shape and checks rejection. - `test_multifunction.py`: - `test_enumerated_method_lists_per_shape_functions`, `test_graph_break_enumerates_every_partition`. - `test_other_compile_specs_must_still_match`, `test_mixed_partitioners_name_the_missing_flag`, `test_duplicate_function_names_are_rejected`. - The real-runtime test `test_enumerated_shapes_dispatch_by_input_shape` covers multi-method with a graph break. **Native.** All 6 CTest targets pass. - `ParsesEnumeratedFunctions`, `RejectsInvalidEnumeratedFunctions` (16 malformed cases, plus both or neither field present). - `EnumeratedFunctionsDispatchByInputShape`: checks which function ran, the resized output, and that an unmatched shape runs nothing. - `EnumeratedFunctionBindFailureFailsInit`. - The sharing tests, including `WaiterAcquiresAfterConcurrentSharedLoadFails`, passed in 5 of 5 runs. **Real SDK, M4 Max:** - **Single method:** rows 4/16/32 match eager (max error ≤1.8e-7) on cold and warm loads. Rows 8 is rejected with `InvalidArgument`. - **Multi-method, C++ runner:** a static `forward` plus an enumerated `prefill`, with a graph break. That's 4 delegates and 8 functions in one model. With sharing off and on, cold and warm, 100 of 100 calls pass in each configuration (worst max error 1.4e-6), and an unmatched shape is rejected with `0x12`. - **AOT (`aimodelc`, `h16c`):** the enumerated functions compile and run (max error ≤1.8e-7). ## Compatibility PTEs exported with `input_enumerations` before this change never loaded, so nothing that worked before breaks. Every other export keeps the `function` manifest field.
This branch was previously deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a
coreai_expect_frequent_reshapesruntime option that exposes the Core AI SDK'sSpecializationOptions.expectFrequentReshapes.Stacked on #24476.
Usage
Bool, default
false. Set it for models whose input shapes change between calls. For AOT exports, the matching export-time setting isAOTCompileConfig(expect_frequent_reshapes=True), which already existed.Why this option
Checked against the Xcode 27 SDK by type-checking each member through
import CoreAI. OfSpecializationOptions, the public surface is.default,.cpuOnly,init(preferredComputeUnitKind:), read-onlyallowedComputeUnitKinds/preferredComputeUnitKind, and read/writeexpectFrequentReshapes. Everything exceptexpectFrequentReshapesis already covered bycoreai_compute_unit. The framework exports other symbols (.interpreterOnly,init(allowedComputeUnitKinds:preferredComputeUnitKind:),enableEncodingFunctions, ...), but none of them are usable fromimport CoreAI.The SDK has no doc comment for
expectFrequentReshapes. The README describes it by its name only.How it works
CoreAIBackend::initreads the option with the sharedruntime_optionhelper. A non-boolean value fails init withInvalidArgument.ETCoreAICreateModelLoadertakes the flag next to the compute unit. The Swift loader setsexpectFrequentReshapeson theSpecializationOptionschosen for that compute unit.expectFrequentReshapes, so each value gets its own specialization, bookmark and shared model (coreai_share_multifunction_modelonly shares between delegates that agree on it).Test plan
CoreAIDelegateTest.ExpectFrequentReshapesOptionReachesLoader: absent /false/truereach the loader factory, alongsidecoreai_compute_unit.CoreAIDelegateTest.OptionsRejectWrongTypes: an integer value fails init.SwiftBridgeTests.testComputeUnitsMapToSDKSpecializationOptions: every compute unit, with and without reshapes, maps to the expectedSpecializationOptions, and all 10 combinations have distinct identifiers.