@@ -1009,7 +1009,10 @@ struct server_slot : server_adaptive_dm_state {
10091009 generated_token_probs.push_back (token);
10101010 }
10111011
1012- int get_n_draft_max (const common_params & global_params, bool advance_adaptive_probe = false ) {
1012+ int get_n_draft_max (
1013+ const common_params & global_params,
1014+ bool advance_adaptive_probe = false ,
1015+ int dflash_cohort_n_max = -1 ) {
10131016 GGML_ASSERT (task);
10141017
10151018 if (!can_speculate ()) {
@@ -1033,43 +1036,58 @@ struct server_slot : server_adaptive_dm_state {
10331036 : configured_base_n_max;
10341037 const int probe_n_max = server_adaptive_dm_probe_n_max (base_n_max, dm_probe_fraction);
10351038 int n_draft_max = (dm_adaptive && adaptive_n_max >= 0 ) ? std::min ((int ) adaptive_n_max, base_n_max) : base_n_max;
1039+ const bool use_dflash_cohort_n_max =
1040+ global_params.speculative .type () == COMMON_SPECULATIVE_TYPE_DFLASH &&
1041+ global_params.speculative .branch_budget == 0 &&
1042+ dflash_cohort_n_max > 0 ;
10361043
10371044 if (use_profit_controller) {
1038- if (profit_baseline_probe_pending) {
1045+ if (profit_baseline_probe_pending && use_dflash_cohort_n_max) {
1046+ n_draft_max = dflash_cohort_n_max;
1047+ } else if (profit_baseline_probe_pending) {
10391048 return 0 ;
1040- }
1041-
1042- if (profit_should_probe_baseline ()) {
1049+ } else if ( profit_should_probe_baseline () && use_dflash_cohort_n_max) {
1050+ n_draft_max = dflash_cohort_n_max;
1051+ } else if (profit_should_probe_baseline ()) {
10431052 if (advance_adaptive_probe) {
10441053 profit_mark_baseline_probe ();
10451054 }
10461055 return 0 ;
1047- }
1048-
1049- if (adaptive_n_max < 0 ) {
1056+ } else if (adaptive_n_max < 0 ) {
10501057 if (advance_adaptive_probe) {
10511058 adaptive_n_max = 0 ;
10521059 }
1053- return 0 ;
1054- } else if (adaptive_n_max == 0 ) {
1055- if (!profit_baseline_ready ()) {
1060+ if (!use_dflash_cohort_n_max) {
10561061 return 0 ;
10571062 }
1058- const bool probe_now = adaptive_probe_counter + 1 >= profit_off_probe_interval ();
1059- if (!probe_now) {
1060- if (advance_adaptive_probe) {
1061- adaptive_probe_counter++;
1063+ n_draft_max = dflash_cohort_n_max;
1064+ } else if (adaptive_n_max == 0 ) {
1065+ if (!profit_baseline_ready ()) {
1066+ if (!use_dflash_cohort_n_max) {
1067+ return 0 ;
1068+ }
1069+ n_draft_max = dflash_cohort_n_max;
1070+ } else {
1071+ const bool probe_now = adaptive_probe_counter + 1 >= profit_off_probe_interval ();
1072+ if (!probe_now) {
1073+ if (advance_adaptive_probe) {
1074+ adaptive_probe_counter++;
1075+ }
1076+ if (!use_dflash_cohort_n_max) {
1077+ return 0 ;
1078+ }
1079+ n_draft_max = dflash_cohort_n_max;
1080+ } else {
1081+ const int profit_probe_n_max = [&]() {
1082+ return profit_next_off_probe_depth (base_n_max, probe_n_max);
1083+ }();
1084+ if (advance_adaptive_probe) {
1085+ adaptive_probe_counter = 0 ;
1086+ adaptive_n_max = profit_probe_n_max;
1087+ }
1088+ n_draft_max = profit_probe_n_max;
10621089 }
1063- return 0 ;
1064- }
1065- const int profit_probe_n_max = [&]() {
1066- return profit_next_off_probe_depth (base_n_max, probe_n_max);
1067- }();
1068- if (advance_adaptive_probe) {
1069- adaptive_probe_counter = 0 ;
1070- adaptive_n_max = profit_probe_n_max;
10711090 }
1072- n_draft_max = profit_probe_n_max;
10731091 } else {
10741092 n_draft_max = std::min ((int ) adaptive_n_max, base_n_max);
10751093 if (advance_adaptive_probe) {
@@ -1115,6 +1133,10 @@ struct server_slot : server_adaptive_dm_state {
11151133 }
11161134 }
11171135
1136+ if (use_dflash_cohort_n_max && n_draft_max > 0 ) {
1137+ n_draft_max = std::min (n_draft_max, dflash_cohort_n_max);
1138+ }
1139+
11181140 // determine the max draft that fits the current slot state
11191141 // note: slot.prompt is not yet expanded with the `id` token sampled above
11201142 // also, need to leave space for 1 extra token to allow context shifts
@@ -4384,8 +4406,8 @@ struct server_context_impl {
43844406 }
43854407 };
43864408 int n_slots_drafted = 0 ;
4387- server_slot * profit_baseline_slot = nullptr ;
43884409 int n_profit_baseline_slots = 0 ;
4410+ std::vector<server_slot *> profit_baseline_slots;
43894411 server_slot * dflash_cycle_slot = nullptr ;
43904412 int dflash_cycle_n_draft = 0 ;
43914413 int dflash_cycle_n_accept = 0 ;
@@ -4405,12 +4427,53 @@ struct server_context_impl {
44054427 std::any_of (slots.begin (), slots.end (), [](const server_slot & slot) {
44064428 return slot.state == SLOT_STATE_STARTED || slot.state == SLOT_STATE_PROCESSING_PROMPT ;
44074429 });
4430+ std::vector<int > dflash_cohort_n_draft_max (slots.size (), -1 );
4431+ if (params_base.speculative .type () == COMMON_SPECULATIVE_TYPE_DFLASH &&
4432+ params_base.speculative .branch_budget == 0 &&
4433+ !dflash_recurrent_has_pending_prompt) {
4434+ std::vector<int > normal_n_max (slots.size (), 0 );
4435+ std::vector<int > cohort_cap_n_max (slots.size (), 0 );
4436+
4437+ for (auto & slot : slots) {
4438+ if (slot.state != SLOT_STATE_GENERATING ||
4439+ !slot.can_speculate () ||
4440+ slot.id < 0 ||
4441+ slot.id >= (int ) slots.size ()) {
4442+ continue ;
4443+ }
4444+
4445+ normal_n_max[slot.id ] = slot.get_n_draft_max (params_base, false );
4446+ cohort_cap_n_max[slot.id ] = normal_n_max[slot.id ] > 0
4447+ ? normal_n_max[slot.id ]
4448+ : slot.get_n_draft_max (params_base, false , INT_MAX );
4449+ }
4450+
4451+ const int cohort_n_max = server_adaptive_dm_resolve_cohort_n_max (
4452+ normal_n_max.data (), cohort_cap_n_max.data (), (int ) slots.size ());
4453+ if (cohort_n_max > 0 ) {
4454+ for (auto & slot : slots) {
4455+ if (slot.id < 0 || slot.id >= (int ) slots.size () ||
4456+ cohort_cap_n_max[slot.id ] <= 0 ) {
4457+ continue ;
4458+ }
4459+ dflash_cohort_n_draft_max[slot.id ] = std::min (cohort_n_max, cohort_cap_n_max[slot.id ]);
4460+ }
4461+ }
4462+ }
4463+ auto dflash_scheduler_n_draft_max = [&](server_slot & slot, bool advance_adaptive_probe) {
4464+ if (slot.id >= 0 && slot.id < (int ) dflash_cohort_n_draft_max.size () &&
4465+ dflash_cohort_n_draft_max[slot.id ] > 0 ) {
4466+ return slot.get_n_draft_max (
4467+ params_base, advance_adaptive_probe, dflash_cohort_n_draft_max[slot.id ]);
4468+ }
4469+ return slot.get_n_draft_max (params_base, advance_adaptive_probe);
4470+ };
44084471 if (ctx_dft_shared) {
44094472 int n_drafting = 0 ;
44104473 for (auto & slot : slots) {
44114474 if (slot.state == SLOT_STATE_GENERATING &&
44124475 slot.can_speculate () &&
4413- slot. get_n_draft_max (params_base , false ) > 0 &&
4476+ dflash_scheduler_n_draft_max (slot , false ) > 0 &&
44144477 !dflash_recurrent_has_pending_prompt) {
44154478 n_drafting++;
44164479 }
@@ -4427,7 +4490,7 @@ struct server_context_impl {
44274490 for (auto & slot : slots) {
44284491 if (slot.state == SLOT_STATE_GENERATING &&
44294492 slot.can_speculate () &&
4430- slot. get_n_draft_max (params_base , false ) > 0 &&
4493+ dflash_scheduler_n_draft_max (slot , false ) > 0 &&
44314494 !dflash_recurrent_has_pending_prompt) {
44324495 batch_specs.push_back (slot.get_spec ());
44334496 batch_id_lasts.push_back (slot.sampled );
@@ -4437,10 +4500,21 @@ struct server_context_impl {
44374500
44384501 std::vector<llama_tokens> batch_results;
44394502 std::vector<std::vector<float >> batch_log_probs;
4503+ common_params_speculative params_batch = params_base.speculative ;
4504+ int batch_cohort_n_max = INT_MAX ;
4505+ for (const int slot_id : batch_slot_ids) {
4506+ if (slot_id >= 0 && slot_id < (int ) dflash_cohort_n_draft_max.size () &&
4507+ dflash_cohort_n_draft_max[slot_id] > 0 ) {
4508+ batch_cohort_n_max = std::min (batch_cohort_n_max, dflash_cohort_n_draft_max[slot_id]);
4509+ }
4510+ }
4511+ if (batch_cohort_n_max != INT_MAX ) {
4512+ params_batch.n_max = std::min (params_batch.n_max , batch_cohort_n_max);
4513+ }
44404514 const int64_t t_batch_start = ggml_time_us ();
44414515 common_speculative_draft_batch (
44424516 batch_specs, ctx_dft_shared.get (),
4443- params_base. speculative , batch_id_lasts, batch_results,
4517+ params_batch , batch_id_lasts, batch_results,
44444518 use_rejection_sampling ? &batch_log_probs : nullptr );
44454519 t_draft_total = ggml_time_us () - t_batch_start;
44464520 llama_set_dflash_n_slots (ctx_dft_shared.get (), 1 );
@@ -4475,7 +4549,7 @@ struct server_context_impl {
44754549 continue ;
44764550 }
44774551
4478- int n_draft_max = slot. get_n_draft_max (params_base , true );
4552+ int n_draft_max = dflash_scheduler_n_draft_max (slot , true );
44794553 if (params_base.speculative .type () == COMMON_SPECULATIVE_TYPE_DFLASH &&
44804554 params_base.speculative .branch_budget == 0 ) {
44814555 n_draft_max = dflash_flat_effective_draft_max (ctx_dft_shared.get (), n_draft_max);
@@ -4757,8 +4831,8 @@ struct server_context_impl {
47574831 if (slot.can_speculate () && slot.dm_adaptive &&
47584832 server_adaptive_dm_uses_profit_controller (slot.dm_controller ) &&
47594833 slot.profit_expects_baseline_sample ()) {
4760- profit_baseline_slot = &slot;
47614834 n_profit_baseline_slots++;
4835+ profit_baseline_slots.push_back (&slot);
47624836 }
47634837
47644838 slot.i_batch = batch.n_tokens ;
@@ -4775,7 +4849,9 @@ struct server_context_impl {
47754849 // --- MTP post-loop phases (upstream-aligned) ---
47764850 // Phase 2: single draft call for all collected MTP slots
47774851 if (!mtp_drafting.empty ()) {
4852+ const int64_t t_mtp_draft_start = ggml_time_us ();
47784853 common_speculative_draft (spec.get ());
4854+ t_draft_total += ggml_time_us () - t_mtp_draft_start;
47794855 }
47804856
47814857 // Phase 3: checkpoint creation and batch building for MTP slots
@@ -7042,19 +7118,24 @@ struct server_context_impl {
70427118 }
70437119 llama_set_dflash_consume_reduced (ctx_tgt, false );
70447120
7045- if (pure_tg_cycle && n_slots_drafted == 0 && n_profit_baseline_slots == 1 && n_tg_tokens == 1 &&
7046- profit_baseline_slot && profit_baseline_slot->task &&
7047- profit_baseline_slot->state == SLOT_STATE_GENERATING ) {
7121+ if (pure_tg_cycle && n_slots_drafted == 0 && n_profit_baseline_slots > 0 ) {
70487122 const int64_t t_cycle_total_now = ggml_time_us () - t_cycle_start;
7049- profit_baseline_slot->observe_profit_timing (
7050- 0 ,
7051- 0 ,
7052- 0 ,
7053- t_draft_total / 1000 .0f ,
7054- t_verify_total / 1000 .0f ,
7055- t_accept_total / 1000 .0f ,
7056- t_cycle_total_now / 1000 .0f );
7057- apply_profit_decision (*profit_baseline_slot);
7123+ const float baseline_shared_scale = n_tg_tokens > 1 ? 1 .0f / (float ) n_tg_tokens : 1 .0f ;
7124+ for (server_slot * slot_ptr : profit_baseline_slots) {
7125+ if (!slot_ptr || !slot_ptr->task || slot_ptr->state != SLOT_STATE_GENERATING ) {
7126+ continue ;
7127+ }
7128+
7129+ slot_ptr->observe_profit_timing (
7130+ 0 ,
7131+ 0 ,
7132+ 0 ,
7133+ (t_draft_total / 1000 .0f ) * baseline_shared_scale,
7134+ (t_verify_total / 1000 .0f ) * baseline_shared_scale,
7135+ (t_accept_total / 1000 .0f ) * baseline_shared_scale,
7136+ (t_cycle_total_now / 1000 .0f ) * baseline_shared_scale);
7137+ apply_profit_decision (*slot_ptr);
7138+ }
70587139 }
70597140
70607141 // --- profiling: log per-cycle breakdown ---
0 commit comments