@@ -10,7 +10,9 @@ Backend registry + selector wrapping FlashAttn / SageAttn / SageAttn3 / SDPA / V
1010attention/
1111├── __init__.py # Exports DistributedAttention, LocalAttention, get_attn_backend
1212├── layer.py # DistributedAttention, DistributedAttention_VSA, LocalAttention
13+ ├── ring_attention.py # RingAttention: owns every Ring/USP-specific decision (see below)
1314├── selector.py # get_attn_backend (cached) + _component_attention_backend_scope
15+ ├── ring/ # Vendored Ring FlashAttention kernel (ring_flash_attn_func)
1416├── backends/
1517│ ├── abstract.py # AttentionBackend / AttentionMetadata / AttentionMetadataBuilder
1618│ ├── flash_attn.py # FA2/FA3
@@ -97,6 +99,35 @@ config. Thread the request alongside that tuple, one model family at a time —
9799Wan, LTX-2 and Kandinsky5 first, since those carry per-role requests — and the
98100scope goes away when the last family lands. Do not build on it.
99101
102+ ## Ring Attention / USP Ownership Boundary
103+
104+ Ring Attention (and its Ring+Ulysses/USP hybrid) is a second delegate on
105+ ` DistributedAttention ` , with the same shape as ` self.attn_impl ` :
106+
107+ - ` DistributedAttention ` reads only ` get_ring_size() ` , and only to fail loudly
108+ if a subclass overrides ` forward() ` wholesale (e.g. ` DistributedAttention_VSA ` )
109+ without wiring in the Ring dispatch. Everything else about Ring — reading
110+ the process-wide topology, the Ulysses-within-ring all-to-all, Ring-local
111+ RoPE slicing, construction-time validation, and the direct call into the
112+ vendored Ring FlashAttention kernel (bypassing ` self.attn_impl ` /
113+ ` AttentionBackend ` entirely, since Ring picks * which* backend runs per
114+ shard rather than being one) — lives in ` RingAttention `
115+ (` ring_attention.py ` ). ` DistributedAttention.forward() ` dispatches to
116+ ` self._ring_attention.forward(...) ` when Ring is enabled, exactly the way
117+ it dispatches to ` self.attn_impl.forward(...) ` otherwise.
118+ - The Ring x Ulysses topology itself (deriving ring/ulysses sizes from
119+ ` sp_size ` , the pure-Ring / pure-Ulysses degenerate cases, building the 2D
120+ mesh of subgroups) is policy owned by ` fastvideo/distributed/usp_topology.py ` ,
121+ not ` parallel_state.py ` . ` parallel_state.py ` calls
122+ ` build_usp_topology() ` once from ` initialize_model_parallel() ` and stores
123+ the resulting ` USPTopology ` , then hands out thin accessors
124+ (` get_ring_group() ` , ` get_ulysses_group() ` , ` get_ring_rank() ` , ...) the same
125+ way it does for ` _TP ` /` _SP ` /` _DP ` — it stays a process-group registry, not a
126+ sequence-parallel policy module.
127+
128+ A new Ring/USP-specific decision belongs in ` RingAttention ` or
129+ ` usp_topology.py ` , not back in ` layer.py ` or ` parallel_state.py ` .
130+
100131## Adding a Backend
101132
1021331 . Subclass ` AttentionBackend ` in ` backends/<name>.py ` .
0 commit comments