Skip to content
29 changes: 27 additions & 2 deletions src/transformers/generation/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1885,9 +1885,34 @@ def _prepare_static_cache(
"offloading": offload_cache,
}
cache = EncoderDecoderCache(cache, StaticCache(**cross_attention_cache_kwargs))

if self.config.is_encoder_decoder:
assert isinstance(cache, EncoderDecoderCache) # always true: assigned above when is_encoder_decoder

@ydshieh ydshieh Aug 24, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

this isinstance(cache, EncoderDecoderCache) is to avoid Pyright (check_types.py) complain (fail)

# Eagerly initialize the K/V buffers so that `mark_static_address` is applied before any
# `torch.compile` / CUDAGraph capture context. Without this, `lazy_initialization` runs inside
# graph capture (where `is_torchdynamo_compiling()` is True), causing `mark_static_address` to
# be skipped. CUDAGraphs then treats those tensors as pooled memory and can overwrite them
# between runs, raising:
# "accessing tensor output of CUDAGraphs that has been overwritten by a subsequent run"
# BOTH self-attention and cross-attention caches need early init: cross-attention K/V are
# computed from encoder hidden states on the first decode step, which happens inside the
# compiled graph, so they suffer the same lazy-init-inside-capture problem.
# Skipped when the cache cannot be initialized on a single device.
init_shape = self._get_static_cache_init_shape()
if init_shape is not None:
num_heads, head_dim = init_shape
early_init_kwargs = {
"batch_size": batch_size,
"num_heads": num_heads,
"head_dim": head_dim,
"dtype": self.dtype,
"device": self.device,
}
cache.self_attention_cache.early_initialization(**early_init_kwargs)
cache.cross_attention_cache.early_initialization(**early_init_kwargs)
Comment on lines +1888 to +1912

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@Cyrilvallez Let me know if you still want to have this fix only in T5 modeling code. I think the issue is there for all encoder-decoder models, but among them, only T5 has test_compile_static_cache.

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.

  1. I assume enc-dec always have the same shapes for head dim and num heads? Would investigate on real models
  2. Wouldn't it make much more sense to adjust the encoder-decoder cache's implementation of early_initialization

For 1. I could also see some adjustments for _get_static_cache_init_shape which might be related to #48619 (meaning we could combine something using the new utils introduced there)

Comment on lines +1891 to +1912

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I don't get it, I believe all this comment is wrong since prefill is never compiled - since it's an AI comment, I believe the AI probably hallucinated it

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for the note! To clarify: "the compiled graph" in the comment refers to the compiled decoder step, not the encoder. The encoder runs eagerly, but the cross-attention K/V computation (and lazy_initialization) happens on the first decoder step, which is inside the compiled graph — that's where mark_static_address gets skipped.

elif prefill_chunk_size is not None:
# Chunked prefill compiles the prefill, so eagerly init the fresh cache to avoid a recompile next call
# (#46421). Skipped (-> lazy init) when it can't be initialized on a single device.
# Chunked prefill compiles the prefill, so eagerly init the fresh cache to avoid a recompile
# next call (#46446). Skipped (-> lazy init) when it can't be initialized on a single device.
init_shape = self._get_static_cache_init_shape()
if init_shape is not None:
num_heads, head_dim = init_shape
Expand Down
Loading