RuVI-AI
/

ArushCodes commited on
Commit
05acbb5
·
verified ·
1 Parent(s): d55c0ec

Update kernel.py

Browse files
Files changed (1) hide show
  1. kernel.py +162 -146
kernel.py CHANGED
@@ -1,147 +1,163 @@
1
- """Attention kernel with two backends, chosen once at import.
2
-
3
- * FlexAttention (default): CUDA with compute capability >= 8.0 (sm80+), torch >= 2.5.
4
- Masks are BlockMasks; the mask logic lives in mask_mod.
5
- * xFormers memory-efficient attention: CUDA GPUs below sm80 (no Triton/Flex support).
6
- Masks are dense boolean (T, T) or (B, T, T) tensors converted to additive biases.
7
-
8
- Every attention layer calls `fused_attention(q, k, v, mask)`; the mask object comes from
9
- `causal_masks` or `doc_masks`, so the backend is invisible to model.py.
10
-
11
- Attention-weight dropout is not supported on either backend (DenseMHA refuses dropout > 0).
12
- """
13
- import functools
14
- import importlib
15
-
16
- import torch
17
- from torch.nn.attention.flex_attention import create_block_mask, flex_attention
18
-
19
- _IS_CUDA = torch.cuda.is_available() and torch.version.cuda is not None
20
- _USE_XFORMERS = _IS_CUDA and torch.cuda.get_device_capability() < (8, 0)
21
-
22
- if _USE_XFORMERS:
23
- # Loaded dynamically: Transformers scans source files for static imports and would
24
- # require xformers on every machine, including ones that never use it.
25
- xops = importlib.import_module("xformers.ops") # pip install xformers (match your torch)
26
- _flex = None
27
- else:
28
- xops = None
29
- _flex = torch.compile(flex_attention, dynamic=False) if _IS_CUDA else flex_attention
30
-
31
-
32
- class _XMask:
33
- """Dense allowed-positions mask for the xFormers path. allowed: bool (T, T) or (B, T, T)."""
34
- __slots__ = ("allowed",)
35
-
36
- def __init__(self, allowed: torch.Tensor):
37
- self.allowed = allowed
38
-
39
-
40
- def fused_attention(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, mask) -> torch.Tensor:
41
- """q: (B, Hq, T, D); k, v: (B, Hkv, T, D). GQA is handled natively (Hq a multiple of Hkv)."""
42
- if isinstance(mask, _XMask):
43
- return _xformers_attention(q, k, v, mask.allowed)
44
- return _flex(q, k, v, block_mask=mask, enable_gqa=q.shape[1] != k.shape[1])
45
-
46
-
47
- def _xformers_attention(q, k, v, allowed):
48
- B, Hq, T, D = q.shape
49
- Hkv = k.shape[1]
50
- if Hq != Hkv:
51
- # query head h uses kv head h // group, the same grouping FlexAttention's enable_gqa uses
52
- group = Hq // Hkv
53
- k = k.repeat_interleave(group, dim=1)
54
- v = v.repeat_interleave(group, dim=1)
55
- bias = torch.zeros(allowed.shape, dtype=q.dtype, device=q.device).masked_fill(~allowed, float("-inf"))
56
- bias = bias[None, None] if bias.dim() == 2 else bias[:, None]
57
- bias = bias.expand(B, Hq, T, T)
58
- # xFormers expects (B, T, H, D)
59
- out = xops.memory_efficient_attention(
60
- q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2), attn_bias=bias)
61
- _record_backend("xformers")
62
- return out.transpose(1, 2)
63
-
64
-
65
- # ---------------------------------------------------------------------------
66
- # Masks. One rule for every layer: window = window_size or seq_len; since q - kv <= seq_len - 1,
67
- # "no window" is the same expression as "window = seq_len".
68
- # ---------------------------------------------------------------------------
69
-
70
- @functools.lru_cache(maxsize=None)
71
- def _causal_mask(seq_len: int, window_size, device_key: str):
72
- window = window_size or seq_len
73
-
74
- def mask_mod(b, h, q_idx, kv_idx):
75
- return (kv_idx <= q_idx) & (q_idx - kv_idx < window)
76
-
77
- return create_block_mask(mask_mod, B=None, H=None, Q_LEN=seq_len, KV_LEN=seq_len, device=device_key)
78
-
79
-
80
- @functools.lru_cache(maxsize=None)
81
- def _xcausal_mask(seq_len: int, window_size, device_key: str):
82
- idx = torch.arange(seq_len, device=device_key)
83
- q_idx = idx[:, None]
84
- kv_idx = idx[None, :]
85
- window = window_size or seq_len
86
- return _XMask((kv_idx <= q_idx) & (q_idx - kv_idx < window))
87
-
88
-
89
- @torch.compiler.disable
90
- def causal_masks(seq_len: int, windows, device) -> dict:
91
- """{window_size: mask} for plain causal attention; cached per (seq_len, window, device)."""
92
- if _USE_XFORMERS:
93
- return {w: _xcausal_mask(seq_len, w, str(device)) for w in windows}
94
- return {w: _causal_mask(seq_len, w, str(device)) for w in windows}
95
-
96
-
97
- @torch.compiler.disable
98
- def doc_masks(doc_ids: torch.Tensor, windows) -> dict:
99
- """{window_size: mask} for causal + same-document attention. doc_ids: (B, T) int, per batch."""
100
- b_, t_ = doc_ids.shape
101
-
102
- if _USE_XFORMERS:
103
- idx = torch.arange(t_, device=doc_ids.device)
104
- q_idx = idx[:, None]
105
- kv_idx = idx[None, :]
106
- same_doc = doc_ids[:, :, None] == doc_ids[:, None, :] # (B, T, T)
107
-
108
- def build_x(window_size):
109
- window = window_size or t_
110
- return _XMask(same_doc & (kv_idx <= q_idx) & (q_idx - kv_idx < window))
111
-
112
- return {w: build_x(w) for w in windows}
113
-
114
- def build(window_size):
115
- window = window_size or t_
116
-
117
- def mask_mod(b, h, q_idx, kv_idx):
118
- return (doc_ids[b, q_idx] == doc_ids[b, kv_idx]) & (kv_idx <= q_idx) & (q_idx - kv_idx < window)
119
-
120
- return create_block_mask(mask_mod, B=b_, H=None, Q_LEN=t_, KV_LEN=t_,
121
- device=doc_ids.device, _compile=True)
122
-
123
- return {w: build(w) for w in windows}
124
-
125
-
126
- # ---------------------------------------------------------------------------
127
- # Telemetry counters (model.py records its Liger/Triton success/fallback events here).
128
- # ---------------------------------------------------------------------------
129
-
130
- _backend_stats: dict[str, int] = {}
131
-
132
-
133
- def _record_backend(name: str) -> None:
134
- # Inside a compiled region this body runs only at trace time, so counting here under-reports.
135
- # Record nothing while compiling rather than report wrong numbers.
136
- if torch.compiler.is_compiling():
137
- return
138
- _backend_stats[name] = _backend_stats.get(name, 0) + 1
139
-
140
-
141
- def get_backend_stats() -> dict[str, int]:
142
- return dict(_backend_stats)
143
-
144
-
145
- def reset_backend_stats() -> None:
146
- _backend_stats.clear()
147
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Attention kernel with two backends, chosen once at import.
2
+
3
+ * FlexAttention (default): CUDA with compute capability >= 8.0 (sm80+), torch >= 2.5.
4
+ Masks are BlockMasks; the mask logic lives in mask_mod.
5
+ * xFormers memory-efficient attention: CUDA GPUs below sm80 (no Triton/Flex support).
6
+ Masks are dense boolean (T, T) or (B, T, T) tensors converted to additive biases.
7
+
8
+ Every attention layer calls `fused_attention(q, k, v, mask)`; the mask object comes from
9
+ `causal_masks` or `doc_masks`, so the backend is invisible to model.py.
10
+
11
+ Attention-weight dropout is not supported on either backend (DenseMHA refuses dropout > 0).
12
+ """
13
+ import functools
14
+ import importlib
15
+ import warnings
16
+
17
+ import torch
18
+ import torch.nn.functional as F
19
+ from torch.nn.attention.flex_attention import create_block_mask, flex_attention
20
+
21
+ _IS_CUDA = torch.cuda.is_available() and torch.version.cuda is not None
22
+ _USE_XFORMERS = _IS_CUDA and torch.cuda.get_device_capability() < (8, 0)
23
+
24
+ _XFORMERS_OK = _USE_XFORMERS # cleared on the first failure; then SDPA is used
25
+
26
+ if _USE_XFORMERS:
27
+ # Loaded dynamically: Transformers scans source files for static imports and would
28
+ # require xformers on every machine, including ones that never use it.
29
+ xops = importlib.import_module("xformers.ops") # pip install xformers (match your torch)
30
+ _flex = None
31
+ else:
32
+ xops = None
33
+ _flex = torch.compile(flex_attention, dynamic=False) if _IS_CUDA else flex_attention
34
+
35
+
36
+ class _XMask:
37
+ """Dense allowed-positions mask for the xFormers path. allowed: bool (T, T) or (B, T, T)."""
38
+ __slots__ = ("allowed",)
39
+
40
+ def __init__(self, allowed: torch.Tensor):
41
+ self.allowed = allowed
42
+
43
+
44
+ def fused_attention(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, mask) -> torch.Tensor:
45
+ """q: (B, Hq, T, D); k, v: (B, Hkv, T, D). GQA is handled natively (Hq a multiple of Hkv)."""
46
+ if isinstance(mask, _XMask):
47
+ return _xformers_attention(q, k, v, mask.allowed)
48
+ return _flex(q, k, v, block_mask=mask, enable_gqa=q.shape[1] != k.shape[1])
49
+
50
+
51
+ def _xformers_attention(q, k, v, allowed):
52
+ B, Hq, T, D = q.shape
53
+ Hkv = k.shape[1]
54
+ if Hq != Hkv:
55
+ # query head h uses kv head h // group, the same grouping FlexAttention's enable_gqa uses
56
+ group = Hq // Hkv
57
+ k = k.repeat_interleave(group, dim=1)
58
+ v = v.repeat_interleave(group, dim=1)
59
+ bias = torch.zeros(allowed.shape, dtype=q.dtype, device=q.device).masked_fill(~allowed, float("-inf"))
60
+ bias = bias[None, None] if bias.dim() == 2 else bias[:, None]
61
+ bias = bias.expand(B, Hq, T, T)
62
+ global _XFORMERS_OK
63
+ if _XFORMERS_OK:
64
+ try:
65
+ # xFormers expects (B, T, H, D)
66
+ out = xops.memory_efficient_attention(
67
+ q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2), attn_bias=bias)
68
+ _record_backend("xformers")
69
+ return out.transpose(1, 2)
70
+ except Exception as e:
71
+ # Typical cause: xformers built for a different torch/CUDA than the runtime.
72
+ _XFORMERS_OK = False
73
+ warnings.warn(f"xformers memory_efficient_attention unavailable ({type(e).__name__}: {e}); "
74
+ "using torch scaled_dot_product_attention with the same mask.")
75
+ # Same math with PyTorch's built-in attention (works on any CUDA GPU, no extra package).
76
+ out = F.scaled_dot_product_attention(q, k, v, attn_mask=bias)
77
+ _record_backend("sdpa_masked")
78
+ return out
79
+
80
+
81
+ # ---------------------------------------------------------------------------
82
+ # Masks. One rule for every layer: window = window_size or seq_len; since q - kv <= seq_len - 1,
83
+ # "no window" is the same expression as "window = seq_len".
84
+ # ---------------------------------------------------------------------------
85
+
86
+ @functools.lru_cache(maxsize=None)
87
+ def _causal_mask(seq_len: int, window_size, device_key: str):
88
+ window = window_size or seq_len
89
+
90
+ def mask_mod(b, h, q_idx, kv_idx):
91
+ return (kv_idx <= q_idx) & (q_idx - kv_idx < window)
92
+
93
+ return create_block_mask(mask_mod, B=None, H=None, Q_LEN=seq_len, KV_LEN=seq_len, device=device_key)
94
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
+ @functools.lru_cache(maxsize=None)
97
+ def _xcausal_mask(seq_len: int, window_size, device_key: str):
98
+ idx = torch.arange(seq_len, device=device_key)
99
+ q_idx = idx[:, None]
100
+ kv_idx = idx[None, :]
101
+ window = window_size or seq_len
102
+ return _XMask((kv_idx <= q_idx) & (q_idx - kv_idx < window))
103
+
104
+
105
+ @torch.compiler.disable
106
+ def causal_masks(seq_len: int, windows, device) -> dict:
107
+ """{window_size: mask} for plain causal attention; cached per (seq_len, window, device)."""
108
+ if _USE_XFORMERS:
109
+ return {w: _xcausal_mask(seq_len, w, str(device)) for w in windows}
110
+ return {w: _causal_mask(seq_len, w, str(device)) for w in windows}
111
+
112
+
113
+ @torch.compiler.disable
114
+ def doc_masks(doc_ids: torch.Tensor, windows) -> dict:
115
+ """{window_size: mask} for causal + same-document attention. doc_ids: (B, T) int, per batch."""
116
+ b_, t_ = doc_ids.shape
117
+
118
+ if _USE_XFORMERS:
119
+ idx = torch.arange(t_, device=doc_ids.device)
120
+ q_idx = idx[:, None]
121
+ kv_idx = idx[None, :]
122
+ same_doc = doc_ids[:, :, None] == doc_ids[:, None, :] # (B, T, T)
123
+
124
+ def build_x(window_size):
125
+ window = window_size or t_
126
+ return _XMask(same_doc & (kv_idx <= q_idx) & (q_idx - kv_idx < window))
127
+
128
+ return {w: build_x(w) for w in windows}
129
+
130
+ def build(window_size):
131
+ window = window_size or t_
132
+
133
+ def mask_mod(b, h, q_idx, kv_idx):
134
+ return (doc_ids[b, q_idx] == doc_ids[b, kv_idx]) & (kv_idx <= q_idx) & (q_idx - kv_idx < window)
135
+
136
+ return create_block_mask(mask_mod, B=b_, H=None, Q_LEN=t_, KV_LEN=t_,
137
+ device=doc_ids.device, _compile=True)
138
+
139
+ return {w: build(w) for w in windows}
140
+
141
+
142
+ # ---------------------------------------------------------------------------
143
+ # Telemetry counters (model.py records its Liger/Triton success/fallback events here).
144
+ # ---------------------------------------------------------------------------
145
+
146
+ _backend_stats: dict[str, int] = {}
147
+
148
+
149
+ def _record_backend(name: str) -> None:
150
+ # Inside a compiled region this body runs only at trace time, so counting here under-reports.
151
+ # Record nothing while compiling rather than report wrong numbers.
152
+ if torch.compiler.is_compiling():
153
+ return
154
+ _backend_stats[name] = _backend_stats.get(name, 0) + 1
155
+
156
+
157
+ def get_backend_stats() -> dict[str, int]:
158
+ return dict(_backend_stats)
159
+
160
+
161
+ def reset_backend_stats() -> None:
162
+ _backend_stats.clear()
163
+