Repository navigation
Conversation
Use a byte bitmask to track rejected tokens in llama_grammar_apply_impl, then compact the array with std::remove_if before returning. Downstream samplers (top_k, softmax, dist) then operate on only the grammar-valid candidates (M << n) instead of the full vocabulary. std::remove_if is stable so a pre-sorted array stays sorted after compaction. Also fixes common/sampling.cpp: the is_valid check for single-token grammar validation previously tested data[0].logit != -INFINITY. After compaction, remove_if on a 1-element rejected range leaves the element in place with its original logit, so that check always passes. Changed to single_token_data_array.size > 0. Adapted from koboldcpp PR LostRuins#1606 by Reithan.
|
Hi @Reithan, thanks for your contribution! Per our contribution guidelines, the automated PR checker found the following issue(s) that need your attention:
Please note that maintainers reserve the right to make final decisions on PRs. If you believe there is a mistake, please comment below. |
|
Closing to comply with the 1-open-PR limit for new contributors. Will reopen after PR #28371 is processed. Branch remains at Reithan:grammar-compact-apply-impl. |
llama_grammar_apply_implmarks rejected tokens aslogit = -INFINITYin-place, leavingcur_p->sizeat the full vocabulary size. Downstream samplers (top_k,softmax,dist) then operate on the full n-entry array even when grammar has reduced the valid set to M entries (M << n). For a 128k-token vocabulary,top_kperforms an O(n) pass over all entries every sampling step (partial_sort for k ≤ 128, bucket sort above —src/llama-sampler.cpp:135–215); with compaction it operates on only M entries.This PR switches rejection bookkeeping to a byte bitmask and compacts the array with
std::remove_ifbefore returning fromllama_grammar_apply_impl. All downstream samplers benefit automatically.std::remove_ifis stable, so a pre-sorted array remains sorted after compaction (thesortedflag is preserved).Also fixes
common/sampling.cpp: theis_validcheck for single-token grammar validation previously testeddata[0].logit != -INFINITY. After compaction,remove_ifon a 1-element rejected range leaves the element in place with its original logit, so that check always passes, accepting grammar-invalid tokens and corrupting grammar state on the nextllama_grammar_accept_implcall. Changed tosingle_token_data_array.size > 0. Third-party code wrappingllama_sampler_init_grammarthat checks logit for validity should be updated to check size instead.New failure mode (not a regression): When grammar rejects all tokens,
cur_p->sizereaches 0 and the next sampler hits its size assertion immediately. Upstream currently also aborts in this case, one step later (NaN-sampled token →!stacks.empty()assert inllama_grammar_accept_impl), so this is a fast-fail, not a regression.Estimated improvement: 20–40% on heavy grammars (complex JSON schema, tool-call grammars). No regression on simple grammars or the no-grammar path.
See also: #28371 (grammar recursion memoization), #28373 (pre-cull top-k before grammar scan)
Adapted from LostRuins/koboldcpp PR #1606 by Reithan.