Skip to content

Commit 197824a

Browse files
AdvancedCompileryy33minhenghengxiedaima
authored
[Advanced Compiler]Add Vllm::apply_repetition_penalties_kernel (flagos-ai#1239)
* Add apply_repetition_penalties operator: implement operation, update tests and benchmarks * Corrected the registration logic * Corrected the registration logic * Corrected the registration logic * Corrected the registration logic --------- Co-authored-by: yy33min <2811552420@qq.com> Co-authored-by: henghengxiedaima <1149963331@qq.com>
1 parent b0d3079 commit 197824a

5 files changed

Lines changed: 248 additions & 0 deletions

File tree

benchmark/test_unary_pointwise_perf.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,3 +305,65 @@ def test_bitwise_right_shift_perf():
305305
dtypes=INT_DTYPES,
306306
)
307307
bench.run()
308+
309+
310+
class RepetitionPenaltyBenchmark(Benchmark):
311+
def __init__(self, op_name, torch_op, dtypes):
312+
super().__init__(op_name, torch_op, dtypes)
313+
self.gems_op = None
314+
315+
def set_shapes(self, shape_file_path=None):
316+
self.shapes = [
317+
(1, 1024),
318+
(1, 4096),
319+
(1, 8192),
320+
(8, 4096),
321+
(16, 4096),
322+
(32, 1024),
323+
(8, 8192),
324+
(64, 32000),
325+
]
326+
327+
def get_input_iter(self, cur_dtype):
328+
for shape in self.shapes:
329+
num_seqs, vocab_size = shape
330+
yield (
331+
torch.randn(shape, dtype=cur_dtype, device=self.device),
332+
torch.randint(0, 2, shape, dtype=torch.bool, device=self.device),
333+
torch.randint(0, 2, shape, dtype=torch.bool, device=self.device),
334+
torch.empty(num_seqs, dtype=cur_dtype, device=self.device).uniform_(
335+
1.0, 2.0
336+
),
337+
)
338+
339+
def set_gems(self, gems_op):
340+
self.gems_op = gems_op
341+
342+
343+
UNSUPPORTED_VENDORS = {
344+
"metax",
345+
"kunlunxin",
346+
"iluvatar",
347+
"mthreads",
348+
"hygon",
349+
"cambricon",
350+
}
351+
352+
353+
@pytest.mark.skipif(SkipVersion("vllm", "<0.4"), reason="vLLM <0.4 not supported")
354+
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA required")
355+
@pytest.mark.skipif(
356+
flag_gems.vendor_name in UNSUPPORTED_VENDORS, reason="Vendor not supported"
357+
)
358+
@pytest.mark.apply_repetition_penalties
359+
@pytest.mark.performance
360+
def test_perf_repetition_penalty():
361+
vllm_ops = pytest.importorskip("vllm._custom_ops")
362+
363+
bench = RepetitionPenaltyBenchmark(
364+
op_name="apply_repetition_penalties",
365+
torch_op=vllm_ops.apply_repetition_penalties,
366+
dtypes=FLOAT_DTYPES,
367+
)
368+
bench.set_gems(flag_gems.apply_repetition_penalties)
369+
bench.run()

src/flag_gems/fused/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from flag_gems.fused.apply_repetition_penalties import apply_repetition_penalties
12
from flag_gems.fused.concat_and_cache_mla import concat_and_cache_mla
23
from flag_gems.fused.cross_entropy_loss import cross_entropy_loss
34
from flag_gems.fused.flash_mla import flash_mla
@@ -23,6 +24,7 @@
2324
from flag_gems.fused.weight_norm import weight_norm
2425

