AttentionBackend: Abstraktionsschicht für Attention-Backends
Verantwortung
vLLM unterstützt mehr Attention-Implementierungen (attention), als man an einer Hand abzählen kann: FlashAttention 2/3/4, FlashInfer, Triton, MLA, Mamba, ROCm AITER, XPU … Jede Variante unterscheidet sich in KV-Cache (KV cache)-Layout, Kernel-Blockgröße und cudagraph-Support. Die AttentionBackend-Schicht existiert, um diese Unterschiede zu absorbieren, sodass die obere Schicht GPUModelRunner nur ein einheitliches forward aufruft, ohne wissen zu müssen, auf welchem Kernel gerade gelaufen wird.
Die Abstraktion hat zwei Hälften: AttentionBackend(backend.py:56) selbst ist eine Fabrik aus Klassenattributen und statischen Methoden, die beantwortet: «Wie heiße ich, welche impl-Klasse, welcher Metadata-Builder, welche Form soll der KV-Cache (KV cache) haben?»; AttentionMetadataBuilder(backend.py:600) ist dafür zuständig, in jedem Scheduler-Schritt (Scheduler) das CommonAttentionMetadata (die backend-übergreifend geteilten Per-Batch-Metadaten wie query_start_loc, seq_lens, block_table_tensor) in das AttentionMetadata zu übersetzen, das der Kernel des aktuellen Backends wirklich konsumiert. Darunter liegt AttentionImpl(backend.py:858), das Objekt, das tatsächlich die forward-Methode hält und pro Schicht vom Modellcode einmal aufgerufen wird.
Backends sind austauschbar: AttentionBackendEnum(registry.py:34) listet die «Klassenpfade» aller Backends als Enumeration auf; zur Laufzeit wählt get_attn_backend(selector.py:54) basierend auf head_size, dtype, kv_cache_dtype, MLA-Verwendung und Plattform-Rechenfähigkeit eines aus und lädt das entsprechende Modul lazily. Das Modell erhält bei der Initialisierung lediglich type[AttentionBackend], den Rest übernimmt diese Abstraktion.
Entwurfsmotivation
- Hardware-übergreifende Einheit: Derselbe Modellcode läuft auf NVIDIA / AMD / Intel XPU / CPU; der Unterschied liegt nur in der Backend-Wahl, und der Selector (selector) bündelt diese Entscheidung an einer Stelle.
- KV-Cache-Form entkoppelt: Jedes Backend bestimmt über
get_kv_cache_shapedas physische Layout des Block-Pools (FlashAttention:[num_blocks, 2, block_size, num_kv_heads, head_size], MLA:[num_blocks, block_size, head_size]).KVCacheManagersieht nur die Spec und interessiert sich nicht für das konkrete Backend. - cudagraph-Support abgestuft:
AttentionCGSupport(backend.py:583) teilt in ALWAYS / UNIFORM_BATCH / UNIFORM_SINGLE_TOKEN_DECODE / NEVER ein, damitGPUModelRunnerweiß, ob das aktuelle Backend in cudagraph gepackt werden kann. - argmax-Invarianz klassifiziert:
AttentionImplBase(backend.py:769) unterscheidet Eigenschaften wie «kann softmax-lse zurückgeben», «lse ist ln oder log2», «unterstützt Prefill Context Parallelism»; der DCP-Fusion-Kernel wählt anhand dieser Flags seinen Zweig. - MLA geht separaten Weg:
MLAAttentionImpl(backend.py:941) implementiert keinforward, sondernforward_mqa/forward_mha, weil der KV-Cache (KV cache) bei MLA das komprimierte Latent speichert, nicht das normale K/V.
Schlüsseldateien
AttentionBackend ABC:56— Abstrakte Basisklasse, definiert statische Methoden wieget_name/get_impl_cls/get_builder_cls/get_kv_cache_shape.vier Kern abstractmethods:74-97—get_name,get_impl_cls,get_builder_cls,get_kv_cache_shape, die Unterklassen implementieren müssen.CommonAttentionMetadata:395— Backend-übergreifend geteilte Per-Batch-Metadaten;query_start_loc,seq_lens,block_table_tensoretc. leben hier.AttentionMetadataBuilder ABC:600— Diebuild-Methode übersetztCommonAttentionMetadatain die backend-eigenen Metadaten.AttentionImplBase:769— Basisklasse mit dem echtenforward; hält Fähigkeits-Flags wiecan_return_lse_for_decode,lse_base_on_e,supports_pcp.AttentionImpl:858— ABC für Standard-Attention, definiertforward(layer, query, key, value, kv_cache, attn_metadata, output).MLAAttentionImpl:941— MLA-spezifische ABC; Schnittstelle istforward_mqa/forward_mha, nicht das normaleforward.get_attn_backend:54— Wählt Backend nach head_size/dtype/kv_cache_dtype/use_mla etc.; mit@cache.AttentionBackendEnum:34— Listet alle Backend-Klassenpfade als Enumeration; Standardwerte zeigen aufvllm.v1.attention.backends.*.register_backend:233— Überschreibt zur Laufzeit die Klasse, auf die ein Enum-Member zeigt; für Plugins.
Datenfluss
In einem Schritt (step) bereitet GPUModelRunner zuerst query/key/value und CommonAttentionMetadata für den gesamten Batch vor und ruft dann pro Schicht AttentionImpl.forward auf. Vor jeder Schicht hat AttentionMetadataBuilder.build des Backends die gemeinsamen Metadaten in die vom Kernel benötigte Form gebracht. Im Folgenden die Signatur von AttentionMetadataBuilder.build, die von allen Backends mit derselben Schnittstelle implementiert wird:
# vllm/v1/attention/backend.py L666-L678
@abstractmethod
def build(
self,
common_prefix_len: int,
common_attn_metadata: CommonAttentionMetadata,
fast_build: bool = False,
) -> M:
"""
Central method that builds attention metadata.
Some builders (MLA) require reorder_batch to be called prior to build.
Args:
common_prefix_len: The length of the common prefix of the batch.
common_attn_metadata: The common attention metadata.Welche Route das Backend nimmt, entscheidet get_attn_backend; es holt basierend auf der aktuellen vllm_config + Modell-dtype/head_size etc. den Backend-Klassenpfad aus AttentionBackendEnum und lädt ihn lazily:
# vllm/v1/attention/selector.py L75-L110
from vllm.config import get_current_vllm_config
vllm_config = get_current_vllm_config()
cache_config = vllm_config.cache_config
if cache_config is not None and cache_config.user_specified_block_size:
block_size = cache_config.block_size
else:
block_size = None
kv_transfer_config = vllm_config.kv_transfer_config
use_kv_connector = (
kv_transfer_config is not None and kv_transfer_config.is_kv_transfer_instance
)
attn_selector_config = AttentionSelectorConfig(
head_size=head_size,
dtype=dtype,
kv_cache_dtype=cast(CacheDType | None, kv_cache_dtype),
block_size=block_size,
use_mla=use_mla,
...
)
return _cached_get_attn_backend(
backend=vllm_config.attention_config.backend,
attn_selector_config=attn_selector_config,
num_heads=num_heads,
)Das Modell erhält beim Initialisieren jedes Attention / MLAAttention diese Backend-Klasse, konstruiert via backend.get_impl_cls()(...) den impl und via backend.get_builder_cls()(...) den Metadata-Builder; diese beiden Objekte werden während der gesamten Inferenzrunde weiterverwendet.
Grenzen und Fehler
- Nicht unterstützte Head-Größen:
AttentionBackend.supports_head_size(backend.py:159) befragt das Backend zuerst; FlashAttention verlangt, dass head_size ein Vielfaches von 8 ist und 256 nicht überschreitet (FA4 erst bis 512), sonst wird direkt das Backend gewechselt. - Nicht unterstützte Datentypen / KV-Cache-dtype:
supports_dtype/supports_kv_cache_dtype(backend.py:163-173) prüfen getrennt; FlashInfer unterstützt fp8/nvfp4, FlashAttention akzeptiert fp8 nur auf Hopper + FA3. - Blockgröße muss 16-ausgerichtet sein:
get_supported_kernel_block_sizesvon FlashAttention / Triton / MLA liefern jeweilsMultipleOf(16); nicht ausgerichtete block_size wirft direktValueError(flash_attn.py:130-131). - MLA-Backend verwendet kein
forward:MLAAttentionImpl(backend.py:941) deklariert nurforward_mqa/forward_mha;MLAAttentionauf Modellebene ruft je nach Kontext die entsprechende Methode auf, falsche Verwendung vonforwardführt direkt zuAttributeError. - DCP-LSE-Basis darf nicht falsch sein:
AttentionImplBase.lse_base_on_e(backend.py:795) unterscheidet ln vs. log2; eine falsche Wahl lässt den Softmax-Nenner beim schardübergreifenden Merge unbemerkt falsch werden. - Selector-Cache nimmt unveränderliche Konfiguration an:
_cached_get_attn_backend(selector.py:114) nutzt@cache; Änderungen von dtype/head_size während des Laufens wählen das Backend nicht neu — ein Prozessneustart ist nötig.
Zusammenfassung
Die AttentionBackend-Abstraktionsschicht ist der Schlüssel, mit dem vLLM mehrere Hardware- und Kernel-Varianten parallel hält: Die Modellebene kennt nur die eine Schnittstelle AttentionImpl.forward; wie der Backend den KV-Cache (KV cache) anordnet, wie Metadaten erzeugt werden und ob FlashAttention oder Triton zum Einsatz kommen, wird vollständig vom Selector (selector) + Enum bei der Initialisierung festgelegt. Wie jedes Backend im Detail implementiert ist, siehe /attention/flash (FlashAttention / FlashInfer) und /attention/triton-mla (Triton / MLA). Wo die obere Schicht diese impls aufruft, siehe /worker/gpu-model-runner.