Skip to content

Commit dce4d6c

Browse files
committed
better rope c++ wrapper
1 parent 619b3e7 commit dce4d6c

4 files changed

Lines changed: 84 additions & 77 deletions

File tree

ctests/test_triton_rope.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,9 @@ INSTANTIATE_TEST_SUITE_P(RotaryEmbeddingTests,
134134
// batch_size, seq_len, q_heads, head_dim, dtype, rotary_interleaved, has_pos_id
135135
std::make_tuple(1, 16, 8, 64, torch::kFloat32, true, true),
136136
std::make_tuple(2, 512, 4, 64, torch::kFloat32, false, true),
137-
std::make_tuple(4, 1024, 8, 128, torch::kFloat32, true, true),
138-
std::make_tuple(8, 2048, 128, 128, torch::kFloat32, false, true),
137+
std::make_tuple(4, 1024, 8, 128, torch::kFloat16, true, true),
138+
std::make_tuple(8, 2048, 128, 128, torch::kBFloat16, false, true),
139139
std::make_tuple(8, 1024, 64, 128, torch::kFloat32, true, false),
140-
std::make_tuple(8, 2048, 128, 256, torch::kFloat32, false, false)));
140+
std::make_tuple(8, 2048, 128, 256, torch::kFloat32, false, false),
141+
std::make_tuple(8, 2048, 32, 64, torch::kFloat16, true, true),
142+
std::make_tuple(8, 2048, 16, 32, torch::kBFloat16, false, false)));

lib/rotary_embedding.cpp

