-
Notifications
You must be signed in to change notification settings - Fork 45
server: preemption notices, asynchronous parks and exact concurrency (pin branch on b10869) #210
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: upstream/b10871-inkling-25731
Are you sure you want to change the base?
Changes from 18 commits
afd3248
2834f07
62cf502
1cfb42b
17a0f3b
1d55489
0b9caab
15bcc6c
3e7d953
2176de1
d0202ea
a05a318
f8b7514
0beed51
d040ae7
c5a89f7
7eb9cb7
d8daa8f
5b66acb
b4433f5
41abdfb
c5dc0f9
ae19539
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| #include "ggml.h" | ||
| #include "ggml-backend.h" | ||
| #include "gguf.h" | ||
|
|
||
| #include "build-info.h" | ||
|
|
@@ -1289,6 +1290,12 @@ struct common_init_result::impl { | |
|
|
||
| common_init_result::common_init_result(common_params & params, bool model_only) : | ||
| pimpl(new impl{}) { | ||
| // [TAG_EXACT_CONCURRENCY] before any context exists, so one is never created under a figure the explicit bound does not cover | ||
| if (!model_only && !common_exact_concurrency_init(params)) { | ||
| COM_ERR("%s", "LLAMA_EXACT_CONCURRENCY: refusing to load the model, see the error above\n"); | ||
| return; | ||
| } | ||
|
|
||
| auto mparams = common_model_params_to_llama(params); | ||
| auto cparams = common_context_params_to_llama(params); | ||
|
|
||
|
|
@@ -1337,6 +1344,11 @@ common_init_result::common_init_result(common_params & params, bool model_only) | |
| return; | ||
| } | ||
|
|
||
| if (!common_exact_concurrency_model(params, model)) { | ||
| COM_ERR("%s", "LLAMA_EXACT_CONCURRENCY: refusing to create a context, see the error above\n"); | ||
| return; | ||
| } | ||
|
|
||
| const llama_vocab * vocab = llama_model_get_vocab(model); | ||
|
|
||
| // load and optionally apply lora adapters | ||
|
|
@@ -1403,6 +1415,12 @@ common_init_result::common_init_result(common_params & params, bool model_only) | |
|
|
||
| pimpl->context.reset(lctx); | ||
|
|
||
| if (!common_exact_concurrency_context(params, lctx)) { | ||
| COM_ERR("%s", "LLAMA_EXACT_CONCURRENCY: refusing to serve this context, see the error above\n"); | ||
| pimpl->context.reset(); | ||
| return; | ||
| } | ||
|
|
||
| set_process_priority(params.cpuparams.priority); | ||
|
|
||
| pimpl->threadpools.init(lctx, params); | ||
|
|
@@ -1433,6 +1451,148 @@ std::vector<llama_adapter_lora_ptr> & common_init_result::lora() { | |
| return pimpl->lora; | ||
| } | ||
|
|
||
| // [TAG_EXACT_CONCURRENCY] | ||
| bool common_exact_concurrency() { | ||
| static const bool enabled = []() { | ||
| const char * val = getenv("LLAMA_EXACT_CONCURRENCY"); | ||
| return val && atoi(val) != 0; | ||
| }(); | ||
|
|
||
| return enabled; | ||
| } | ||
|
|
||
| int common_exact_decode_width(const common_params & params) { | ||
| const int64_t n_slots = std::max(1, params.n_parallel); | ||
|
|
||
| const int64_t n_draft = std::max(0, (int) common_speculative_n_max(¶ms.speculative)); | ||
|
|
||
| // the product is handed to a backend as an int; one that overflows is reported, not wrapped | ||
| const int64_t n_cols = n_slots*(1 + n_draft); | ||
|
|
||
| return n_cols > INT32_MAX ? -1 : (int) n_cols; | ||
| } | ||
|
|
||
| bool common_exact_batch_geometry(int n_batch, int n_ubatch, int n_decode_width, int * n_batch_min) { | ||
| // an unset ubatch is the whole batch, and a ubatch never exceeds it | ||
| const int n_ub = std::min(n_batch, n_ubatch <= 0 ? n_batch : n_ubatch); | ||
|
|
||
| const int n_min = n_ub + std::max(0, n_decode_width); | ||
|
|
||
| if (n_batch_min) { | ||
| *n_batch_min = n_min; | ||
| } | ||
|
|
||
| return n_batch >= n_min; | ||
| } | ||
|
|
||
| // [TAG_EXACT_CONCURRENCY] the refusals that need the loaded model, run before a context exists | ||
| bool common_exact_concurrency_model(const common_params & params, const llama_model * model) { | ||
| if (!common_exact_concurrency() || params.mmproj.path.empty()) { | ||
| return true; | ||
| } | ||
|
|
||
| // the paged pool places a cell from the sequence and the position alone, and M-RoPE gives every token of one image the same temporal position, so the second of them lands on the first one's cell and the batch is refused at the first image | ||
| const llama_rope_type rope_type = llama_model_rope_type(model); | ||
|
|
||
| if (rope_type == LLAMA_ROPE_TYPE_MROPE || rope_type == LLAMA_ROPE_TYPE_IMROPE) { | ||
| COM_ERR("%s", "LLAMA_EXACT_CONCURRENCY does not support M-RoPE together with a projector: the tokens of one image share a temporal position and the paged pool would give them one cell\n"); | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool common_exact_concurrency_init(const common_params & params) { | ||
| if (!common_exact_concurrency()) { | ||
| return true; | ||
| } | ||
|
|
||
| // DFlash drafting turns causal attention off on its draft context, which the paged attention needs; say so instead of asserting in the graph. DSpark is the same. | ||
| for (const auto type : params.speculative.types) { | ||
| if (type == COMMON_SPECULATIVE_TYPE_DRAFT_DFLASH || type == COMMON_SPECULATIVE_TYPE_DRAFT_DSPARK) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
With Useful? React with 👍 / 👎.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A shared-memory draft never claims a cell: apply_ubatch returns early when other is set and it is the only caller of exact_pages_claim, so every draft step sees the same free cell at n_past. The combination is also unreachable: ctx_other exists only for GEMMA4_ASSISTANT, EAGLE3 and DFLASH, the last two cannot produce an MTP context, and the Gemma 4 assistant is SWA, which exact mode refuses at cache construction. |
||
| COM_ERR("%s", "LLAMA_EXACT_CONCURRENCY does not support --spec-type draft-dflash or draft-dspark: both disable causal attention on the draft, which the paged attention needs\n"); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| const int n_cols = common_exact_decode_width(params); | ||
|
|
||
| if (n_cols < 0) { | ||
| COM_ERR("LLAMA_EXACT_CONCURRENCY: a decode step of %d slots with %d draft tokens each is too wide to report\n", | ||
| std::max(1, params.n_parallel), std::max(0, (int) common_speculative_n_max(¶ms.speculative))); | ||
| return false; | ||
| } | ||
|
|
||
| const char * bound = getenv("GGML_CUDA_BATCH_INVARIANT_MAX_COLS"); | ||
| if (bound) { | ||
| const int max_cols = atoi(bound); | ||
| if (max_cols > 0 && max_cols < n_cols) { | ||
| COM_ERR("GGML_CUDA_BATCH_INVARIANT_MAX_COLS is %d but LLAMA_EXACT_CONCURRENCY needs at " | ||
| "least %d to cover a decode step of %d slots, above which a matmul is left " | ||
| "batched and its rows depend on the other rows in the ubatch. Raise it to %d, " | ||
| "set it to 0 for no bound, or unset it to let it default to %d.\n", | ||
| max_cols, n_cols, std::max(1, params.n_parallel), n_cols, n_cols); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| // a prompt is added to a batch in whole ubatches, so a batch that cannot hold one beside a decode step of every slot would leave a prefill shorter ubatches than it gets alone, and the mode would report itself as on while a shared step changed the prompt's arithmetic | ||
| // a causal context clamps the batch to the context size, so that is the batch a prefill really gets; an unset -c is only known once the context exists, which common_exact_concurrency_context() checks | ||
| const int n_batch_eff = params.n_ctx > 0 ? std::min(params.n_ctx, params.n_batch) : params.n_batch; | ||
|
|
||
| int n_batch_min = 0; | ||
|
|
||
| if (!common_exact_batch_geometry(n_batch_eff, params.n_ubatch, n_cols, &n_batch_min)) { | ||
| COM_ERR("LLAMA_EXACT_CONCURRENCY needs a batch of at least %d tokens for a %d-token ubatch " | ||
| "and a decode step of %d slots (%d columns), but the batch is %d: a prefill beside " | ||
| "a running slot would be split into shorter ubatches than the same prompt gets alone. " | ||
| "Raise -b to %d (and -c to at least that), or lower -ub.\n", | ||
| n_batch_min, std::min(n_batch_eff, params.n_ubatch <= 0 ? n_batch_eff : params.n_ubatch), | ||
| std::max(1, params.n_parallel), n_cols, n_batch_eff, n_batch_min); | ||
| return false; | ||
| } | ||
|
|
||
| // the batch splitter isolates prompts by width, so tell it how wide one sequence's decode step is; this also covers a caller that decodes before creating a context | ||
| if (!llama_set_exact_decode_tokens((uint32_t) (n_cols / std::max(1, params.n_parallel))) || | ||
| !llama_set_exact_decode_width((uint32_t) n_cols)) { | ||
| COM_ERR("%s", "LLAMA_EXACT_CONCURRENCY: the decode width could not be reported, see the error above\n"); | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| bool common_exact_concurrency_context(const common_params & params, const llama_context * ctx) { | ||
| if (!common_exact_concurrency()) { | ||
| return true; | ||
| } | ||
|
|
||
| const int n_cols = common_exact_decode_width(params); | ||
|
|
||
| if (n_cols < 0) { | ||
| return false; // already reported by common_exact_concurrency_init() | ||
| } | ||
|
|
||
| // the context clamps the batch to the context size and the ubatch to the batch, and an unset -c takes its size from the model or from the fit to device memory, so this is the geometry a prefill really gets | ||
| const int n_batch = (int) llama_n_batch(ctx); | ||
| const int n_ubatch = (int) llama_n_ubatch(ctx); | ||
|
|
||
| int n_batch_min = 0; | ||
|
|
||
| if (!common_exact_batch_geometry(n_batch, n_ubatch, n_cols, &n_batch_min)) { | ||
| COM_ERR("LLAMA_EXACT_CONCURRENCY needs a batch of at least %d tokens for a %d-token ubatch " | ||
| "and a decode step of %d slots (%d columns), but the context was created with a batch " | ||
| "of %d: a context of %d tokens clamps it, so a prefill beside a running slot would be " | ||
| "split into shorter ubatches than the same prompt gets alone. Raise -c to at least %d " | ||
| "(-fitc as well when the context was fitted to device memory), or lower -ub.\n", | ||
| n_batch_min, n_ubatch, std::max(1, params.n_parallel), n_cols, n_batch, | ||
| (int) llama_n_ctx(ctx), n_batch_min); | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| common_init_result_ptr common_init_from_params(common_params & params, bool model_only) { | ||
| common_init_result_ptr res(new common_init_result(params, model_only)); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
--preempt-ram(orLLAMA_ARG_PREEMPT_RAM) is set to-2or another unsupported negative value, this parser accepts it andpreempt_ram_budget()treats every negative value asSIZE_MAX. A typo therefore silently removes the advertised RAM limit and can let parked states consume memory until allocation failure; reject values below the documented-1sentinel.Useful? React with 👍 / 👎.