use crate::common::error::AicError;
use crate::operators::base::PerformanceResult;
use crate::operators::{Op, RuntimeContext};
use crate::perf_database::PerfDatabase;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ContextOpFilter {
All,
SkipContextAttention,
OnlyContextAttention,
}
pub(crate) fn run_context_ops(
ops: &[Op],
db: &PerfDatabase,
batch_size: u32,
effective_isl: u32,
prefix: u32,
seq_imbalance_correction_scale: f64,
filter: ContextOpFilter,
) -> Result<f64, AicError> {
run_context_ops_with(
ops,
db,
batch_size,
effective_isl,
prefix,
seq_imbalance_correction_scale,
filter,
|_, _| {},
)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn run_context_ops_with<'a>(
ops: &'a [Op],
db: &PerfDatabase,
batch_size: u32,
effective_isl: u32,
prefix: u32,
seq_imbalance_correction_scale: f64,
filter: ContextOpFilter,
mut on_op: impl FnMut(&'a Op, PerformanceResult),
) -> Result<f64, AicError> {
let mut total = 0.0_f64;
for op in ops {
match filter {
ContextOpFilter::All => {}
ContextOpFilter::SkipContextAttention if op.is_context_attention() => continue,
ContextOpFilter::OnlyContextAttention if !op.is_context_attention() => continue,
_ => {}
}
let result = query_context_op(
op,
db,
batch_size,
effective_isl,
prefix,
seq_imbalance_correction_scale,
None,
)?;
total += result.latency_ms;
on_op(op, result);
}
Ok(total)
}
pub(crate) fn query_context_op(
op: &Op,
db: &PerfDatabase,
batch_size: u32,
effective_isl: u32,
prefix: u32,
seq_imbalance_correction_scale: f64,
x_override: Option<u32>,
) -> Result<PerformanceResult, AicError> {
let x = x_override.unwrap_or(if op.is_logits_gemm() {
batch_size
} else {
batch_size * effective_isl
});
let ctx = RuntimeContext {
batch_size,
beam_width: 1,
s: effective_isl,
prefix,
num_tokens: x,
seq_imbalance_correction_scale,
gen_seq_imbalance_correction_scale: 1.0,
num_image_tokens: 0,
};
op.query(db, &ctx)
}
pub(crate) fn run_generation_ops_step(
ops: &[Op],
db: &PerfDatabase,
batch_size: u32,
kv_seq_tokens: u32,
gen_seq_imbalance_correction_scale: f64,
only_generation_attention: bool,
) -> Result<f64, AicError> {
run_generation_ops_step_beamed(
ops,
db,
batch_size,
1,
kv_seq_tokens,
gen_seq_imbalance_correction_scale,
only_generation_attention,
)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn run_generation_ops_step_beamed(
ops: &[Op],
db: &PerfDatabase,
batch_size: u32,
beam_width: u32,
kv_seq_tokens: u32,
gen_seq_imbalance_correction_scale: f64,
only_generation_attention: bool,
) -> Result<f64, AicError> {
run_generation_ops_step_beamed_with(
ops,
db,
batch_size,
beam_width,
kv_seq_tokens,
gen_seq_imbalance_correction_scale,
only_generation_attention,
|_, _| {},
)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn run_generation_ops_step_beamed_with<'a>(
ops: &'a [Op],
db: &PerfDatabase,
batch_size: u32,
beam_width: u32,
kv_seq_tokens: u32,
gen_seq_imbalance_correction_scale: f64,
only_generation_attention: bool,
mut on_op: impl FnMut(&'a Op, PerformanceResult),
) -> Result<f64, AicError> {
let mut total = 0.0_f64;
for op in ops {
if only_generation_attention && !op.is_generation_attention() {
continue;
}
let result = query_generation_op(
op,
db,
batch_size,
beam_width,
kv_seq_tokens,
gen_seq_imbalance_correction_scale,
0,
None,
)?;
total += result.latency_ms;
on_op(op, result);
}
Ok(total)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn query_generation_op(
op: &Op,
db: &PerfDatabase,
batch_size: u32,
beam_width: u32,
kv_seq_tokens: u32,
gen_seq_imbalance_correction_scale: f64,
prefix: u32,
x_override: Option<u32>,
) -> Result<PerformanceResult, AicError> {
let ctx = RuntimeContext {
batch_size,
beam_width: beam_width.max(1),
s: kv_seq_tokens,
prefix,
num_tokens: x_override.unwrap_or(batch_size.saturating_mul(beam_width.max(1))),
seq_imbalance_correction_scale: 1.0,
gen_seq_imbalance_correction_scale,
num_image_tokens: 0,
};
op.query(db, &ctx)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn get_mix_step_ops(
context_ops: &[Op],
generation_ops: &[Op],
db: &PerfDatabase,
ctx_tokens: u32,
gen_tokens: u32,
new_tokens_per_prefill_req: u32,
prefix_per_req: u32,
combined_prefix: u32,
kv_per_decode_req: u32,
decode_batch: u32,
) -> Result<f64, AicError> {
let effective_isl_combined = (ctx_tokens + gen_tokens).max(1);
let mut total = 0.0_f64;
for op in context_ops {
if op.is_context_attention() {
continue;
}
let x = if op.is_logits_gemm() {
1
} else {
effective_isl_combined
};
let ctx = RuntimeContext {
batch_size: 1,
beam_width: 1,
s: effective_isl_combined,
prefix: combined_prefix,
num_tokens: x,
seq_imbalance_correction_scale: 1.0,
gen_seq_imbalance_correction_scale: 1.0,
num_image_tokens: 0,
};
total += op.query(db, &ctx)?.latency_ms;
}
let isl_eff_pass2 = new_tokens_per_prefill_req.max(1);
let ctx_attn_batch = ((ctx_tokens + isl_eff_pass2 - 1) / isl_eff_pass2).max(1);
let scale_factor = ((isl_eff_pass2 + ctx_tokens - 1) / ctx_tokens.max(1)).max(1) as f64;
for op in context_ops {
if !op.is_context_attention() {
continue;
}
let ctx = RuntimeContext {
batch_size: ctx_attn_batch,
beam_width: 1,
s: isl_eff_pass2,
prefix: prefix_per_req,
num_tokens: ctx_tokens,
seq_imbalance_correction_scale: 1.0,
gen_seq_imbalance_correction_scale: 1.0,
num_image_tokens: 0,
};
total += op.query(db, &ctx)?.latency_ms / scale_factor;
}
if decode_batch > 0 {
for op in generation_ops {
if !op.is_generation_attention() {
continue;
}
let ctx = RuntimeContext {
batch_size: decode_batch,
beam_width: 1,
s: kv_per_decode_req.max(1),
prefix: 0,
num_tokens: decode_batch,
seq_imbalance_correction_scale: 1.0,
gen_seq_imbalance_correction_scale: 1.0,
num_image_tokens: 0,
};
total += op.query(db, &ctx)?.latency_ms;
}
}
Ok(total)
}