use super::types::ActiveSeq;
const BUCKETS: [f32; 4] = [0.25, 0.5, 0.75, 1.0];
pub(super) const VERIFY_ROW_BUDGET: usize = metrale_gpu_runtime::buffers::VERIFY_ROW_CAP;
pub(super) const WIDTH_CAP: usize = metrale_model_layers::layer::VERIFY_WY_TABLE_SEQS;
pub(super) const DCUT_WIDTH_CAP_DEFAULT: usize = 8;
pub(crate) fn dcut_ratio_resolved(cli: Option<f32>) -> f32 {
resolve_dcut_ratio(cli, std::env::var("METRALE_MTP_DCUT_RATIO").ok().as_deref())
}
pub(super) fn resolve_dcut_ratio(cli: Option<f32>, env: Option<&str>) -> f32 {
snap_ratio(
cli.or_else(|| env.and_then(|v| v.parse::<f32>().ok()))
.unwrap_or(0.75),
)
}
pub(super) fn snap_ratio(raw: f32) -> f32 {
let raw = raw.clamp(0.0, 1.0);
{
*BUCKETS
.iter()
.min_by(|a, b| {
(*a - raw)
.abs()
.partial_cmp(&(*b - raw).abs())
.unwrap_or(std::cmp::Ordering::Equal)
})
.expect("BUCKETS is non-empty")
}
}
pub(super) fn select(
confs: &[&[f32]],
k_drafts: usize,
row_budget: usize,
ratio: f32,
) -> Vec<usize> {
let n = confs.len();
let mut retained = vec![1usize.min(k_drafts); n];
if k_drafts <= 1 || n == 0 {
return retained;
}
let prunable = n * (k_drafts - 1);
let mut ranked: Vec<(f32, usize, usize)> = Vec::with_capacity(prunable);
for (i, c) in confs.iter().enumerate() {
let mut acc = 0.0f32;
for j in 0..k_drafts {
acc += c.get(j).copied().unwrap_or(0.0);
if j >= 1 {
ranked.push((acc, i, j));
}
}
}
ranked.sort_by(|a, b| {
b.0.partial_cmp(&a.0)
.unwrap_or(std::cmp::Ordering::Equal)
.then(a.1.cmp(&b.1))
.then(a.2.cmp(&b.2))
});
let by_ratio = ((prunable as f32) * ratio).round() as usize;
let committed = 2 * n;
let by_budget = row_budget.saturating_sub(committed);
let keep = by_ratio.min(by_budget).min(prunable);
for &(_, i, j) in ranked.iter().take(keep) {
retained[i] = retained[i].max(j + 1);
}
retained
}
pub(super) fn plan(
sched: &crate::scheduler::sched_ctx::SchedCtx,
active: &mut [ActiveSeq],
batchable: &mut Vec<usize>,
ladder_nd: usize,
rows: usize,
) -> Vec<usize> {
plan_with_stop(
sched,
active,
batchable,
ladder_nd,
rows,
metrale_model_layers::speculative::draft_stop::draft_stop_rule(),
)
}
pub(super) fn plan_with_stop(
sched: &crate::scheduler::sched_ctx::SchedCtx,
active: &mut [ActiveSeq],
batchable: &mut Vec<usize>,
ladder_nd: usize,
rows: usize,
stop: Option<metrale_model_layers::speculative::draft_stop::StopRule>,
) -> Vec<usize> {
let mut ks: Vec<usize> = vec![rows; batchable.len()];
let measured = sched.levers.spec_cost.as_ref();
let dcut_on = measured.is_none()
&& sched.levers.dcut_enabled
&& ladder_nd >= 2
&& !batchable.is_empty()
&& batchable.len() <= sched.levers.dcut_width_cap;
let stop_on = measured.is_none() && stop.is_some() && ladder_nd >= 2 && !batchable.is_empty();
let measured_on = measured.is_some() && ladder_nd >= 1 && !batchable.is_empty();
if !dcut_on && !stop_on && !measured_on {
return ks;
}
let confs: Vec<&[f32]> = batchable
.iter()
.map(|&i| {
let a = &active[i];
if a.pending_draft_conf.len() == a.pending_drafts.len() {
a.pending_draft_conf.as_slice()
} else {
&[]
}
})
.collect();
let held: Vec<usize> = batchable
.iter()
.map(|&i| active[i].pending_drafts.len())
.collect();
let retained = if dcut_on {
select(
&confs,
ladder_nd,
VERIFY_ROW_BUDGET,
sched.levers.dcut_ratio,
)
} else if let Some(sc) = measured {
metrale_speculative::spec_cost::plan::sequence_depths(
&sc.table,
&sc.calibration,
&confs,
&held,
ladder_nd,
VERIFY_ROW_BUDGET,
sc.slack,
)
} else {
vec![ladder_nd; batchable.len()]
};
for (pos, r) in retained.iter().enumerate() {
let mut depth = (*r).min(held[pos]);
if let Some(rule) = stop
&& !confs[pos].is_empty()
{
depth = depth.min(rule.depth(confs[pos]));
}
ks[pos] = depth.clamp(1, ladder_nd) + 1;
}
let slots: Vec<usize> = batchable
.iter()
.map(|&idx| active[idx].seq.ssm_slot_idx().unwrap_or(usize::MAX))
.collect();
let canonical =
metrale_model_layers::speculative::verify_key::canonical_assignment(batchable.len());
let (mut order, mut ks_out) =
metrale_model_layers::speculative::verify_key::verify_batch_order(&slots, &ks, canonical);
if canonical && order.iter().zip(&ks_out).any(|(&p, &k)| k - 1 > held[p]) {
(order, ks_out) =
metrale_model_layers::speculative::verify_key::verify_batch_order(&slots, &ks, false);
}
let reordered: Vec<usize> = order.iter().map(|&p| batchable[p]).collect();
for (idx, &k) in reordered.iter().zip(&ks_out) {
let a = &mut active[*idx];
debug_assert!(
k >= 2 && k - 1 <= a.pending_drafts.len(),
"assigned depth {k} exceeds the {} drafts proposed",
a.pending_drafts.len()
);
a.pending_drafts.truncate(k - 1);
a.pending_draft_conf.truncate(k - 1);
}
if dcut_on {
sched.dcut.record(
sched.levers.mtp_accept_debug,
sched.levers.dcut_ratio,
batchable.len() * rows,
ks_out.iter().sum(),
&ks_out,
);
}
*batchable = reordered;
ks_out
}
pub(super) fn chunk_ranges(ks: &[usize]) -> Vec<(usize, usize)> {
let mut out = Vec::new();
let mut lo = 0usize;
while lo < ks.len() {
let seq_cap = (VERIFY_ROW_BUDGET / ks[lo].max(1)).min(WIDTH_CAP);
let mut hi = lo;
let mut r = 0usize;
while hi < ks.len() && hi - lo < seq_cap && r + ks[hi] <= VERIFY_ROW_BUDGET {
r += ks[hi];
hi += 1;
}
if hi == lo {
hi = lo + 1;
}
out.push((lo, hi));
lo = hi;
}
out
}
#[derive(Debug, Default)]
pub struct DcutTelemetry {
steps: std::cell::Cell<u64>,
full: std::cell::Cell<u64>,
kept: std::cell::Cell<u64>,
}
impl DcutTelemetry {
fn record(&self, debug: bool, ratio: f32, rows_full: usize, rows_kept: usize, ks: &[usize]) {
const PERIOD: u64 = 200;
if !debug {
return;
}
self.full.set(self.full.get() + rows_full as u64);
self.kept.set(self.kept.get() + rows_kept as u64);
let steps = self.steps.get() + 1;
self.steps.set(steps);
if steps >= PERIOD {
let steps = self.steps.replace(0).max(1);
let full = self.full.replace(0).max(1);
let kept = self.kept.replace(0);
tracing::info!(
"MTP D-Cut ratio={ratio:.2} steps={steps} rows_full={full} rows_kept={kept} \
kept_frac={:.3} last_ks={ks:?}",
kept as f64 / full as f64,
);
}
}
}
pub(super) fn reached_outcomes(
conf: &[f32],
accepted: usize,
) -> impl Iterator<Item = (f32, bool)> + '_ {
conf.iter()
.take(accepted.saturating_add(1))
.enumerate()
.map(move |(j, &lp)| (lp, j < accepted))
}
#[cfg(test)]
#[path = "mtp_dcut_tests.rs"]
mod tests;