2526
__all__ = [
27+
"apply_repetition_penalties",
2628
"apply_rotary_pos_emb",
2729
"skip_layer_norm",
2830
"fused_add_rms_norm",
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import torch
2+
import triton
3+
import triton.language as tl
4+
5+
6+
@triton.jit
7+
def _repetition_penalty_kernel(
8+
logits_ptr,
9+
prompt_mask_ptr,
10+
output_mask_ptr,
11+
penalties_ptr,
12+
num_seqs,
13+
vocab_size,
14+
BLOCK_SIZE: tl.constexpr,
15+
):
16+
seq_idx = tl.program_id(0)
17+
vocab_offset = tl.program_id(1) * BLOCK_SIZE
18+
19+
if seq_idx >= num_seqs:
20+
return
21+
22+
penalty = tl.load(penalties_ptr + seq_idx)
23+
24+
vocab_idx = vocab_offset + tl.arange(0, BLOCK_SIZE)
25+
26+
valid_vocab = vocab_idx < vocab_size
27+
28+
logits_idx = seq_idx * vocab_size + vocab_idx
29+
mask_idx = logits_idx
30+
31+
prompt_mask = tl.load(prompt_mask_ptr + mask_idx, mask=valid_vocab, other=False)
32+
output_mask = tl.load(output_mask_ptr + mask_idx, mask=valid_vocab, other=False)
33+
logits = tl.load(logits_ptr + logits_idx, mask=valid_vocab, other=0.0)
34+
35+
is_repeated = prompt_mask | output_mask
36+
37+
logits = tl.where(is_repeated & (logits > 0), logits / penalty, logits)
38+
logits = tl.where(is_repeated & (logits <= 0), logits * penalty, logits)
39+
40+
tl.store(logits_ptr + logits_idx, logits, mask=valid_vocab)
41+
42+
43+
def apply_repetition_penalties(logits, prompt_mask, output_mask, repetition_penalties):
44+
assert logits.is_contiguous(), "logits must be contiguous"
45+
assert (
46+
prompt_mask.is_contiguous() and prompt_mask.dtype == torch.bool
47+
), "prompt_mask must be contiguous bool tensor"
48+
assert (
49+
output_mask.is_contiguous() and output_mask.dtype == torch.bool
50+
), "output_mask must be contiguous bool tensor"
51+
assert (
52+
repetition_penalties.is_contiguous()
53+
), "repetition_penalties must be contiguous"
54+
assert logits.dim() == 2, f"logits must be 2D, got {logits.dim()}D"
55+
assert (
56+
logits.shape == prompt_mask.shape == output_mask.shape
57+
), "shape mismatch between logits and masks"
58+
assert (
59+
repetition_penalties.dim() == 1
60+
and repetition_penalties.numel() == logits.shape[0]
61+
), "repetition_penalties must be 1D with length equal to num_seqs"
62+
63+
num_seqs, vocab_size = logits.shape
64+
65+
BLOCK_SIZE = 1024
66+
67+
grid = (
68+
num_seqs,
69+
triton.cdiv(vocab_size, BLOCK_SIZE),
70+
)
71+
72+
_repetition_penalty_kernel[grid](
73+
logits,
74+
prompt_mask,
75+
output_mask,
76+
repetition_penalties,
77+
num_seqs,
78+
vocab_size,
79+
BLOCK_SIZE=BLOCK_SIZE,
80+
)
81+
return None

src/flag_gems/patches/patch_vllm_all.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,17 @@ def custom_topk_softmax(
286286
)
287287

288288

289+
def custom_apply_repetition_penalties(
290+
logits: torch.Tensor,
291+
prompt_mask: torch.Tensor,
292+
output_mask: torch.Tensor,
293+
repetition_penalties: torch.Tensor,
294+
):
295+
return flag_gems.apply_repetition_penalties(
296+
logits, prompt_mask, output_mask, repetition_penalties
297+
)
298+
299+
289300
def custom_get_scheduler_metadata(
290301
batch_size: int,
291302
max_seqlen_q: int,
@@ -409,3 +420,10 @@ def apply_gems_patches_to_vllm(verbose=True):
409420
"CUDA",
410421
verbose,
411422
)
423+
patch_vllm_lib(
424+
"_C",
425+
"apply_repetition_penalties_",
426+
custom_apply_repetition_penalties,
427+
"CUDA",
428+
verbose,
429+
)

tests/test_unary_pointwise_ops.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1458,3 +1458,88 @@ def test_accuracy_reglu(shape, dtype):
14581458
res_out = flag_gems.reglu(input_tensor)
14591459

14601460
gems_assert_close(res_out, ref_out, dtype)
1461+
1462+
1463+
def _init_vllm():
1464+
if not torch.cuda.is_available():
1465+
return None, False
1466+
try:
1467+
from vllm._custom_ops import apply_repetition_penalties as fn
1468+
1469+
t, m = torch.randn(2, 1024, device="cuda"), torch.zeros(
1470+
2, 1024, dtype=torch.bool, device="cuda"
1471+
)
1472+
fn(t, m, m, torch.full((2,), 1.2, device="cuda"))
1473+
return fn, True
1474+
except (ImportError, RuntimeError):
1475+
1476+
def fallback(logits, pm, om, pens):
1477+
for i in range(logits.shape[0]):
1478+
m = pm[i] | om[i]
1479+
logits[i][m] = torch.where(
1480+
logits[i][m] > 0, logits[i][m] / pens[i], logits[i][m] * pens[i]
1481+
)
1482+
1483+
return fallback, True
1484+
1485+
1486+
_vllm_fn, _VLLM_OK = _init_vllm()
1487+
1488+
_REP_PENALTY_CFG = {
1489+
"shapes": [
1490+
(1, 1024),
1491+
(1, 4096),
1492+
(1, 8192),
1493+
(8, 4096),
1494+
(16, 4096),
1495+
(32, 1024),
1496+
(8, 8192),
1497+
],
1498+
"penalties": [1.0, 1.2, 1.5],
1499+
"device": torch.device("cuda:0"),
1500+
}
1501+
1502+
1503+
@pytest.mark.apply_repetition_penalties
1504+
@pytest.mark.skipif(
1505+
not _VLLM_OK or not torch.cuda.is_available(), reason="need VLLM+CUDA"
1506+
)
1507+
@pytest.mark.parametrize("shape", _REP_PENALTY_CFG["shapes"])
1508+
@pytest.mark.parametrize("penalty", _REP_PENALTY_CFG["penalties"])
1509+
@pytest.mark.parametrize("dtype", FLOAT_DTYPES)
1510+
@pytest.mark.parametrize("mask_mode", ["random", "empty"])
1511+
def test_repetition_penalty(shape, penalty, dtype, mask_mode):
1512+
device = _REP_PENALTY_CFG["device"]
1513+
1514+
logits = torch.randn(shape, dtype=dtype, device=device).contiguous()
1515+
logits_ori = logits.clone()
1516+
1517+
if mask_mode == "random":
1518+
prompt_mask = torch.randint(0, 2, shape, dtype=torch.bool, device=device)
1519+
output_mask = torch.randint(0, 2, shape, dtype=torch.bool, device=device)
1520+
else:
1521+
prompt_mask = torch.zeros(shape, dtype=torch.bool, device=device)
1522+
output_mask = torch.zeros(shape, dtype=torch.bool, device=device)
1523+
1524+
penalties = torch.full((shape[0],), penalty, dtype=dtype, device=device)
1525+
1526+
logits_vllm = logits.clone()
1527+
_vllm_fn(logits_vllm, prompt_mask.clone(), output_mask.clone(), penalties.clone())
1528+
ref = to_reference(logits_vllm, True).to(dtype)
1529+
1530+
with flag_gems.use_gems():
1531+
flag_gems.apply_repetition_penalties(
1532+
logits, prompt_mask, output_mask, penalties
1533+
)
1534+
res = to_reference(logits, True).to(dtype)
1535+
1536+
gems_assert_close(res, ref, dtype)
1537+
1538+
has_mask = (prompt_mask | output_mask).any().item()
1539+
should_modify = has_mask and penalty != 1.0
1540+
if should_modify:
1541+
assert not torch.equal(
1542+
to_reference(logits, True), to_reference(logits_ori, True)
1543+
), "In-place未生效"
1544+
elif mask_mode == "empty":
1545+
gems_assert_close(res, to_reference(logits_ori, True).to(dtype), dtype)

0 commit comments

Comments
 (0)