Lines changed: 51 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -9,65 +9,78 @@
99
namespace flag_gems {
1010
using namespace triton_jit;
1111

12-
void rotary_embedding_inplace(
13-
at::Tensor& q, // [batch_size, seq_len, q_heads, head_dim] or [num_tokens, q_heads, head_dim]
14-
at::Tensor& k, // [batch_size, seq_len, k_heads, head_dim] or [num_tokens, k_heads, head_dim]
12+
void check_rotary_embedding_inputs(
13+
const at::Tensor& q, // [batch_size, seq_len, q_heads, head_dim] or [num_tokens, q_heads, head_dim]
14+
const at::Tensor& k, // [batch_size, seq_len, k_heads, head_dim] or [num_tokens, k_heads, head_dim]
1515
const at::Tensor& cos, // [max_seq_len, head_dim // 2]
1616
const at::Tensor& sin, // [max_seq_len, head_dim // 2]
17-
std::optional<at::Tensor> position_ids, // None or [..., seq_len]
18-
bool rotary_interleaved) { // default false
19-
17+
const std::optional<at::Tensor>& position_ids) { // None or [..., seq_len]
18+
// 1. Check that q and k have the same head dimension
2019
TORCH_CHECK(k.size(-1) == q.size(-1),
2120
"q and k must have the same last dimension, got ",
2221
q.sizes(),
2322
" and ",
2423
k.sizes());
24+
25+
// 2. Check that cos and sin have the same last dimension
2526
TORCH_CHECK(cos.size(-1) == sin.size(-1),
2627
"cos and sin must have the same last dimension, got ",
2728
cos.sizes(),
2829
" and ",
2930
sin.sizes());
31+
32+
// 3. Check that cos/sin dimension matches q/k head_dim // 2
3033
TORCH_CHECK(cos.size(-1) * 2 == q.size(-1),
3134
"cos/sin dim must be half of q/k dim, got ",
3235
cos.sizes(),
3336
" and ",
3437
q.sizes());
3538

36-
TORCH_CHECK(cos.stride(-1) == 1, "cos must be contiguous at the last dimension");
37-
TORCH_CHECK(sin.stride(-1) == 1, "sin must be contiguous at the last dimension");
39+
// 4. Check that cos and sin are contiguous at the last dimension
40+
TORCH_CHECK(cos.stride(-1) == 1,
41+
"cos must be contiguous at the last dimension, got stride ",
42+
cos.stride(-1));
43+
TORCH_CHECK(sin.stride(-1) == 1,
44+
"sin must be contiguous at the last dimension, got stride ",
45+
sin.stride(-1));
3846

3947
auto q_sizes = q.sizes();
4048
auto k_sizes = k.sizes();
41-
std::optional<int64_t> seq_len;
4249

50+
// 5. Check that q and k have the same number of dimensions
4351
TORCH_CHECK(q_sizes.size() == k_sizes.size(),
4452
"q and k must have the same number of dimensions, got ",
4553
q_sizes.size(),
4654
" and ",
4755
k_sizes.size());
4856

57+
// 6. Check that all dimensions except the last two match between q and k
4958
for (int i = 0; i < q_sizes.size() - 2; ++i) {
5059
TORCH_CHECK(q_sizes[i] == k_sizes[i],
51-
"q and k must have the same shape before the last two dims, got ",
52-
q_sizes,
60+
"Mismatch in q and k shape at dim ",
61+
i,
62+
": got ",
63+
q_sizes[i],
5364
" and ",
54-
k_sizes);
65+
k_sizes[i]);
5566
}
5667

68+
// 7. If position_ids is not provided, q must have 4 dimensions
5769
if (!position_ids.has_value()) {
5870
TORCH_CHECK(q_sizes.size() == 4,
5971
"q must have 4 dimensions if position_ids is not provided, got ",
6072
q_sizes.size());
61-
seq_len = q_sizes[1];
62-
63-
} else { // default case
73+
} else {
6474
auto pos_sizes = position_ids.value().sizes();
75+
76+
// 8. Check that position_ids has the same number of dims as q.shape[:-2]
6577
TORCH_CHECK(pos_sizes.size() == q_sizes.size() - 2,
6678
"position_ids must have the same number of dims as q.shape[:-2], got ",
6779
pos_sizes.size(),
6880
" and ",
6981
q_sizes.size() - 2);
7082

83+
// 9. Check that position_ids shape matches q.shape[:-2] on each dimension
7184
for (int i = 0; i < pos_sizes.size(); ++i) {
7285
TORCH_CHECK(pos_sizes[i] == q_sizes[i],
7386
"Mismatch in position_ids and q shape at dim ",
@@ -77,7 +90,28 @@ void rotary_embedding_inplace(
7790
" and ",
7891
q_sizes[i]);
7992
}
93+
}
94+
}
95+
96+
void rotary_embedding_inplace(
97+
at::Tensor& q, // [batch_size, seq_len, q_heads, head_dim] or [num_tokens, q_heads, head_dim]
98+
at::Tensor& k, // [batch_size, seq_len, k_heads, head_dim] or [num_tokens, k_heads, head_dim]
99+
const at::Tensor& cos, // [max_seq_len, head_dim // 2]
100+
const at::Tensor& sin, // [max_seq_len, head_dim // 2]
101+
std::optional<at::Tensor> position_ids, // None or [..., seq_len]
102+
bool rotary_interleaved) { // default false
103+
104+
check_rotary_embedding_inputs(q, k, cos, sin, position_ids);
105+
106+
auto q_sizes = q.sizes();
107+
auto k_sizes = k.sizes();
108+
std::optional<int64_t> seq_len;
109+
110+
if (!position_ids.has_value()) {
111+
seq_len = q_sizes[1];
80112

113+
} else { // default case
114+
auto pos_sizes = position_ids.value().sizes();
81115
position_ids = position_ids.value().view({-1}); // flatten the position_ids tensor
82116
seq_len = std::nullopt;
83117
}
@@ -166,67 +200,18 @@ std::tuple<at::Tensor, at::Tensor> rotary_embedding(const at::Tensor& q,
166200
const at::Tensor& sin,
167201
std::optional<at::Tensor> position_ids,
168202
bool rotary_interleaved) {
169-
TORCH_CHECK(k.size(-1) == q.size(-1),
170-
"q and k must have the same last dimension, got ",
171-
q.sizes(),
172-
" and ",
173-
k.sizes());
174-
TORCH_CHECK(cos.size(-1) == sin.size(-1),
175-
"cos and sin must have the same last dimension, got ",
176-
cos.sizes(),
177-
" and ",
178-
sin.sizes());
179-
TORCH_CHECK(cos.size(-1) * 2 == q.size(-1),
180-
"cos/sin dim must be half of q/k dim, got ",
181-
cos.sizes(),
182-
" and ",
183-
q.sizes());
184-
185-
TORCH_CHECK(cos.stride(-1) == 1, "cos must be contiguous at the last dimension");
186-
TORCH_CHECK(sin.stride(-1) == 1, "sin must be contiguous at the last dimension");
203+
// Check inputs
204+
check_rotary_embedding_inputs(q, k, cos, sin, position_ids);
187205

188206
auto q_sizes = q.sizes();
189207
auto k_sizes = k.sizes();
190208
std::optional<int64_t> seq_len;
191209

192-
TORCH_CHECK(q_sizes.size() == k_sizes.size(),
193-
"q and k must have the same number of dimensions, got ",
194-
q_sizes.size(),
195-
" and ",
196-
k_sizes.size());
197-
198-
for (int i = 0; i < q_sizes.size() - 2; ++i) {
199-
TORCH_CHECK(q_sizes[i] == k_sizes[i],
200-
"q and k must have the same shape before the last two dims, got ",
201-
q_sizes,
202-
" and ",
203-
k_sizes);
204-
}
205-
206210
if (!position_ids.has_value()) {
207-
TORCH_CHECK(q_sizes.size() == 4,
208-
"q must have 4 dimensions if position_ids is not provided, got ",
209-
q_sizes.size());
210211
seq_len = q_sizes[1];
211212

212213
} else { // default case
213214
auto pos_sizes = position_ids.value().sizes();
214-
TORCH_CHECK(pos_sizes.size() == q_sizes.size() - 2,
215-
"position_ids must have the same number of dims as q.shape[:-2], got ",
216-
pos_sizes.size(),
217-
" and ",
218-
q_sizes.size() - 2);
219-
220-
for (int i = 0; i < pos_sizes.size(); ++i) {
221-
TORCH_CHECK(pos_sizes[i] == q_sizes[i],
222-
"Mismatch in position_ids and q shape at dim ",
223-
i,
224-
": got ",
225-
pos_sizes[i],
226-
" and ",
227-
q_sizes[i]);
228-
}
229-
230215
position_ids = position_ids.value().view({-1}); // flatten the position_ids tensor
231216
seq_len = std::nullopt;
232217
}

src/flag_gems/modules/rotary_embedding.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@
2525

2626
logger = logging.getLogger(__name__)
2727

28-
has_c_extension = False # Disable C extension for now, as we have not implemented c++ wrapper for rotary_embedding yet.
28+
try:
29+
from flag_gems import ext_ops # noqa: F401
30+
31+
has_c_extension = True
32+
except ImportError:
33+
has_c_extension = False
2934

3035
__all__ = [
3136
"gems_rope_forward",
@@ -44,10 +49,21 @@ def gems_rope_forward(
4449
inplace: bool = False,
4550
) -> Union[torch.Tensor, torch.Tensor]:
4651
logger.debug("GEMS CUSTOM ROPE FORWARD")
47-
# TODO: Implement C++ wrapper for rotary_embedding
48-
return flag_gems.apply_rotary_pos_emb(
49-
query, key, cos, sin, position_ids, rotary_interleaved, inplace
50-
)
52+
if has_c_extension:
53+
if inplace:
54+
torch.ops.flag_gems.rotary_embedding_inplace(
55+
query, key, cos, sin, position_ids, rotary_interleaved
56+
)
57+
return query, key
58+
else:
59+
return torch.ops.flag_gems.rotary_embedding(
60+
query, key, cos, sin, position_ids, rotary_interleaved
61+
)
62+
else:
63+
# Fallback to pure python implementation
64+
return flag_gems.apply_rotary_pos_emb(
65+
query, key, cos, sin, position_ids, rotary_interleaved, inplace
66+
)
5167

5268

5369
class GemsRope(nn.Module):

tests/test_special_ops.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,13 @@ def test_apply_rotary_pos_emb(
203203
position_ids=ref_position_ids if has_pos_id else None,
204204
rotary_interleaved=rotary_interleaved,
205205
)
206-
207-
q_embed_out, k_embed_out = torch.ops.flag_gems.rotary_embedding(
208-
q, k, cos, sin, position_ids if has_pos_id else None, rotary_interleaved
206+
q_embed_out, k_embed_out = flag_gems.apply_rotary_pos_emb(
207+
q=q,
208+
k=k,
209+
cos=cos,
210+
sin=sin,
211+
position_ids=position_ids if has_pos_id else None,
212+
rotary_interleaved=rotary_interleaved,
209213
)
210214

211215
gems_assert_close(q_embed_out, q_embed_ref, dtype)

0 commit comments

Comments
 (0)