[Spec] Reduce host-side overhead in ngram draft prep (#35207)
This commit is contained in:
@@ -241,47 +241,61 @@ class NGRAMWorker(BaseSpecWorker):
|
|||||||
bs = len(batch.reqs)
|
bs = len(batch.reqs)
|
||||||
stride = self.draft_token_num
|
stride = self.draft_token_num
|
||||||
|
|
||||||
prev_token_ids, prev_accept_lens = (
|
|
||||||
batch.spec_info.accept_tokens,
|
|
||||||
batch.spec_info.accept_lens,
|
|
||||||
)
|
|
||||||
if not prev_token_ids.is_cpu:
|
|
||||||
prev_token_ids = prev_token_ids.cpu()
|
|
||||||
prev_accept_lens = prev_accept_lens.cpu()
|
|
||||||
# Worker-level staging: written here at draft prep, consumed by
|
|
||||||
# _update_ngram_corpus after verify within the same forward call.
|
|
||||||
self.prev_token_ids = prev_token_ids.tolist()
|
|
||||||
self.prev_accept_lens = prev_accept_lens.tolist()
|
|
||||||
|
|
||||||
self.ngram_corpus.synchronize()
|
|
||||||
req_ids = []
|
|
||||||
batch_tokens = []
|
|
||||||
total_lens = []
|
|
||||||
assert len(batch.reqs) == len(self.prev_accept_lens)
|
|
||||||
# Overlap mode processes results one iteration behind, so the last
|
# Overlap mode processes results one iteration behind, so the last
|
||||||
# round's accepted tokens are not yet in req.output_ids and must be
|
# round's accepted tokens are not yet in req.output_ids and must be
|
||||||
# spliced in from spec_info. Sync mode and grammar batches process
|
# spliced in from spec_info. Sync mode and grammar batches process
|
||||||
# results before the next draft prep, so output_ids is already
|
# results before the next draft prep, so output_ids is already
|
||||||
# complete and splicing would duplicate the tail.
|
# complete and splicing would duplicate the tail.
|
||||||
use_prev_tokens = self.enable_overlap and not batch.grammar_needs_sync()
|
use_prev_tokens = self.enable_overlap and not batch.grammar_needs_sync()
|
||||||
i = 0
|
|
||||||
for req in batch.reqs:
|
# Accept-independent prep, hoisted above the blocking .cpu() below.
|
||||||
|
req_ids = [req.rid for req in batch.reqs]
|
||||||
|
# Only the last max_trie_depth tokens can match; the ids are
|
||||||
|
# array.array, so list() the tails for list concat below.
|
||||||
|
input_tails = [
|
||||||
|
list(req.origin_input_ids[-self.max_trie_depth :]) for req in batch.reqs
|
||||||
|
]
|
||||||
|
output_tails = [
|
||||||
|
list(req.output_ids[-self.max_trie_depth :]) for req in batch.reqs
|
||||||
|
]
|
||||||
|
base_lens = [
|
||||||
|
len(req.origin_input_ids) + len(req.output_ids) for req in batch.reqs
|
||||||
|
]
|
||||||
|
|
||||||
|
if use_prev_tokens:
|
||||||
|
prev_token_ids, prev_accept_lens = (
|
||||||
|
batch.spec_info.accept_tokens,
|
||||||
|
batch.spec_info.accept_lens,
|
||||||
|
)
|
||||||
|
if not prev_token_ids.is_cpu:
|
||||||
|
prev_token_ids = prev_token_ids.cpu()
|
||||||
|
prev_accept_lens = prev_accept_lens.cpu()
|
||||||
|
# Worker-level staging: written here at draft prep, consumed by
|
||||||
|
# _update_ngram_corpus after verify within the same forward call.
|
||||||
|
self.prev_token_ids = prev_token_ids.tolist()
|
||||||
|
self.prev_accept_lens = prev_accept_lens.tolist()
|
||||||
|
assert bs == len(self.prev_accept_lens)
|
||||||
|
else:
|
||||||
|
# _update_ngram_corpus still reads the staging; fill it empty.
|
||||||
|
self.prev_token_ids = []
|
||||||
|
self.prev_accept_lens = [0] * bs
|
||||||
|
|
||||||
|
self.ngram_corpus.synchronize()
|
||||||
|
batch_tokens = []
|
||||||
|
total_lens = []
|
||||||
|
for i in range(bs):
|
||||||
prev_tokens = (
|
prev_tokens = (
|
||||||
self.prev_token_ids[i * stride : i * stride + self.prev_accept_lens[i]]
|
self.prev_token_ids[i * stride : i * stride + self.prev_accept_lens[i]]
|
||||||
if use_prev_tokens
|
if use_prev_tokens
|
||||||
else []
|
else []
|
||||||
)
|
)
|
||||||
check_token = self._efficient_concat_last_n(
|
check_token = self._efficient_concat_last_n(
|
||||||
list(req.origin_input_ids),
|
input_tails[i],
|
||||||
list(req.output_ids[-self.max_trie_depth :]) + prev_tokens,
|
output_tails[i] + prev_tokens,
|
||||||
self.max_trie_depth,
|
self.max_trie_depth,
|
||||||
)
|
)
|
||||||
req_ids.append(req.rid)
|
|
||||||
batch_tokens.append(check_token)
|
batch_tokens.append(check_token)
|
||||||
i += 1
|
total_lens.append(base_lens[i] + len(prev_tokens))
|
||||||
total_lens.append(
|
|
||||||
len(req.origin_input_ids) + len(req.output_ids) + len(prev_tokens)
|
|
||||||
)
|
|
||||||
req_drafts, mask = self.ngram_corpus.batch_get(
|
req_drafts, mask = self.ngram_corpus.batch_get(
|
||||||
req_ids, batch_tokens, total_lens
|
req_ids, batch_tokens, total_lens
|
||||||
)
|
)
|
||||||
@@ -309,6 +323,17 @@ class NGRAMWorker(BaseSpecWorker):
|
|||||||
tree_mask = self.tree_mask_batch[bs]
|
tree_mask = self.tree_mask_batch[bs]
|
||||||
draft_tokens = self.draft_tokens_batch[bs]
|
draft_tokens = self.draft_tokens_batch[bs]
|
||||||
|
|
||||||
|
# Pre-sync: only needs seq_lens_cpu, so build before the blocking
|
||||||
|
# accept sync inside _prepare_draft_tokens.
|
||||||
|
ones_masks = None
|
||||||
|
if USE_FULL_MASK and not _is_cpu:
|
||||||
|
ones_masks = [
|
||||||
|
torch.ones(
|
||||||
|
(self.draft_token_num, batch.seq_lens_cpu[i]), device=self.device
|
||||||
|
)
|
||||||
|
for i in range(bs)
|
||||||
|
]
|
||||||
|
|
||||||
req_drafts, mask = self._prepare_draft_tokens(batch)
|
req_drafts, mask = self._prepare_draft_tokens(batch)
|
||||||
tree_mask.copy_(torch.from_numpy(mask), non_blocking=True)
|
tree_mask.copy_(torch.from_numpy(mask), non_blocking=True)
|
||||||
draft_tokens.copy_(torch.from_numpy(req_drafts), non_blocking=True)
|
draft_tokens.copy_(torch.from_numpy(req_drafts), non_blocking=True)
|
||||||
@@ -335,13 +360,9 @@ class NGRAMWorker(BaseSpecWorker):
|
|||||||
mask = mask.reshape(bs, self.draft_token_num, self.draft_token_num)
|
mask = mask.reshape(bs, self.draft_token_num, self.draft_token_num)
|
||||||
# TODO(siyuan): the for loop here leads to significant overhead in large batch size. Can be written into a kernel.
|
# TODO(siyuan): the for loop here leads to significant overhead in large batch size. Can be written into a kernel.
|
||||||
for i in range(bs):
|
for i in range(bs):
|
||||||
seq_len = batch.seq_lens_cpu[i]
|
|
||||||
req_mask = torch.ones(
|
|
||||||
(self.draft_token_num, seq_len), device=self.device
|
|
||||||
)
|
|
||||||
req_mask = torch.cat(
|
req_mask = torch.cat(
|
||||||
(
|
(
|
||||||
req_mask,
|
ones_masks[i],
|
||||||
torch.from_numpy(mask[i]).to(
|
torch.from_numpy(mask[i]).to(
|
||||||
device=self.device, non_blocking=True
|
device=self.device, non_blocking=True
|
||||||
),
|
),
|
||||||
@@ -393,7 +414,7 @@ class NGRAMWorker(BaseSpecWorker):
|
|||||||
else []
|
else []
|
||||||
)
|
)
|
||||||
put_ids = self._efficient_concat_last_n(
|
put_ids = self._efficient_concat_last_n(
|
||||||
list(req.origin_input_ids),
|
list(req.origin_input_ids[-self.max_trie_depth :]),
|
||||||
list(req.output_ids[-self.max_trie_depth :]) + prev_tokens,
|
list(req.output_ids[-self.max_trie_depth :]) + prev_tokens,
|
||||||
self.max_trie_depth,
|
self.max_trie_depth,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user