Fix mask creation not being skipped under torch.compile - #48975
Open
jiqing-feng wants to merge 2 commits into
Open
jiqing-feng wants to merge 2 commits into
jiqing-feng wants to merge 2 commits into
Conversation
jiqing-feng
force-pushed
the
fix-mask-skip-under-compile
branch
from
September 21, 2026 05:32
b4ac03a to
d829df5
Compare
`_ignore_causal_mask_sdpa` and `_ignore_bidirectional_mask_sdpa` bailed out on `is_tracing(padding_mask)`, which is always True under `torch.compile`. As a result the 4D mask was materialized even without a padding mask, so sdpa could never dispatch to its flash/oneDNN kernels via `is_causal`. Whether a padding mask is passed at all is a static property that dynamo guards on. Only the checks reading the mask values are a data-dependent control flow, so the tracing guard is moved down to those, and `torch.export` keeps bailing out early through `is_torchdynamo_exporting()`. OPT additionally overwrote `attention_mask` with a dense all-ones mask to infer its learned positional embeddings, which then leaked into `create_causal_mask` and defeated the skip. The dense mask is now kept in a local variable. Fixes huggingface#48924 Fixes huggingface#48925
jiqing-feng
force-pushed
the
fix-mask-skip-under-compile
branch
from
September 21, 2026 05:55
d829df5 to
2dc4531
Compare
torch.compile when there is no padding mask
…kip helpers
`is_torchdynamo_exporting()` maps to `torch.compiler.is_exporting()`, which dynamo
hard-coded to `True` in its `tracing_state_functions` table, i.e. it also reported
exporting under plain `torch.compile`. That was only fixed by pytorch#176499, released
in torch 2.14, so on older versions the export guard still triggers under compile and
the mask keeps being materialized (conservative and correct, just without the speedup).
Since setup.py allows torch>=2.5, gate the new compile test with
`@require_torch_greater_or_equal("2.14")` so it does not fail on older versions, and
document the dependency next to the guard.
Note that this cannot be worked around from user code: on 2.7.1 every tracing-state
signal has identical values under `torch.compile` and strict `torch.export`, and reading
`torch.compiler._is_exporting_flag` directly is unsafe because it is `False` during
strict export and would bake the skip into the exported program.
Also return a Python `bool` via `bool(fast_all(padding_mask))` from
`_ignore_bidirectional_mask_sdpa` / `_can_skip_bidirectional_mask_xpu` instead of a
0-dim tensor from `padding_mask.all()`, matching their `-> bool` annotation and the
`fast_all` helper already used by the causal path.
torch.compile when there is no padding masktorch.compile
Contributor
|
[For maintainers] Suggested jobs to run (before merge) run-slow: opt |
Contributor
CI recapDashboard: View test results in Grafana |
jiqing-feng
marked this pull request as ready for review
September 21, 2026 07:34
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.
Fixes #48924
Fixes #48925
Problem
create_causal_maskandcreate_bidirectional_maskreturnNonewhen SDPA can express the maskthrough its own
is_causalargument. This lets SDPA dispatch to flash attention. Passing a denseattn_maskinstead selects a slower path, which materializes a[batch, 1, q_len, kv_len]tensor onevery forward.
Two independent bugs prevented the skip:
OPTDecoder.forwardoverwrote its ownattention_mask(OPT training with torch.compile decomposes SDPA into Triton kernels due to attention_mask #48924). When the caller passedNone,OPT replaced it with a dense all-ones tensor and forwarded that into
create_causal_mask. Thetensor is only needed to derive
position_ids.masking_utilsbailed out of every skip condition underis_tracing(padding_mask)(4D mask created under torch.compile when padding_mask is None #48925).That guard also covers
torch.compile, so a compiled model never skipped mask creation. Yet theunpadded case reads no tensor values: the presence of a padding mask,
q_length,kv_lengthandq_offsetare static properties that dynamo already guards on. Onlyfast_all(padding_mask)isdata-dependent.
Fix
OPTDecoderkeeps the dense tensor in a localposition_attention_mask. The caller'sattention_masknow reachescreate_causal_maskunchanged._ignore_causal_mask_sdpa,_ignore_bidirectional_mask_sdpaand_can_skip_bidirectional_mask_xpuare reordered to: export guard, static conditions, tracing guard, value-dependent conditions.
torch.exportstill bails out first, since export hard-codesis_causalinto the exported program(pytorch#108108).
Both fixes are required. Spying on
create_causal_maskinside OPT without a padding mask:torch.compilemainFix 2 takes effect on torch >= 2.14. Dynamo hard-coded
torch.compiler.is_exporting()toTrueuntil pytorch#176499, so the export guard also
triggered under
torch.compile. On older versions the mask is still materialized, which is thecurrent behavior. The new compile test is therefore gated with
@require_torch_greater_or_equal("2.14").Result
On one A100 80GB with torch 2.14.0: 63.78 ms -> 49.17 ms, a 1.30x speedup. The assertion passes
on both revisions, so the skip does not change the results. Padded masks are still materialized.
Dynamo reports the same two graph breaks before and after, both from unrelated data-dependent
branching.
Tests
test_attention_mask_is_not_overwritten_for_causal_mask(OPT) andtest_mask_skip_without_padding_mask_under_compile(masking utils) are new. Both fail onmain.Full runs on A100 with torch 2.14:
tests/models/opt150 passed,tests/models/{llama,bert,gemma2}593 passed,
tests/utils/test_masking_utils.pyall passed except the pre-existingtest_packed_sequence_mask_flex_attentionfailure.The bidirectional helpers now return a Python
boolviabool(fast_all(padding_mask))instead of a0-dim tensor, which matches their
-> boolannotation. No replacement guard was added for thetorch.jit.is_tracing()coverage thatis_tracing()provided, astorch.jitis deprecated andis_jit_tracinghas no other call site.