Repository navigation
feat(router): add prefix_affinity_weight and report non-max-overlap picks in aggregated mode #16986
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: main
Are you sure you want to change the base?
Changes from 1 commit
a09612e
5fe92ff
5e1ab89
7ba06aa
360c655
ad909ff
cb346a7
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 |
|---|---|---|
|
|
@@ -34,6 +34,7 @@ struct DefaultScorer<const REQUEST_COST: bool, const SHARED_CREDIT: bool> { | |
| shared_cache_multiplier: f64, | ||
| decode_active_request_weight: f64, | ||
| prefill_load_scale: f64, | ||
| prefix_affinity_weight: f64, | ||
| is_decode: bool, | ||
| prepared: PreparedRequest, | ||
| } | ||
|
|
@@ -94,6 +95,7 @@ impl<const REQUEST_COST: bool, const SHARED_CREDIT: bool> | |
| shared_cache_multiplier: config.shared_cache_multiplier, | ||
| decode_active_request_weight: config.decode_active_request_weight, | ||
| prefill_load_scale: config.prefill_load_scale, | ||
| prefix_affinity_weight: config.prefix_affinity_weight, | ||
| is_decode: worker_label == "decode", | ||
| prepared: PreparedRequest::default(), | ||
| } | ||
|
|
@@ -139,7 +141,7 @@ impl<const REQUEST_COST: bool, const SHARED_CREDIT: bool> | |
| let load = candidate | ||
| .load() | ||
| .ok_or_else(|| WorkerSelectionPolicyError::failed("load input unavailable"))?; | ||
| let (cached_tokens, credit) = if let Some(cache) = candidate.cache() { | ||
| let (cached_tokens, credit, device_blocks) = if let Some(cache) = candidate.cache() { | ||
| let (estimated_overlap, cached_tokens) = cache.accounting_cache_estimate(); | ||
| let device = if cache.has_tier_matches() { | ||
| cache.device_overlap_blocks() | ||
|
|
@@ -169,9 +171,9 @@ impl<const REQUEST_COST: bool, const SHARED_CREDIT: bool> | |
| + self.host_cache_hit_weight * cache.host_overlap_blocks() | ||
| + self.disk_cache_hit_weight * cache.disk_overlap_blocks() | ||
| + shared_credit; | ||
| (cached_tokens, credit) | ||
| (cached_tokens, credit, device) | ||
| } else { | ||
| (0, 0.0) | ||
| (0, 0.0, 0.0) | ||
| }; | ||
| let request_cost = if REQUEST_COST { | ||
| self.decode_active_request_weight * load.active_requests() as f64 | ||
|
|
@@ -190,7 +192,17 @@ impl<const REQUEST_COST: bool, const SHARED_CREDIT: bool> | |
| context.prompt_tokens() | ||
| }; | ||
| let prefill = (self.prepared.block_size.divide(raw_tokens as f64) - credit).max(0.0); | ||
| self.prefill_load_scale * prefill + load.decode_cost_blocks() + request_cost | ||
| let logit = | ||
| self.prefill_load_scale * prefill + load.decode_cost_blocks() + request_cost; | ||
| if self.prefix_affinity_weight != 0.0 { | ||
| // Charge each prompt block this worker lacks. The overlap credit above is | ||
| // capped at the request's own prefill, so it cannot outweigh transient | ||
| // decode load on the worker that already holds a resumed prefix. | ||
| let missing = (self.prepared.request_blocks.value - device_blocks).max(0.0); | ||
|
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.
🤖 AI FixTrack the unclamped request-block count separately and use it for missing-prefix penalties in both plugin and reference scorers, adding a zero-block regression case with differing preferred-taint multipliers.
Contributor
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. same fix as above, in 5e1ab89. |
||
| logit + self.prefix_affinity_weight * missing | ||
|
coderabbitai[bot] marked this conversation as resolved.
Resolved
|
||
| } else { | ||
| logit | ||
| } | ||
| }; | ||
| let cost = logit * candidate.preferred_taint_multiplier().unwrap_or(1.0); | ||
| Ok(cost) | ||
|
|
@@ -203,6 +215,7 @@ impl<const REQUEST_COST: bool, const SHARED_CREDIT: bool> WorkerScorer | |
| fn required_worker_inputs(&self) -> WorkerInputs { | ||
| let inputs = WorkerInputs::LOAD | WorkerInputs::PREFERRED_TAINT; | ||
| if self.overlap_score_credit != 0.0 | ||
| || self.prefix_affinity_weight != 0.0 | ||
| || self.host_cache_hit_weight != 0.0 | ||
| || self.disk_cache_hit_weight != 0.0 | ||
| || self.shared_cache_multiplier != 0.0 | ||
|
|
||
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.
The affinity calculation uses
request_blocks.value, butpreparestorescontext.request_blocks().max(1)there solely to avoid a divide-by-zero in overlap decay. An empty/embeddings-only request therefore acquires one fictitious missing prefix block on every worker. Because this added cost is subsequently multiplied by each worker's preferred-taint multiplier, it is not merely a common offset and can change which worker is selected even though the request has no prefix. The reference scorer repeats the same clamp. Preserve the real (possibly zero) prompt-block count for this penalty and keep the clamped value only for normalization.🤖 AI Fix
Track the unclamped request block count separately and use it for missing-prefix penalties in both plugin and reference scorers, with a regression test for a zero-block request and differing preferred-taint multipliers.
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.
good catch, fixed in 5e1ab89. the penalty now uses the unclamped prompt block count in both the plugin scorer and the reference scorer, so an empty prompt charges nothing; the .max(1) clamp stays only on the divisor. added
prefix_affinity_weight_charges_nothing_for_empty_prompt, which uses two workers with different preferred-taint multipliers and checks plugin and reference agree and pick the lower cost worker. it fails on the old code.