Conversation
This branch has not been 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.
Summary
Since iOS 18 the torch
scaled_dot_product_attentionconverter emits the fused Core MLscaled_dot_product_attentionop, but only when torch'sscaleargument is left asNone. Any explicitscalesends the op down the matmul / softmax / matmul decomposition instead, even when the value is just the default1 / sqrt(head_dim).In practice that means the fused op is almost never produced for transformer models coming out of Hugging Face
transformers:sdpa_attention_forward(integrations/sdpa_attention.py) always calls SDPA withscale=scaling, wherescalingishead_dim**-0.5for Llama / Mistral / Qwen andquery_pre_attn_scalar**-0.5for Gemma 2 / Gemma 3. Converting a randomly initialised Llama attention layer withtorch.exportandminimum_deployment_target=ct.target.iOS18gives, onmainvs. this branch:scaled_dot_product_attentionmatmulsoftmaxscale=head_dim**-0.5), mainscale=query_pre_attn_scalar**-0.5), mainMinimal reproduction on
main:Implementation
The fused op always applies
1 / sqrt(embed_size), so a custom scale is folded into the query beforehand:_fold_scale_into_querycomputes the factor whenscaleis a compile-time constant and the embedding size is static, emits a singlemulon the query, and skips themulentirely when the factor is (numerically) 1, which is the Hugging Face default. The factor is created in the query's dtype, mirroring what_decompose_scaled_dot_product_attentionalready does for the default scale in fp16.scaleor symbolic embedding size) the converter falls back to the existing decomposition, so behaviour there is unchanged. Targets below iOS 18 are untouched.is_causal, rank-2 inputs andenable_gqacombine with an explicit scale the same way they do without one.Tests
test_scaleis now parametrised overminimum_deployment_target in (None, iOS18)andscale in (1.5, 1 / sqrt(embedding_dim)). For iOS 18 it additionally checks the emitted program: exactly one fusedscaled_dot_product_attentionop, onemulfor the custom scale and none for the default one. The existing pre-iOS18 cases still take the decomposition path.Locally the emitted MIL programs were also evaluated op by op in NumPy and compared against torch eager (fp32) for embedding sizes 7 / 16 / 64, scales 0.25 / 1.5 / 1.0 / default, both TorchScript and torch.export, plus a symbolic batch and sequence length, causal, bool mask, float mask, rank-2 and grouped-query cases; the largest difference was 6e-6.
TestScaledDotProductAttentionshows no new failures compared tomain.