Skip to content

Commit 9a942b6

Browse files
committed
perf: speed up guided decoding with xgrammar new version and batched update
1 parent 7a0c3df commit 9a942b6

4 files changed

Lines changed: 44 additions & 13 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ FetchContent_MakeAvailable(yaml-cpp)
100100
FetchContent_Declare(
101101
xgrammar
102102
GIT_REPOSITORY https://github.com/mlc-ai/xgrammar.git
103-
GIT_TAG v0.1.27
103+
GIT_TAG v0.2.1
104104
GIT_SUBMODULES "3rdparty/dlpack"
105105
GIT_PROGRESS TRUE
106106
USES_TERMINAL_DOWNLOAD TRUE

lmdeploy/turbomind/turbomind.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ def __init__(self,
165165
trust_remote_code=trust_remote_code)
166166
self.is_dummy = self.model_comm.is_dummy_node()
167167
self.tokenizer = Tokenizer(model_path, trust_remote_code=trust_remote_code)
168+
self._grammar_compiler = None
168169
if not _engine_config.empty_init:
169170
with torch.cuda.device(self.devices[0]):
170171
model_loader.export()
@@ -173,6 +174,14 @@ def __init__(self,
173174

174175
self.session_len = _engine_config.session_len
175176

177+
@property
178+
def grammar_compiler(self):
179+
"""Lazy-initialized GrammarCompiler shared across all requests."""
180+
if self._grammar_compiler is None:
181+
tokenizer_info = TokenizerInfo.from_huggingface(
182+
self.tokenizer.model.model, vocab_size=self._vocab_size)
183+
self._grammar_compiler = _xgr.GrammarCompiler(tokenizer_info)
184+
return self._grammar_compiler
176185

177186
def _process_weights(self):
178187
"""Process weight."""
@@ -672,11 +681,8 @@ async def async_stream_infer(self,
672681
gen_config=gen_config)
673682

674683
if gen_config.response_format is not None:
675-
tokenizer = self.tm_model.tokenizer
676-
vocab_size = self.tm_model._vocab_size
677-
678684
try:
679-
tokenizer_info = TokenizerInfo.from_huggingface(tokenizer.model.model, vocab_size=vocab_size)
685+
compiler = self.tm_model.grammar_compiler
680686
decode_grammar_type = gen_config.response_format['type']
681687
if decode_grammar_type == 'json_schema':
682688
decode_grammar = gen_config.response_format[decode_grammar_type]['schema']
@@ -685,8 +691,6 @@ async def async_stream_infer(self,
685691
elif decode_grammar_type == 'json_object':
686692
decode_grammar = '{"type" : "object", "additionalProperties": true}'
687693

688-
compiler = _xgr.GrammarCompiler(tokenizer_info)
689-
690694
if decode_grammar_type == 'json_schema':
691695
decode_grammar = json.dumps(decode_grammar)
692696
grammar = compiler.compile_json_schema(decode_grammar)
@@ -702,7 +706,7 @@ async def async_stream_infer(self,
702706

703707
self.model_inst.set_grammar(grammar)
704708
except ValueError as e:
705-
logger.warning(f'Failed to initialize guided decoding for tokenizer {tokenizer}, '
709+
logger.warning(f'Failed to initialize guided decoding, '
706710
f'disable guided decoding: {e}')
707711
gen_config.response_format = None
708712

src/turbomind/generation/guided_decoding.cc

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ namespace turbomind {
1212
struct GuidedDecoding::Data {
1313
Tensor_<int32_t> bitmask;
1414
bool active{};
15+
bool needs_apply{};
1516

1617
std::vector<std::shared_ptr<xgrammar::GrammarMatcher>> matchers;
1718
};
@@ -56,24 +57,36 @@ void GuidedDecoding::FillMask(int phase, TensorMap& env)
5657
(int64_t*)bitmask_buf_.shape().data(),
5758
nullptr,
5859
0};
60+
61+
std::vector<xgrammar::GrammarMatcher> active_matchers;
62+
std::vector<int32_t> active_indices;
63+
64+
d.needs_apply = false;
65+
5966
if (tp_group_->rank() == 0) {
6067
for (size_t i = 0; i < d.matchers.size(); ++i) {
61-
if (const auto& matcher = d.matchers[i]; matcher && !matcher->IsTerminated()) {
62-
matcher->FillNextTokenBitmask(&dlbitmask, i);
68+
if (const auto& m = d.matchers[i]; m && !m->IsTerminated()) {
69+
active_matchers.push_back(*m);
70+
active_indices.push_back(static_cast<int32_t>(i));
6371
}
6472
else {
6573
std::fill_n(bitmask_buf_.data() + i * bitmask_buf_.stride(0),
6674
bitmask_buf_.stride(0),
6775
static_cast<int32_t>(-1));
6876
}
6977
}
78+
79+
if (!active_matchers.empty()) {
80+
batch_matcher_.BatchFillNextTokenBitmask(&active_matchers, &dlbitmask, active_indices);
81+
d.needs_apply = true;
82+
}
7083
}
7184
}
7285
}
7386

7487
void GuidedDecoding::ApplyMask(int phase, TensorMap& env)
7588
{
76-
if (auto& d = *data_.at(phase); d.active) {
89+
if (auto& d = *data_.at(phase); d.active && d.needs_apply) {
7790
const ssize_t numel = d.matchers.size() * bitmask_buf_.stride(0);
7891
if (tp_group_->n_ranks() > 1) {
7992
// bcast the data instead of `bitmask_buf` instance (which may avoid copying the data)
@@ -93,13 +106,24 @@ void GuidedDecoding::Update(int phase, TensorMap& env)
93106
if (auto& d = *data_.at(phase); d.active) {
94107
Copy(env.at("output_ids").buffer(), d.matchers.size(), output_ids_buf_);
95108
core::Context::stream().Sync();
109+
96110
if (tp_group_->rank() == 0) {
111+
// Collect active matchers and their token IDs for batch AcceptToken
112+
std::vector<xgrammar::GrammarMatcher> active_matchers;
113+
std::vector<int32_t> active_token_ids;
114+
97115
for (size_t i = 0; i < d.matchers.size(); ++i) {
98-
if (const auto& matcher = d.matchers[i]; matcher && !matcher->IsTerminated()) {
99-
matcher->AcceptToken(output_ids_buf_[i]);
116+
if (const auto& m = d.matchers[i]; m && !m->IsTerminated()) {
117+
active_matchers.push_back(*m); // copy: refcount++
118+
active_token_ids.push_back(output_ids_buf_[i]);
100119
}
101120
}
121+
122+
if (!active_matchers.empty()) {
123+
xgrammar::BatchGrammarMatcher::BatchAcceptToken(&active_matchers, active_token_ids);
124+
}
102125
}
126+
// active_matchers destroyed here: refcount-- for each entry
103127
}
104128
}
105129

src/turbomind/generation/guided_decoding.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include "src/turbomind/comm/host_comm.h"
88
#include "src/turbomind/core/core.h"
9+
#include "xgrammar/matcher.h"
910

1011
namespace turbomind {
1112

@@ -27,6 +28,8 @@ class GuidedDecoding: public BaseGenerationParam {
2728
struct Data;
2829
std::vector<std::shared_ptr<Data>> data_;
2930

31+
xgrammar::BatchGrammarMatcher batch_matcher_;
32+
3033
Tensor_<int32_t> bitmask_buf_;
3134
Buffer_<int> output_ids_buf_;
3235
};

0 commit comments

Comments
 (0)