Skip to content

Fix data-dependent branch in _ignore_causal_mask_sdpa under compile - #49354

Merged
vasqu merged 4 commits into
mainfrom
fix-static-cache-prefill-mask-graph-break
Oct 8, 2026
Merged

vasqu merged 4 commits into
mainfrom
fix-static-cache-prefill-mask-graph-break

Conversation

@dacorvo

@dacorvo dacorvo commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

CPU CI GPU run-slow

What does this PR do?

With a static cache, q_offset is a tensor. Under torch.compile (torch ≥ 2.14), the condition in _ignore_causal_mask_sdpa tests its value, which breaks the graph (Unsupported: Data-dependent branching with fullgraph=True). A traced q_offset now materializes the mask instead.

In generate(), this affects chunked prefill, the only path that runs multi-token inputs through the compiled forward. For the first chunk of an unpadded prompt, prepare_inputs_for_generation skips the mask because the cache is empty, so the compiled forward rebuilds it and hits that condition.

Tests

  • test_static_cache_prefill_mask_under_compile (new): fails on main, passes with the fix (torch 2.14.1).
  • Compiled chunked prefill (static cache, fullgraph=True) fails on main with the error above; with the fix, it matches a dynamic-cache generate().

Who can review?

@vasqu @Cyrilvallez

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

@dacorvo
dacorvo requested review from Cyrilvallez and vasqu and removed request for Cyrilvallez October 7, 2026 07:08
Comment thread src/transformers/masking_utils.py Outdated
Comment on lines +278 to +280
# Under `torch.compile`, a tensor `q_offset` cannot be read without breaking the graph
if isinstance(q_offset, torch.Tensor) and is_tracing(q_offset):
return False

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm so this happens since forever tho right? Why is it only torch 2.14? Is this rather a regression on torch or something we could fix on cache side?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @Cyrilvallez would love your take here

@dacorvo dacorvo Oct 7, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vasqu good question, that made me wonder why I had not seen it before.
Turns out this only happens with torch ≥ 2.14 (now the default version shipped with the Neuron SDK).
I asked Claude to dig in, and it's a regression from #48975, revealed by the torch bump.

Before #48975: is_tracing(padding_mask) returned early under compile on all versions, so q_offset == 0 was never reached.

After #48975: the guard now uses is_torchdynamo_exporting(), which dynamo hard-codes to True under compile before torch 2.14, so older versions still return early. On ≥ 2.14 it only returns True during export and False under compile. This is why we don't return early and run into the data-dependent graph issue.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok this is now revealed as torch 2.14 can properly use it during compile. That was a super recent PR

2 things tho

  1. Let's move this close where we already check for padding mask tracing (and add a comment that this only happens under (static) caches that need a tensor object (otherwise its another graph break))
  2. Let's just use is_tracing no? I think it indirectly checks for this to be a tensor like object no?

@dacorvo dacorvo Oct 8, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move this close where we already check for padding mask tracing (and add a comment that this only happens under (static) caches that need a tensor object (otherwise its another graph break))

Done

Let's just use is_tracing no? I think it indirectly checks for this to be a tensor like object no?

I double checked: is_tracing(x) returns True as soon as is_torchdynamo_compiling() is True, without looking at x. So without the isinstance additional check, an int q_offset (and not only the Tensor q_offset we have when using a static cache) would make _ignore_causal_mask_sdpa return False and force the causal mask to be materialized, hurting performance even for non static caches.

@vasqu vasqu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some smaller comments, this is indeed something that only got revealed through a recent PR

Comment thread src/transformers/masking_utils.py Outdated
Comment on lines +278 to +280
# Under `torch.compile`, a tensor `q_offset` cannot be read without breaking the graph
if isinstance(q_offset, torch.Tensor) and is_tracing(q_offset):
return False

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok this is now revealed as torch 2.14 can properly use it during compile. That was a super recent PR

2 things tho

  1. Let's move this close where we already check for padding mask tracing (and add a comment that this only happens under (static) caches that need a tensor object (otherwise its another graph break))
  2. Let's just use is_tracing no? I think it indirectly checks for this to be a tensor like object no?

dacorvo and others added 2 commits October 8, 2026 12:26
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread src/transformers/masking_utils.py Resolved
@vasqu
vasqu enabled auto-merge October 8, 2026 12:39
@vasqu
vasqu added this pull request to the merge queue Oct 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

CI recap

Dashboard: View test results in Grafana
Latest run: 37777671026:1
Result: success | Jobs: 16 | Tests: 198,274 | Failures: 0 | Duration: 12h 35m

Merged via the queue into main with commit 6595731 Oct 8, 2026
119 checks passed
@vasqu
vasqu deleted the fix-static-cache-prefill-mask-graph-break branch October 8, 2026 13:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants