1use std::collections::{BTreeMap, BTreeSet, HashMap};
10use std::fmt;
11use std::ops::Range;
12use std::sync::atomic::{AtomicBool, AtomicU64, AtomicU8, AtomicUsize, Ordering};
13use std::sync::{Arc, Weak};
14use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
15
16use ferrum_interfaces::kv_cache::{BlockTable, CacheHandleStats};
17use ferrum_interfaces::model_executor::{
18 AttentionType, DecodeInput, DecodeOutput, ExecutionResourceAuthority, ExecutorAdmissionEpochs,
19 ExecutorBatchDecodeOutcome, ExecutorBatchPrefillOutcome, ExecutorCapabilities,
20 ExecutorCapacityWaitRegistration, ExecutorExecutionCapacityDeferral,
21 ExecutorExecutionCapacityPreemption, ExecutorExecutionCapacityPreemptionAuthority,
22 ExecutorExecutionCapacityPreemptionReceipt, ExecutorExecutionCapacityStage,
23 ExecutorExecutionDeferral, ExecutorMemoryUsage, ExecutorPrefillAdmission,
24 ExecutorPrefillAdmissionDecision, ExecutorPrefillAdmissionReceipt, ExecutorPrefillCompletion,
25 ExecutorPrefillMaintenanceDeferral, ExecutorPrefillMaintenanceOutcome, ExecutorPrefillOutcome,
26 ExecutorRequestOrigin, ExecutorRequestStateDeferral, ExecutorSamplingOutput,
27 ExecutorSequenceCompletion, ExecutorState, ExecutorStatus, LogitsReturnPolicy,
28 MemoryRequirements, PlanRuntimeBatchDecodeOutcome, PlanRuntimeBatchPrefillOutcome,
29 PlanRuntimeDecodeInput, PlanRuntimeDecodeOutput, PlanRuntimePrefillAuthority,
30 PlanRuntimePrefillCompletion, PlanRuntimePrefillInput, PlanRuntimePrefillOutcome,
31 PlanRuntimePrefillOutput, PlanRuntimePrefillProduct, PlanRuntimePrefixRestoreDeferral,
32 PlanRuntimePrefixRestoreInput, PlanRuntimePrefixRestoreOutcome, PlanRuntimePrefixRestoreOutput,
33 PlanRuntimeResourceSnapshot, PrefillChunk, PrefillInput, PrefillOutput, PrefixCaptureBoundary,
34 PrefixCaptureLease, PrefixCapturePlan, PrefixCaptureRequest, TypedSequenceStateMemory,
35};
36use ferrum_interfaces::vnext::*;
37use ferrum_interfaces::{KvCacheHandle, ModelExecutor, TensorRef};
38use ferrum_types::{
39 AttentionExecutionPolicy, Device, EngineConfig, ExecutorAdmissionLimits, FerrumError,
40 ModelInfo, ObservabilityProfileDetail, ProfileEntrypoint, RequestId, Result,
41 ReusableExecutionCaptureConfig, ReusableExecutionPreparationMode, SchedulingPolicy,
42 SequenceFitPolicy, TokenId, VNextDiagnosticFault,
43 MAXIMUM_REUSABLE_EXECUTION_STARTUP_CAPTURE_WIDTH,
44};
45use parking_lot::{Mutex, RwLock};
46use serde::Serialize;
47use tokio::sync::Mutex as AsyncMutex;
48
49use crate::vnext::PreparedProductionModel;
50
51use super::{
52 common,
53 vnext_checkpoint::{
54 VNextCheckpointArtifactRecord, VNextCheckpointCapture, VNextCheckpointClaim,
55 VNextCheckpointProductOutputMode, VNextCheckpointProductOutputRecord,
56 VNextCheckpointSelection, VNextTeacherForcedDecision,
57 },
58 vnext_completion_worker::{VNextCompletionTaskKind, VNextCompletionWorker},
59 vnext_timing::{log_static_initialization_receipt, AtomicDurationMetrics, StartupPhaseTimer},
60};
61
62mod composition;
63mod determinism;
64pub use composition::{VNextCompiledModel, VNextRuntimeComposition};
65mod prefix_cache;
66mod request;
67mod reusable_catalog;
68mod state_memory;
69pub use determinism::{
70 VNextDeterminismExecutionMode, VNextDeterminismExecutionSpec, VNextDeterminismInitialState,
71 VNextDeterminismParticipantSpec, VNextDeterminismPhase, VNextDeterminismWorkspacePoison,
72 MAX_VNEXT_DETERMINISM_PARTICIPANTS,
73};
74use request::{terminalize_unsubmitted_session, VNextRequestRoot};
75
76const POLICY_ID: &str = "policy.ferrum.product.vnext.default";
77const POLICY_VERSION: ContractVersion = ContractVersion::new(3, 0);
78const UNIFORM_QUERY_REUSABLE_CLASS: &str = "execution.uniform-query-token";
79const PACKED_TOKEN_REUSABLE_CLASS: &str = "execution.single-sequence-packed-token";
80const DEFAULT_STATIC_STAGING_BYTES: u64 = 64 * 1024 * 1024;
81const DEFAULT_STATIC_COMMANDS_PER_BATCH: usize = 64;
82const DEFAULT_CANCELLATION_CHECK_INTERVAL_STEPS: u32 = 1;
83const MAX_DEFINITELY_NOT_SUBMITTED_RETRIES: u32 = 1;
84const MAX_BACKING_MAINTENANCE_ATTEMPTS: u32 = 2;
85const MAX_EXTENSION_RECHECKS: u32 = 2;
86const MAX_PROFILED_REUSABLE_EXECUTABLES: usize = 256;
87const MAX_PRODUCT_TOKEN_MASK_SLOT_CACHE_ENTRIES: usize = 1_024;
88const REUSABLE_EXECUTION_WARMUP_PASSES: u32 = 1;
89const REUSABLE_EXECUTION_CAPTURE_PASSES: u32 = 1;
90const REUSABLE_EXECUTION_REPLAY_VALIDATION_PASSES: u32 = 1;
91type VNextDriver<R> = RuntimeResourceDriver<R>;
92
93const fn submission_execution_policy_for_timing(
94 timing_mode: DeviceTimingMode,
95) -> SubmissionExecutionPolicy {
96 match timing_mode {
97 DeviceTimingMode::Verification => SubmissionExecutionPolicy::determinism_eager(0),
98 DeviceTimingMode::Off
99 | DeviceTimingMode::Completion
100 | DeviceTimingMode::Replay
101 | DeviceTimingMode::Kernel => SubmissionExecutionPolicy::adaptive(),
102 }
103}
104
105const fn reusable_catalog_lookup_allowed(
106 has_startup_plan: bool,
107 direct_reusable_execution_allowed: bool,
108 direct_reusable_execution_already_attempted: bool,
109) -> bool {
110 has_startup_plan
111 && direct_reusable_execution_allowed
112 && !direct_reusable_execution_already_attempted
113}
114
115const fn reusable_program_identity_required(
116 has_startup_plan: bool,
117 catalog_installed: bool,
118 direct_reusable_execution_allowed: bool,
119 direct_reusable_execution_already_attempted: bool,
120) -> bool {
121 has_startup_plan
122 && (!catalog_installed
123 || reusable_catalog_lookup_allowed(
124 true,
125 direct_reusable_execution_allowed,
126 direct_reusable_execution_already_attempted,
127 ))
128}
129
130const fn resolved_sequence_fit_policy(policy: SequenceFitPolicy) -> AdmissionFitPolicy {
131 match policy {
132 SequenceFitPolicy::FullInputMustFit => AdmissionFitPolicy::FullInputMustFit,
133 SequenceFitPolicy::ImmediateOnly => AdmissionFitPolicy::ImmediateOnly,
134 }
135}
136
137fn resolve_runtime_attention_authority(
138 requested: AttentionExecutionPolicy,
139 native_adaptive_supported: bool,
140 installed: AttentionExecutionPolicy,
141) -> Result<AttentionExecutionPolicy> {
142 let requested = requested
143 .resolve(native_adaptive_supported)
144 .map_err(|reason| {
145 FerrumError::config(format!(
146 "invalid vNext attention execution policy: {reason}"
147 ))
148 })?;
149 if !installed.is_resolved() {
150 return Err(FerrumError::config(
151 "vNext runtime exposed unresolved auto attention policy",
152 ));
153 }
154 if requested != installed {
155 return Err(FerrumError::config(format!(
156 "vNext attention policy authority mismatch: product configuration resolves to {}, but the runtime composition installed {}",
157 requested.as_runtime_value(),
158 installed.as_runtime_value(),
159 )));
160 }
161 Ok(installed)
162}
163
164#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
165#[serde(rename_all = "snake_case")]
166enum VNextReusableExecutionDecodeWidthSource {
167 Automatic,
168 Explicit,
169}
170
171#[derive(Debug, Clone, Copy, PartialEq, Eq)]
172enum VNextPrefillFrontierPolicy {
173 Adaptive,
174 ExactStartup,
175}
176
177#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
178struct VNextReusableExecutionCaptureResolution {
179 source: VNextReusableExecutionDecodeWidthSource,
180 admission_maximum_decode_width: usize,
181 requested_decode_widths: Vec<usize>,
182 effective_decode_widths: Vec<usize>,
183 reduction_reason: Option<String>,
184}
185
186#[derive(Debug, Clone)]
187struct VNextReusableExecutionPolicyResolution {
188 policy: ReusableExecutionPolicy,
189 capture: Option<VNextReusableExecutionCaptureResolution>,
190}
191
192fn reusable_execution_anchor_decode_widths(maximum_width: usize) -> Vec<usize> {
193 let mut widths = Vec::new();
194 let mut width = 1_usize;
195 while width < maximum_width {
196 widths.push(width);
197 width = width
198 .checked_mul(2)
199 .unwrap_or(maximum_width)
200 .min(maximum_width);
201 }
202 if widths.last().copied() != Some(maximum_width) {
203 widths.push(maximum_width);
204 }
205 widths
206}
207
208fn reusable_execution_maximum_decode_sequence_tokens(decode_width_count: usize) -> Result<usize> {
209 let passes_per_width = usize::try_from(
210 REUSABLE_EXECUTION_WARMUP_PASSES
211 .checked_add(REUSABLE_EXECUTION_CAPTURE_PASSES)
212 .and_then(|passes| passes.checked_add(REUSABLE_EXECUTION_REPLAY_VALIDATION_PASSES))
213 .ok_or_else(|| FerrumError::config("vNext reusable startup pass count overflowed"))?,
214 )
215 .map_err(|_| FerrumError::config("vNext reusable startup pass count exceeds usize"))?;
216 decode_width_count
217 .checked_mul(passes_per_width)
218 .and_then(|decode_tokens| decode_tokens.checked_add(1))
219 .ok_or_else(|| FerrumError::config("vNext reusable startup token ceiling overflowed"))
220}
221
222fn resolve_reusable_execution_policy(
223 maximum_active_sequences: u32,
224 maximum_scheduled_tokens: u64,
225 maximum_model_tokens: usize,
226 prefill_chunks: &[PrefillChunk],
227 capture_config: &ReusableExecutionCaptureConfig,
228 prepare_device_programs: bool,
229) -> Result<VNextReusableExecutionPolicyResolution> {
230 let maximum_active_sequences = usize::try_from(maximum_active_sequences)
231 .map_err(|_| FerrumError::config("vNext active sequence limit exceeds usize"))?;
232 let maximum_scheduled_tokens = usize::try_from(maximum_scheduled_tokens)
233 .map_err(|_| FerrumError::config("vNext scheduled token limit exceeds usize"))?;
234 let maximum_width = maximum_active_sequences.min(maximum_scheduled_tokens);
235 if maximum_width == 0 {
236 return Err(FerrumError::config(
237 "vNext reusable execution requires a non-zero decode width",
238 ));
239 }
240
241 let uniform_class = ReusableExecutionClassId::new(UNIFORM_QUERY_REUSABLE_CLASS)
242 .map_err(|error| FerrumError::config(error.to_string()))?;
243 let packed_class = ReusableExecutionClassId::new(PACKED_TOKEN_REUSABLE_CLASS)
244 .map_err(|error| FerrumError::config(error.to_string()))?;
245 let mut buckets = Vec::new();
246 for width in reusable_execution_anchor_decode_widths(maximum_width) {
247 let width_u32 = u32::try_from(width)
248 .map_err(|_| FerrumError::config("vNext decode width exceeds u32"))?;
249 let width_u64 = u64::try_from(width)
250 .map_err(|_| FerrumError::config("vNext decode width exceeds u64"))?;
251 buckets.push(
252 ReusableExecutionBucketSpec::new(
253 uniform_class.clone(),
254 ReusableExecutionCapacity::new(width_u32, width_u64, 1)
255 .map_err(|error| FerrumError::config(error.to_string()))?,
256 )
257 .map_err(|error| FerrumError::config(error.to_string()))?,
258 );
259 }
260
261 let mut prefill_token_counts = prefill_chunks
262 .iter()
263 .map(|chunk| chunk.tokens_to_process())
264 .map(|token_count| {
265 token_count
266 .min(maximum_scheduled_tokens)
267 .min(maximum_model_tokens)
268 })
269 .filter(|token_count| *token_count > 0)
270 .collect::<Vec<_>>();
271 prefill_token_counts.sort_unstable();
272 prefill_token_counts.dedup();
273 for token_count in prefill_token_counts {
274 buckets.push(
275 ReusableExecutionBucketSpec::new(
276 packed_class.clone(),
277 ReusableExecutionCapacity::new(
278 1,
279 u64::try_from(token_count).map_err(|_| {
280 FerrumError::config("vNext prefill token capacity exceeds u64")
281 })?,
282 1,
283 )
284 .map_err(|error| FerrumError::config(error.to_string()))?,
285 )
286 .map_err(|error| FerrumError::config(error.to_string()))?,
287 );
288 }
289 let mut policy = ReusableExecutionPolicy::new(1, buckets)
290 .map_err(|error| FerrumError::config(error.to_string()))?;
291 if !prepare_device_programs {
292 return Ok(VNextReusableExecutionPolicyResolution {
293 policy,
294 capture: None,
295 });
296 }
297
298 if capture_config.maximum_automatic_exact_decode_width == 0
299 || capture_config.maximum_automatic_exact_decode_width
300 > MAXIMUM_REUSABLE_EXECUTION_STARTUP_CAPTURE_WIDTH
301 {
302 return Err(FerrumError::config(format!(
303 "runtime.reusable_execution_max_automatic_exact_decode_width must be within 1..={MAXIMUM_REUSABLE_EXECUTION_STARTUP_CAPTURE_WIDTH}"
304 )));
305 }
306 let mut resolution_reasons = Vec::new();
307 let (source, mut requested_decode_widths) = match &capture_config.exact_decode_widths {
308 Some(widths) => {
309 if widths.is_empty() {
310 return Err(FerrumError::config(
311 "runtime.reusable_execution_exact_decode_widths must not be empty",
312 ));
313 }
314 (
315 VNextReusableExecutionDecodeWidthSource::Explicit,
316 widths.clone(),
317 )
318 }
319 None => {
320 let automatic_maximum_width = maximum_width
321 .min(capture_config.maximum_automatic_exact_decode_width)
322 .min(MAXIMUM_REUSABLE_EXECUTION_STARTUP_CAPTURE_WIDTH);
323 if automatic_maximum_width < maximum_width {
324 resolution_reasons.push(format!(
325 "automatic startup capture is bounded at width {automatic_maximum_width}; admitted widths {}..={maximum_width} use eager fallback",
326 automatic_maximum_width + 1
327 ));
328 }
329 (
330 VNextReusableExecutionDecodeWidthSource::Automatic,
331 (1..=automatic_maximum_width).collect(),
332 )
333 }
334 };
335 requested_decode_widths.sort_unstable();
336 requested_decode_widths.dedup();
337 if requested_decode_widths.iter().any(|width| {
338 *width == 0
339 || *width > maximum_width
340 || *width > MAXIMUM_REUSABLE_EXECUTION_STARTUP_CAPTURE_WIDTH
341 }) {
342 return Err(FerrumError::config(format!(
343 "vNext reusable exact decode widths must be within 1..={} and may not exceed the independent startup capture hard bound {MAXIMUM_REUSABLE_EXECUTION_STARTUP_CAPTURE_WIDTH}",
344 maximum_width.min(MAXIMUM_REUSABLE_EXECUTION_STARTUP_CAPTURE_WIDTH)
345 )));
346 }
347
348 let on_demand = capture_config.preparation == ReusableExecutionPreparationMode::OnDemand;
349 let requested_sequence_tokens = if on_demand {
350 0
351 } else {
352 reusable_execution_maximum_decode_sequence_tokens(requested_decode_widths.len())?
353 };
354 let (effective_decode_widths, budget_reduction_reason) = if requested_sequence_tokens
355 <= maximum_model_tokens
356 {
357 (requested_decode_widths.clone(), None)
358 } else if source == VNextReusableExecutionDecodeWidthSource::Automatic {
359 let requested_maximum_width = requested_decode_widths.last().copied().ok_or_else(|| {
360 FerrumError::config("vNext automatic reusable decode matrix is empty")
361 })?;
362 let anchors = reusable_execution_anchor_decode_widths(requested_maximum_width);
363 let anchor_sequence_tokens =
364 reusable_execution_maximum_decode_sequence_tokens(anchors.len())?;
365 if anchor_sequence_tokens > maximum_model_tokens {
366 return Err(FerrumError::config(format!(
367 "vNext model length {maximum_model_tokens} cannot cover reusable execution startup ceiling {anchor_sequence_tokens}"
368 )));
369 }
370 (
371 anchors,
372 Some(format!(
373 "automatic exact matrix needs {requested_sequence_tokens} synthetic sequence tokens, exceeding model length {maximum_model_tokens}; reduced to canonical power-of-two anchors"
374 )),
375 )
376 } else {
377 return Err(FerrumError::config(format!(
378 "explicit reusable exact decode matrix needs {requested_sequence_tokens} synthetic sequence tokens, exceeding model length {maximum_model_tokens}"
379 )));
380 };
381 if let Some(reason) = budget_reduction_reason {
382 resolution_reasons.push(reason);
383 }
384 let reduction_reason = (!resolution_reasons.is_empty()).then(|| resolution_reasons.join("; "));
385
386 let mut program_specs = effective_decode_widths
387 .iter()
388 .copied()
389 .map(|width| {
390 let shape = ReusableExecutionProgramShape::uniform_decode(
391 u32::try_from(width)
392 .map_err(|_| FerrumError::config("vNext decode width exceeds u32"))?,
393 1,
394 )
395 .map_err(|error| FerrumError::config(error.to_string()))?;
396 ReusableExecutionProgramSpec::new(uniform_class.clone(), shape)
397 .map_err(|error| FerrumError::config(error.to_string()))
398 })
399 .collect::<Result<Vec<_>>>()?;
400 for chunk in prefill_chunks.iter().copied() {
401 if chunk.tokens_processed() > 0 {
402 let shape = ReusableExecutionProgramShape::prefill(
403 0,
404 u64::try_from(chunk.tokens_processed())
405 .map_err(|_| FerrumError::config("vNext prefill prefix exceeds u64"))?,
406 u64::try_from(chunk.total_prompt_tokens())
407 .map_err(|_| FerrumError::config("vNext prefill prompt exceeds u64"))?,
408 )
409 .map_err(|error| FerrumError::config(error.to_string()))?;
410 program_specs.push(
411 ReusableExecutionProgramSpec::new(packed_class.clone(), shape)
412 .map_err(|error| FerrumError::config(error.to_string()))?,
413 );
414 }
415 let shape = ReusableExecutionProgramShape::prefill(
416 u64::try_from(chunk.tokens_processed())
417 .map_err(|_| FerrumError::config("vNext prefill frontier exceeds u64"))?,
418 u64::try_from(chunk.tokens_to_process())
419 .map_err(|_| FerrumError::config("vNext prefill chunk exceeds u64"))?,
420 u64::try_from(chunk.total_prompt_tokens())
421 .map_err(|_| FerrumError::config("vNext prefill prompt exceeds u64"))?,
422 )
423 .map_err(|error| FerrumError::config(error.to_string()))?;
424 program_specs.push(
425 ReusableExecutionProgramSpec::new(packed_class.clone(), shape)
426 .map_err(|error| FerrumError::config(error.to_string()))?,
427 );
428 }
429 let program_policy = if on_demand {
430 ReusableExecutionProgramPolicy::exact_on_demand(program_specs)
431 } else {
432 ReusableExecutionProgramPolicy::exact_startup_sealed(
433 REUSABLE_EXECUTION_WARMUP_PASSES,
434 REUSABLE_EXECUTION_CAPTURE_PASSES,
435 REUSABLE_EXECUTION_REPLAY_VALIDATION_PASSES,
436 program_specs,
437 )
438 }
439 .map_err(|error| FerrumError::config(error.to_string()))?;
440 policy = policy
441 .with_program_policy(program_policy)
442 .map_err(|error| FerrumError::config(error.to_string()))?;
443 Ok(VNextReusableExecutionPolicyResolution {
444 policy,
445 capture: Some(VNextReusableExecutionCaptureResolution {
446 source,
447 admission_maximum_decode_width: maximum_width,
448 requested_decode_widths,
449 effective_decode_widths,
450 reduction_reason,
451 }),
452 })
453}
454
455#[derive(Debug, Clone, Copy, PartialEq, Eq)]
458enum VNextPlanObservationPolicy {
459 Product,
460 DeterminismTerminalWitnesses,
465}
466
467impl VNextPlanObservationPolicy {
468 fn apply(
469 self,
470 family: &PreparedModelFamily,
471 options: &mut ProgramPlanCompileOptions,
472 ) -> Result<()> {
473 match self {
474 Self::Product => Ok(()),
475 Self::DeterminismTerminalWitnesses => options
476 .retain_all_outputs_for_determinism(family)
477 .map_err(|error| {
478 FerrumError::model(format!(
479 "vNext determinism terminal-witness retention: {error}"
480 ))
481 }),
482 }
483 }
484
485 fn validate_compilation(
486 self,
487 family: &PreparedModelFamily,
488 compilation: &ProgramPlanCompilation,
489 ) -> Result<()> {
490 if self == Self::Product {
491 return Ok(());
492 }
493 let expected = CompletionRetentionSpec::for_determinism_outputs(family)
494 .map_err(|error| FerrumError::model(error.to_string()))?;
495 let retained = compilation
496 .executable()
497 .execution_plan()
498 .payload()
499 .retained_completion_values()
500 .iter()
501 .map(|value| value.value_id().clone())
502 .collect::<BTreeSet<_>>();
503 if compilation.completion_retention() != &expected || retained != *expected.values() {
504 return Err(FerrumError::internal(
505 "vNext determinism compilation did not retain every operation output exactly once",
506 ));
507 }
508 Ok(())
509 }
510}
511
512fn budget_reusable_decode_seed_prefill(
513 chunks: &mut Vec<PrefillChunk>,
514 prepare_device_programs: bool,
515) -> Result<()> {
516 if prepare_device_programs {
517 chunks.push(PrefillChunk::new(0, 1, 1)?);
518 }
519 Ok(())
520}
521
522#[derive(Debug, Clone)]
526pub struct VNextExecutorConfig {
527 pub maximum_model_tokens: usize,
528 pub startup_memory_plan: Option<ferrum_types::StartupMemoryPlan>,
529 pub static_initialization: StaticInitializationPolicy,
530 pub runtime_policy: ResolvedRuntimePolicy,
531 pub device_reusable_execution_enabled: bool,
532 pub reusable_execution_prefill_chunks: Vec<PrefillChunk>,
533 reusable_execution_capture_resolution: Option<VNextReusableExecutionCaptureResolution>,
534 pub diagnostic_fault: Option<VNextDiagnosticFault>,
535 plan_observation: VNextPlanObservationPolicy,
536}
537
538impl VNextExecutorConfig {
539 pub fn from_engine_config<R: DeviceRuntime>(
540 engine: &EngineConfig,
541 info: &ModelInfo,
542 runtime: &R,
543 ) -> Result<Self> {
544 Self::from_engine_config_with_prefill_chunks(
545 engine,
546 info,
547 runtime,
548 &[],
549 VNextPlanObservationPolicy::Product,
550 )
551 }
552
553 pub fn for_determinism_collection<R: DeviceRuntime>(
554 engine: &EngineConfig,
555 info: &ModelInfo,
556 runtime: &R,
557 ) -> Result<Self> {
558 let mut engine = engine.clone();
562 match engine.backend.reusable_execution_capture.preparation {
563 ReusableExecutionPreparationMode::Auto => {
564 engine.backend.reusable_execution_capture.preparation =
565 ReusableExecutionPreparationMode::Startup;
566 }
567 ReusableExecutionPreparationMode::Startup => {}
568 ReusableExecutionPreparationMode::OnDemand => {
569 return Err(FerrumError::config(
570 "fixed determinism collection requires startup reusable execution preparation",
571 ))
572 }
573 }
574 let required_chunks = [
575 PrefillChunk::new(0, 1, 1)?,
576 PrefillChunk::new(0, 4, 4)?,
577 PrefillChunk::new(4, 4, 8)?,
578 ];
579 let config = Self::from_engine_config_with_prefill_chunks(
580 &engine,
581 info,
582 runtime,
583 &required_chunks,
584 VNextPlanObservationPolicy::DeterminismTerminalWitnesses,
585 )?;
586 if config.runtime_policy.memory().maximum_active_sequences
587 < u32::try_from(MAX_VNEXT_DETERMINISM_PARTICIPANTS)
588 .map_err(|_| FerrumError::config("determinism width exceeds u32"))?
589 || config.runtime_policy.admission().maximum_scheduled_tokens
590 < u64::try_from(MAX_VNEXT_DETERMINISM_PARTICIPANTS)
591 .map_err(|_| FerrumError::config("determinism width exceeds u64"))?
592 {
593 return Err(FerrumError::config(format!(
594 "vNext determinism collection requires at least {MAX_VNEXT_DETERMINISM_PARTICIPANTS} active sequences and scheduled tokens"
595 )));
596 }
597 Ok(config)
598 }
599
600 fn from_engine_config_with_prefill_chunks<R: DeviceRuntime>(
601 engine: &EngineConfig,
602 info: &ModelInfo,
603 runtime: &R,
604 additional_prefill_chunks: &[PrefillChunk],
605 plan_observation: VNextPlanObservationPolicy,
606 ) -> Result<Self> {
607 let descriptor = runtime.descriptor();
608 descriptor
609 .validate()
610 .map_err(|error| FerrumError::config(format!("invalid vNext runtime: {error}")))?;
611
612 let maximum_model_tokens = engine
613 .runtime
614 .max_model_len
615 .unwrap_or(info.max_sequence_length)
616 .min(info.max_sequence_length);
617 if maximum_model_tokens == 0 {
618 return Err(FerrumError::config(
619 "vNext maximum model length must be greater than zero",
620 ));
621 }
622
623 let memory_budget = engine
624 .memory
625 .resolve_capacity_budget(descriptor.total_memory_bytes)
626 .map_err(FerrumError::config)?;
627
628 let maximum_active_sequences = u32::try_from(engine.scheduler.max_running_requests)
629 .map_err(|_| {
630 FerrumError::config("scheduler.max_running_requests exceeds the vNext limit")
631 })?;
632 let maximum_queue_depth =
633 u32::try_from(engine.scheduler.max_waiting_requests).map_err(|_| {
634 FerrumError::config("scheduler.max_waiting_requests exceeds the vNext limit")
635 })?;
636 let maximum_scheduled_tokens = u64::try_from(engine.batching.max_num_batched_tokens)
637 .map_err(|_| {
638 FerrumError::config("batching.max_num_batched_tokens exceeds the vNext limit")
639 })?;
640 let scheduling = match engine.scheduler.policy {
641 SchedulingPolicy::Priority | SchedulingPolicy::FairShare => {
642 SchedulingDiscipline::Priority
643 }
644 SchedulingPolicy::FCFS
645 | SchedulingPolicy::SJF
646 | SchedulingPolicy::RoundRobin
647 | SchedulingPolicy::ContinuousBatch => SchedulingDiscipline::FirstReady,
648 };
649 let dynamic_storage_profile_order = descriptor
650 .dynamic_storage_profiles
651 .iter()
652 .copied()
653 .collect::<Vec<_>>();
654
655 let device_reusable_execution_supported = descriptor
656 .capabilities
657 .iter()
658 .any(|capability| capability.as_str() == DEVICE_REUSABLE_EXECUTION_CAPABILITY_ID);
659 let prepare_device_programs =
660 engine.backend.enable_reusable_execution && device_reusable_execution_supported;
661 let mut capture_config = engine.backend.reusable_execution_capture.clone();
662 if engine.backend.enable_reusable_execution {
663 capture_config.preparation = capture_config
664 .preparation
665 .resolve(descriptor.capabilities.iter().any(|capability| {
666 capability.as_str() == DEVICE_ON_DEMAND_REUSABLE_EXECUTION_CAPABILITY_ID
667 }))
668 .map_err(FerrumError::config)?;
669 }
670 let mut reusable_execution_prefill_chunks = [
671 engine.scheduler.prefill_step_chunk,
672 engine.scheduler.active_decode_prefill_chunk,
673 ]
674 .into_iter()
675 .flatten()
676 .filter_map(|token_count| {
677 let token_count = token_count
678 .min(engine.batching.max_num_batched_tokens)
679 .min(maximum_model_tokens);
680 (token_count > 0).then_some(token_count)
681 })
682 .map(|token_count| PrefillChunk::new(0, token_count, token_count))
683 .collect::<Result<Vec<_>>>()?;
684 reusable_execution_prefill_chunks.extend_from_slice(additional_prefill_chunks);
685 budget_reusable_decode_seed_prefill(
689 &mut reusable_execution_prefill_chunks,
690 prepare_device_programs,
691 )?;
692 if reusable_execution_prefill_chunks.iter().any(|chunk| {
693 !chunk.is_final()
694 || chunk.total_prompt_tokens() > maximum_model_tokens
695 || chunk.tokens_to_process() > engine.batching.max_num_batched_tokens
696 }) {
697 return Err(FerrumError::config(
698 "vNext reusable prefill capture requires a final chunk within model and scheduled-token limits",
699 ));
700 }
701 reusable_execution_prefill_chunks.sort_unstable_by(|left, right| {
702 right
703 .tokens_to_process()
704 .cmp(&left.tokens_to_process())
705 .then_with(|| left.tokens_processed().cmp(&right.tokens_processed()))
706 .then_with(|| left.total_prompt_tokens().cmp(&right.total_prompt_tokens()))
707 });
708 reusable_execution_prefill_chunks.dedup();
709 let reusable_execution_resolution = resolve_reusable_execution_policy(
713 maximum_active_sequences,
714 maximum_scheduled_tokens,
715 maximum_model_tokens,
716 &reusable_execution_prefill_chunks,
717 &capture_config,
718 prepare_device_programs,
719 )?;
720 let reusable_execution_policy = Some(reusable_execution_resolution.policy);
721 let reusable_execution_capture_resolution = reusable_execution_resolution.capture;
722 let execution_determinism = if prepare_device_programs {
723 ExecutionDeterminismRequirement::BitwiseSameRuntimeWithReplay
724 } else {
725 ExecutionDeterminismRequirement::BitwiseSameRuntime
726 };
727 let attention_execution = resolve_runtime_attention_authority(
728 engine.runtime.attention_execution_policy,
729 descriptor.capabilities.iter().any(|capability| {
730 capability.as_str() == DEVICE_NATIVE_ADAPTIVE_ATTENTION_CAPABILITY_ID
731 }),
732 runtime.attention_execution_policy(),
733 )?;
734
735 let runtime_policy = ResolvedRuntimePolicy::new(
736 POLICY_ID,
737 POLICY_VERSION,
738 scheduling,
739 RuntimeMemoryPolicy {
740 checkpoint_capacity: engine
741 .runtime
742 .prefix_state_cache_enabled
743 .then(|| {
744 CheckpointCapacityPolicy::new(
745 memory_budget.capacity_bytes - memory_budget.reserve_bytes,
746 )
747 })
748 .transpose()
749 .map_err(|error| FerrumError::config(error.to_string()))?,
750 capacity_bytes: memory_budget.capacity_bytes,
751 reserve_bytes: memory_budget.reserve_bytes,
752 maximum_active_sequences,
753 dynamic_storage_profile_order,
754 },
755 AdmissionPolicy {
756 maximum_queue_depth,
757 maximum_scheduled_tokens,
758 sequence_fit_policy: resolved_sequence_fit_policy(
759 engine.scheduler.sequence_fit_policy,
760 ),
761 allow_defer: true,
762 cancellation_check_interval_steps: DEFAULT_CANCELLATION_CHECK_INTERVAL_STEPS,
763 },
764 attention_execution,
765 execution_determinism,
766 reusable_execution_policy,
767 )
768 .map_err(|error| FerrumError::config(format!("invalid vNext policy: {error}")))?;
769 let static_initialization = StaticInitializationPolicy::new(
770 DEFAULT_STATIC_STAGING_BYTES,
771 DEFAULT_STATIC_COMMANDS_PER_BATCH,
772 )
773 .map_err(|error| FerrumError::config(error.to_string()))?;
774 let diagnostic_fault = engine.runtime.vnext_diagnostic_fault;
775 if diagnostic_fault.is_some()
776 && (engine.runtime.profile_detail != ObservabilityProfileDetail::Latency
777 || engine.runtime.profile_jsonl.is_none()
778 || !matches!(
779 engine.runtime.profile_entrypoint,
780 Some(ProfileEntrypoint::Run | ProfileEntrypoint::Serve)
781 ))
782 {
783 return Err(FerrumError::config(
784 "vNext diagnostic faults require a product run/serve latency profile and --profile-jsonl",
785 ));
786 }
787
788 Ok(Self {
789 maximum_model_tokens,
790 startup_memory_plan: engine.runtime.startup_memory_plan.clone(),
791 static_initialization,
792 runtime_policy,
793 device_reusable_execution_enabled: engine.backend.enable_reusable_execution,
794 reusable_execution_prefill_chunks,
795 reusable_execution_capture_resolution,
796 diagnostic_fault,
797 plan_observation,
798 })
799 }
800}
801
802#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
803#[serde(tag = "topology", rename_all = "snake_case")]
804enum VNextReusableExecutionDescriptor {
805 UniformDecode {
806 query_tokens_per_sequence: usize,
807 token_capacity: usize,
808 request_capacity: usize,
809 },
810 Prefill {
811 tokens_processed: usize,
812 token_capacity: usize,
813 total_prompt_tokens: usize,
814 request_capacity: usize,
815 },
816}
817
818impl VNextReusableExecutionDescriptor {
819 const fn uniform_decode(width: usize) -> Self {
820 Self::UniformDecode {
821 query_tokens_per_sequence: 1,
822 token_capacity: width,
823 request_capacity: width,
824 }
825 }
826
827 const fn prefill(chunk: PrefillChunk) -> Self {
828 Self::Prefill {
829 tokens_processed: chunk.tokens_processed(),
830 token_capacity: chunk.tokens_to_process(),
831 total_prompt_tokens: chunk.total_prompt_tokens(),
832 request_capacity: 1,
833 }
834 }
835
836 fn from_program_shape(shape: ReusableExecutionProgramShape) -> Result<Self> {
837 match shape {
838 ReusableExecutionProgramShape::UniformDecode {
839 query_tokens_per_sequence,
840 token_capacity,
841 request_capacity,
842 } => Ok(Self::UniformDecode {
843 query_tokens_per_sequence: usize::try_from(query_tokens_per_sequence)
844 .map_err(|_| FerrumError::config("vNext decode query width exceeds usize"))?,
845 token_capacity: usize::try_from(token_capacity).map_err(|_| {
846 FerrumError::config("vNext decode token capacity exceeds usize")
847 })?,
848 request_capacity: usize::try_from(request_capacity).map_err(|_| {
849 FerrumError::config("vNext decode request capacity exceeds usize")
850 })?,
851 }),
852 ReusableExecutionProgramShape::Prefill {
853 tokens_processed,
854 token_capacity,
855 total_prompt_tokens,
856 } => Ok(Self::Prefill {
857 tokens_processed: usize::try_from(tokens_processed)
858 .map_err(|_| FerrumError::config("vNext prefill frontier exceeds usize"))?,
859 token_capacity: usize::try_from(token_capacity)
860 .map_err(|_| FerrumError::config("vNext prefill capacity exceeds usize"))?,
861 total_prompt_tokens: usize::try_from(total_prompt_tokens)
862 .map_err(|_| FerrumError::config("vNext prefill prompt exceeds usize"))?,
863 request_capacity: 1,
864 }),
865 }
866 }
867}
868
869#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
870struct VNextReusableExecutionStartupPlan {
871 descriptors: Vec<VNextReusableExecutionDescriptor>,
872 prefill_chunks: Vec<PrefillChunk>,
873 maximum_decode_sequence_tokens: usize,
874 warmup_passes: usize,
875 capture_passes: usize,
876 replay_validation_passes: usize,
877 program_policy: ReusableExecutionProgramPolicy,
878 capture_resolution: VNextReusableExecutionCaptureResolution,
879 device_plan: DeviceReusableExecutionPlan,
880}
881
882impl VNextReusableExecutionStartupPlan {
883 fn resolve(
884 program_policy: &ReusableExecutionProgramPolicy,
885 capture_resolution: VNextReusableExecutionCaptureResolution,
886 maximum_model_tokens: usize,
887 prefill_chunks: &[PrefillChunk],
888 maximum_device_executables: u64,
889 ) -> Result<Self> {
890 let mut prefill_chunks = prefill_chunks.iter().copied().collect::<Vec<_>>();
891 if prefill_chunks
892 .iter()
893 .any(|chunk| !chunk.is_final() || chunk.total_prompt_tokens() > maximum_model_tokens)
894 {
895 return Err(FerrumError::config(
896 "vNext reusable execution prefill chunk exceeds its immutable startup limits",
897 ));
898 }
899 prefill_chunks.sort_unstable_by(|left, right| {
900 right
901 .tokens_to_process()
902 .cmp(&left.tokens_to_process())
903 .then_with(|| left.tokens_processed().cmp(&right.tokens_processed()))
904 .then_with(|| left.total_prompt_tokens().cmp(&right.total_prompt_tokens()))
905 });
906 prefill_chunks.dedup();
907
908 let mut descriptors = program_policy
909 .programs()
910 .iter()
911 .map(|program| {
912 let shape = program.shape();
913 let expected_class = match shape {
914 ReusableExecutionProgramShape::UniformDecode { .. } => {
915 UNIFORM_QUERY_REUSABLE_CLASS
916 }
917 ReusableExecutionProgramShape::Prefill { .. } => {
918 PACKED_TOKEN_REUSABLE_CLASS
919 }
920 };
921 if program.class_id().as_str() != expected_class {
922 return Err(FerrumError::config(format!(
923 "vNext reusable {:?} program is bound to workspace class `{}`, expected `{expected_class}`",
924 shape,
925 program.class_id().as_str()
926 )));
927 }
928 VNextReusableExecutionDescriptor::from_program_shape(shape)
929 })
930 .collect::<Result<Vec<_>>>()?;
931 let mut decode_widths = descriptors
932 .iter()
933 .filter_map(|descriptor| match descriptor {
934 VNextReusableExecutionDescriptor::UniformDecode {
935 query_tokens_per_sequence,
936 token_capacity,
937 request_capacity,
938 } if *query_tokens_per_sequence == 1 && token_capacity == request_capacity => {
939 Some(*request_capacity)
940 }
941 VNextReusableExecutionDescriptor::UniformDecode { .. } => None,
942 VNextReusableExecutionDescriptor::Prefill { .. } => None,
943 })
944 .collect::<Vec<_>>();
945 decode_widths.sort_unstable();
946 if decode_widths.is_empty()
947 || decode_widths
948 .iter()
949 .any(|width| *width > MAXIMUM_REUSABLE_EXECUTION_STARTUP_CAPTURE_WIDTH)
950 {
951 return Err(FerrumError::config(format!(
952 "vNext reusable startup decode widths must be non-empty and within the independent hard bound 1..={MAXIMUM_REUSABLE_EXECUTION_STARTUP_CAPTURE_WIDTH}"
953 )));
954 }
955 if decode_widths != capture_resolution.effective_decode_widths {
956 return Err(FerrumError::config(
957 "vNext reusable startup widths differ from the resolved program policy",
958 ));
959 }
960 decode_widths.sort_unstable_by(|left, right| right.cmp(left));
961
962 let warmup_passes = usize::try_from(program_policy.warmup_passes())
963 .map_err(|_| FerrumError::config("vNext reusable warmup passes exceed usize"))?;
964 let capture_passes = usize::try_from(program_policy.capture_passes())
965 .map_err(|_| FerrumError::config("vNext reusable capture passes exceed usize"))?;
966 let replay_validation_passes = usize::try_from(program_policy.replay_validation_passes())
967 .map_err(|_| {
968 FerrumError::config("vNext reusable replay validation passes exceed usize")
969 })?;
970 let passes_per_width = warmup_passes
971 .checked_add(capture_passes)
972 .and_then(|passes| passes.checked_add(replay_validation_passes))
973 .ok_or_else(|| FerrumError::config("vNext reusable startup passes overflow usize"))?;
974 let maximum_decode_sequence_tokens = if program_policy.catalog_lifetime()
975 == ReusableExecutionCatalogLifetime::OnDemandBounded
976 {
977 0
978 } else {
979 decode_widths
980 .len()
981 .checked_mul(passes_per_width)
982 .and_then(|decode_tokens| decode_tokens.checked_add(1))
983 .ok_or_else(|| FerrumError::config("vNext startup token ceiling overflowed"))?
984 };
985 if maximum_decode_sequence_tokens > maximum_model_tokens {
986 return Err(FerrumError::config(format!(
987 "vNext model length {maximum_model_tokens} cannot cover reusable execution startup ceiling {maximum_decode_sequence_tokens}"
988 )));
989 }
990
991 let expected_prefill_descriptors = prefill_chunks
992 .iter()
993 .copied()
994 .flat_map(|chunk| {
995 let prefix = (chunk.tokens_processed() > 0).then(|| {
996 VNextReusableExecutionDescriptor::Prefill {
997 tokens_processed: 0,
998 token_capacity: chunk.tokens_processed(),
999 total_prompt_tokens: chunk.total_prompt_tokens(),
1000 request_capacity: 1,
1001 }
1002 });
1003 prefix
1004 .into_iter()
1005 .chain([Self::descriptor_for_chunk(chunk)])
1006 })
1007 .collect::<BTreeSet<_>>();
1008 let resolved_prefill_descriptors = descriptors
1009 .iter()
1010 .copied()
1011 .filter(|descriptor| {
1012 matches!(descriptor, VNextReusableExecutionDescriptor::Prefill { .. })
1013 })
1014 .collect::<BTreeSet<_>>();
1015 if expected_prefill_descriptors != resolved_prefill_descriptors {
1016 return Err(FerrumError::config(
1017 "vNext reusable prefill startup work differs from the resolved program policy",
1018 ));
1019 }
1020 descriptors.sort_unstable();
1021 let decode_descriptor_count = descriptors
1022 .iter()
1023 .take_while(|descriptor| {
1024 matches!(
1025 descriptor,
1026 VNextReusableExecutionDescriptor::UniformDecode { .. }
1027 )
1028 })
1029 .count();
1030 descriptors[..decode_descriptor_count].reverse();
1031
1032 let maximum_executables = usize::try_from(maximum_device_executables)
1033 .map_err(|_| FerrumError::config("vNext reusable executable capacity exceeds usize"))?;
1034 let device_plan = if program_policy.catalog_lifetime()
1035 == ReusableExecutionCatalogLifetime::OnDemandBounded
1036 {
1037 DeviceReusableExecutionPlan::on_demand(maximum_executables)
1038 } else {
1039 DeviceReusableExecutionPlan::new(maximum_executables)
1040 }
1041 .map_err(|error| FerrumError::config(error.to_string()))?;
1042 Ok(Self {
1043 descriptors,
1044 prefill_chunks,
1045 maximum_decode_sequence_tokens,
1046 warmup_passes,
1047 capture_passes,
1048 replay_validation_passes,
1049 program_policy: program_policy.clone(),
1050 capture_resolution,
1051 device_plan,
1052 })
1053 }
1054
1055 const fn descriptor_for_chunk(chunk: PrefillChunk) -> VNextReusableExecutionDescriptor {
1056 VNextReusableExecutionDescriptor::prefill(chunk)
1057 }
1058
1059 fn decode_widths(&self) -> Vec<usize> {
1060 self.descriptors
1061 .iter()
1062 .filter_map(|descriptor| match descriptor {
1063 VNextReusableExecutionDescriptor::UniformDecode {
1064 request_capacity, ..
1065 } => Some(*request_capacity),
1066 VNextReusableExecutionDescriptor::Prefill { .. } => None,
1067 })
1068 .collect()
1069 }
1070
1071 fn prefill_chunks(&self) -> Vec<PrefillChunk> {
1072 self.prefill_chunks.clone()
1073 }
1074
1075 fn prefill_token_counts(&self) -> Vec<usize> {
1076 let mut token_counts = self
1077 .prefill_chunks()
1078 .into_iter()
1079 .map(PrefillChunk::tokens_to_process)
1080 .collect::<Vec<_>>();
1081 token_counts.sort_unstable_by(|left, right| right.cmp(left));
1082 token_counts.dedup();
1083 token_counts
1084 }
1085
1086 fn prefill_wave_shapes(&self) -> usize {
1087 self.prefill_chunks()
1088 .iter()
1089 .map(|chunk| usize::from(chunk.tokens_processed() > 0) + 1)
1090 .sum()
1091 }
1092
1093 fn decode_catalog_omits_admitted_widths(&self) -> bool {
1094 self.capture_resolution
1095 .effective_decode_widths
1096 .iter()
1097 .copied()
1098 .ne(1..=self.capture_resolution.admission_maximum_decode_width)
1099 }
1100}
1101
1102#[derive(Debug, Clone, Serialize)]
1103struct VNextReusableExecutionCatalogProgramReceipt {
1104 program_id: DeviceReusableExecutionProgramId,
1105 program_fingerprint: String,
1106 state: DeviceReusableExecutionProgramState,
1107 node_count: u32,
1108 eager_boundary_node_indices: Vec<u32>,
1109 resident_segments: Vec<DeviceReusableExecutionSegment>,
1110 per_wave_binding_node_indices: Vec<u32>,
1111 gaps: Vec<DeviceReusableExecutionProgramGap>,
1112}
1113
1114impl VNextReusableExecutionCatalogProgramReceipt {
1115 fn from_program(program: &DeviceReusableExecutionProgram) -> Self {
1116 Self {
1117 program_id: program.program_id().clone(),
1118 program_fingerprint: program.program_id().fingerprint(),
1119 state: program.state(),
1120 node_count: program.node_count(),
1121 eager_boundary_node_indices: program.eager_boundary_node_indices().to_vec(),
1122 resident_segments: program.segments().to_vec(),
1123 per_wave_binding_node_indices: program.per_wave_binding_node_indices().to_vec(),
1124 gaps: program.gaps().to_vec(),
1125 }
1126 }
1127}
1128
1129#[derive(Debug, Clone, Serialize)]
1130struct VNextReusableExecutionCaptureCaseReceipt {
1131 descriptor: VNextReusableExecutionDescriptor,
1132 observed_program_fingerprints: Vec<String>,
1133 resident_program_fingerprints: Vec<String>,
1134}
1135
1136#[derive(Debug, Clone, Serialize)]
1137struct VNextReusableExecutionStartupReport {
1138 enabled: bool,
1139 supported: bool,
1140 eager_fallback_required: bool,
1141 resolved_runtime_policy_fingerprint: String,
1142 resolved_program_policy: Option<ReusableExecutionProgramPolicy>,
1143 decode_width_resolution: Option<VNextReusableExecutionCaptureResolution>,
1144 maximum_device_executables: usize,
1145 requested_descriptors: Vec<VNextReusableExecutionDescriptor>,
1146 prepared_descriptors: Vec<VNextReusableExecutionDescriptor>,
1147 capture_case_receipts: Vec<VNextReusableExecutionCaptureCaseReceipt>,
1148 catalog_programs: Vec<VNextReusableExecutionCatalogProgramReceipt>,
1149 requested_decode_widths: Vec<usize>,
1150 prepared_decode_widths: Vec<usize>,
1151 requested_prefill_token_counts: Vec<usize>,
1152 prepared_prefill_token_counts: Vec<usize>,
1153 requested_prefill_chunks: Vec<PrefillChunk>,
1154 prepared_prefill_chunks: Vec<PrefillChunk>,
1155 synthetic_sequences: usize,
1156 eager_warmup_waves: usize,
1157 capture_waves: usize,
1158 replay_inventory_check_waves: usize,
1159 prepared_programs: usize,
1160 device_preparation: DeviceReusableExecutionPreparation,
1161 elapsed_ms: u64,
1162}
1163
1164fn reusable_executable_inventory_matches(
1165 before: DeviceReusableExecutionPreparation,
1166 after: DeviceReusableExecutionPreparation,
1167) -> bool {
1168 before.maximum_executables() == after.maximum_executables()
1169 && before.resident_executables() == after.resident_executables()
1170 && before.rejected_executables() == after.rejected_executables()
1171 && before.captured_executables() == after.captured_executables()
1172 && before.uploaded_executables() == after.uploaded_executables()
1173}
1174
1175fn reusable_execution_requires_eager_fallback(
1176 preparation: DeviceReusableExecutionPreparation,
1177) -> bool {
1178 preparation.resident_executables() == 0
1179 || preparation.rejected_executables() != 0
1180 || preparation.capacity_deferred_executables() != 0
1181}
1182
1183fn reusable_execution_program_catalog_is_usable(
1184 preparation: DeviceReusableExecutionPreparation,
1185 prepared_programs: usize,
1186) -> bool {
1187 preparation.resident_executables() == 0 || prepared_programs != 0
1188}
1189
1190fn reusable_startup_case_budget_violation<'a, D: Ord, P: Ord>(
1191 requested_cases: &BTreeSet<D>,
1192 observed_programs: &'a BTreeMap<D, BTreeSet<P>>,
1193) -> Option<(&'a D, usize)> {
1194 observed_programs
1195 .iter()
1196 .find(|(case, programs)| !requested_cases.contains(*case) || programs.len() > 1)
1197 .map(|(case, programs)| (case, programs.len()))
1198}
1199
1200#[derive(Debug, Clone, Serialize)]
1201#[serde(tag = "state", rename_all = "snake_case")]
1202enum VNextStartupPreparationState {
1203 Pending,
1204 Preparing,
1205 Ready {
1206 report: VNextReusableExecutionStartupReport,
1207 },
1208 Failed {
1209 message: String,
1210 },
1211}
1212
1213impl VNextStartupPreparationState {
1214 const fn is_ready(&self) -> bool {
1215 matches!(self, Self::Ready { .. })
1216 }
1217}
1218
1219#[derive(Debug, Clone)]
1220struct VNextLanguageIoIds {
1221 token_input: ProgramValueId,
1222 token_mask_input: ProgramValueId,
1223 repetition_token_ids_input: ProgramValueId,
1224 repetition_offsets_input: ProgramValueId,
1225 repetition_penalty_input: ProgramValueId,
1226 logits_output: ProgramValueId,
1227 greedy_token_output: ProgramValueId,
1228}
1229
1230fn is_language_token_embedding_operation(operation_id: &str) -> bool {
1231 operation_id == TOKEN_EMBEDDING_OPERATION_ID
1232 || operation_id == TOKEN_EMBEDDING_F32_MASTER_OPERATION_ID
1233}
1234
1235fn is_language_masked_argmax_operation(operation_id: &str) -> bool {
1236 operation_id == LAST_TOKEN_MASKED_ARGMAX_OPERATION_ID
1237 || operation_id == LAST_TOKEN_MASKED_ARGMAX_F32_OPERATION_ID
1238}
1239
1240#[derive(Debug, Clone)]
1241struct VNextIoBinding {
1242 input_node_id: NodeId,
1243 input_ordinal: u32,
1244 token_mask_input_node_id: NodeId,
1245 token_mask_input_ordinal: u32,
1246 token_mask_residency_eligible: bool,
1247 repetition_token_ids_input_node_id: NodeId,
1248 repetition_token_ids_input_ordinal: u32,
1249 repetition_offsets_input_node_id: NodeId,
1250 repetition_offsets_input_ordinal: u32,
1251 repetition_penalty_input_node_id: NodeId,
1252 repetition_penalty_input_ordinal: u32,
1253 repetition_capacity: usize,
1254 output_node_id: NodeId,
1255 output_resource_id: ResourceId,
1256 output_offset_bytes: u64,
1257 output_layout: HostTransferLayout,
1258 output_element_type: ElementType,
1259 output_elements: usize,
1260 greedy_token_output_node_id: NodeId,
1261 greedy_token_output_resource_id: ResourceId,
1262 greedy_token_output_offset_bytes: u64,
1263 greedy_token_output_layout: HostTransferLayout,
1264}
1265
1266struct VNextReusableExecutionCatalog {
1267 lane_epoch: u64,
1268 programs: BTreeMap<DeviceReusableExecutionProgramId, DeviceReusableExecutionProgram>,
1269}
1270
1271const MAX_REUSABLE_EXECUTION_CATALOG_MISS_KEYS: usize = 64;
1272
1273#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)]
1274#[serde(rename_all = "snake_case")]
1275enum VNextReusableExecutionCatalogMissReason {
1276 ProgramIdentityUnavailable,
1277 CatalogEmpty,
1278 ProgramAbsent,
1279 ProgramNonResident,
1280 EpochMismatch,
1281}
1282
1283impl VNextReusableExecutionCatalogMissReason {
1284 const fn is_epoch_mismatch(self) -> bool {
1285 matches!(self, Self::EpochMismatch)
1286 }
1287}
1288
1289#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)]
1290struct VNextReusableExecutionCatalogMissKey {
1291 immediate_sequences: u32,
1292 immediate_tokens: u64,
1293 immediate_pages: u64,
1294 topology_fingerprint: DeviceReusableExecutionTopologyFingerprint,
1295 reason: VNextReusableExecutionCatalogMissReason,
1296}
1297
1298impl VNextReusableExecutionCatalogMissKey {
1299 fn from_program_id(
1300 program_id: &DeviceReusableExecutionProgramId,
1301 reason: VNextReusableExecutionCatalogMissReason,
1302 ) -> Self {
1303 Self {
1304 immediate_sequences: program_id.immediate_sequences(),
1305 immediate_tokens: program_id.immediate_tokens(),
1306 immediate_pages: program_id.immediate_pages(),
1307 topology_fingerprint: program_id.topology_fingerprint(),
1308 reason,
1309 }
1310 }
1311
1312 fn without_program_identity(
1313 work_shape: &BatchWorkShape,
1314 reason: VNextReusableExecutionCatalogMissReason,
1315 ) -> Self {
1316 Self {
1317 immediate_sequences: work_shape.immediate_sequences(),
1318 immediate_tokens: work_shape.immediate_tokens(),
1319 immediate_pages: work_shape.immediate_pages(),
1320 topology_fingerprint: DeviceReusableExecutionTopologyFingerprint::static_program(),
1321 reason,
1322 }
1323 }
1324}
1325
1326#[derive(Debug, Clone, Serialize)]
1327struct VNextReusableExecutionCatalogMissRow {
1328 key: VNextReusableExecutionCatalogMissKey,
1329 count: u64,
1330}
1331
1332#[derive(Debug, Clone, Serialize)]
1333struct VNextReusableExecutionCatalogMissOverflowRow {
1334 reason: VNextReusableExecutionCatalogMissReason,
1335 count: u64,
1336}
1337
1338#[derive(Debug, Clone, Serialize)]
1339struct VNextReusableExecutionCatalogMissSnapshot {
1340 maximum_distinct_keys: usize,
1341 distinct_keys: usize,
1342 rows: Vec<VNextReusableExecutionCatalogMissRow>,
1343 overflow: Vec<VNextReusableExecutionCatalogMissOverflowRow>,
1344}
1345
1346#[derive(Default)]
1347struct VNextReusableExecutionCatalogMissLedger {
1348 counts: BTreeMap<VNextReusableExecutionCatalogMissKey, u64>,
1349 overflow: BTreeMap<VNextReusableExecutionCatalogMissReason, u64>,
1350}
1351
1352impl VNextReusableExecutionCatalogMissLedger {
1353 fn record(&mut self, key: VNextReusableExecutionCatalogMissKey) {
1354 if let Some(count) = self.counts.get_mut(&key) {
1355 *count = count.saturating_add(1);
1356 } else if self.counts.len() < MAX_REUSABLE_EXECUTION_CATALOG_MISS_KEYS {
1357 self.counts.insert(key, 1);
1358 } else {
1359 let count = self.overflow.entry(key.reason).or_default();
1360 *count = count.saturating_add(1);
1361 }
1362 }
1363
1364 fn snapshot(&self) -> VNextReusableExecutionCatalogMissSnapshot {
1365 VNextReusableExecutionCatalogMissSnapshot {
1366 maximum_distinct_keys: MAX_REUSABLE_EXECUTION_CATALOG_MISS_KEYS,
1367 distinct_keys: self.counts.len(),
1368 rows: self
1369 .counts
1370 .iter()
1371 .map(|(key, count)| VNextReusableExecutionCatalogMissRow {
1372 key: *key,
1373 count: *count,
1374 })
1375 .collect(),
1376 overflow: self
1377 .overflow
1378 .iter()
1379 .map(
1380 |(reason, count)| VNextReusableExecutionCatalogMissOverflowRow {
1381 reason: *reason,
1382 count: *count,
1383 },
1384 )
1385 .collect(),
1386 }
1387 }
1388
1389 fn reset(&mut self) {
1390 self.counts.clear();
1391 self.overflow.clear();
1392 }
1393}
1394
1395#[derive(Default)]
1396struct VNextExecutorMetrics {
1397 prefix_cache: Arc<prefix_cache::PrefixCacheMetrics>,
1398 prefill_operations: AtomicU64,
1399 prefill_frontier_narrowings: AtomicU64,
1400 decode_operations: AtomicU64,
1401 prepared_wave_topology: VNextPreparedWaveTopologyMetrics,
1402 submitted_waves: AtomicU64,
1403 completed_waves: AtomicU64,
1404 failed_waves: AtomicU64,
1405 direct_reusable_waves: AtomicU64,
1406 direct_reusable_segments: AtomicU64,
1407 direct_reusable_logical_nodes: AtomicU64,
1408 direct_reusable_binding_nodes: AtomicU64,
1409 direct_reusable_fallbacks: AtomicU64,
1410 reusable_catalog_misses: AtomicU64,
1411 reusable_catalog_epoch_misses: AtomicU64,
1412 reusable_catalog_miss_ledger: Mutex<VNextReusableExecutionCatalogMissLedger>,
1413 identity_waves: AtomicU64,
1414 identity_logical_nodes: AtomicU64,
1415 identity_nodes_materialized_before_submit: AtomicU64,
1416 identity_full_participant_materializations_before_submit: AtomicU64,
1417 definitely_not_submitted_retries: AtomicU64,
1418 request_deferrals: AtomicU64,
1419 sequence_deferrals: AtomicU64,
1420 extension_deferrals: AtomicU64,
1421 step_deferrals: AtomicU64,
1422 wave_deferrals: AtomicU64,
1423 backing_deferrals: AtomicU64,
1424 uploaded_bytes: AtomicU64,
1425 readback_bytes: AtomicU64,
1426 full_logits_readback_waves: AtomicU64,
1427 greedy_token_readback_waves: AtomicU64,
1428 greedy_policy_fallback_waves: AtomicU64,
1429 token_mask_upload_participants: AtomicU64,
1430 token_mask_cache_hit_participants: AtomicU64,
1431 sparse_repetition_waves: AtomicU64,
1432 sparse_repetition_participants: AtomicU64,
1433 sparse_repetition_token_ids_uploaded: AtomicU64,
1434 total_prefill_us: AtomicU64,
1435 total_decode_us: AtomicU64,
1436 wave_timing: VNextWaveTimingMetrics,
1437 prefill_wave_timing: VNextWaveTimingMetrics,
1438 decode_wave_timing: VNextWaveTimingMetrics,
1439 device_timing: VNextDeviceTimingMetrics,
1440 prefill_device_timing: VNextDeviceTimingMetrics,
1441 decode_device_timing: VNextDeviceTimingMetrics,
1442 last_failure: Mutex<Option<String>>,
1443}
1444
1445#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1446enum VNextExecutionWaveKind {
1447 Prefill,
1448 Decode,
1449}
1450
1451#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1452enum VNextProductOutputMode {
1453 FullLogits,
1454 GreedyToken,
1455}
1456
1457impl VNextProductOutputMode {
1458 const fn checkpoint_mode(self) -> VNextCheckpointProductOutputMode {
1459 match self {
1460 Self::FullLogits => VNextCheckpointProductOutputMode::FullLogits,
1461 Self::GreedyToken => VNextCheckpointProductOutputMode::GreedyToken,
1462 }
1463 }
1464}
1465
1466#[derive(Debug, Clone)]
1467enum VNextProductTokenMaskContent {
1468 AllValid {
1469 vocabulary_size: usize,
1470 },
1471 Selection {
1472 vocabulary_size: usize,
1473 fingerprint: u64,
1474 valid_token_mask: Arc<[i8]>,
1475 },
1476}
1477
1478impl VNextProductTokenMaskContent {
1479 fn from_policy(
1480 policy: Option<&LogitsReturnPolicy>,
1481 output_mode: VNextProductOutputMode,
1482 vocabulary_size: usize,
1483 ) -> Self {
1484 if output_mode == VNextProductOutputMode::GreedyToken {
1485 if let Some(LogitsReturnPolicy::GreedyArgmax {
1486 token_mask: Some(token_mask),
1487 ..
1488 }) = policy
1489 {
1490 return Self::Selection {
1491 vocabulary_size,
1492 fingerprint: token_mask.fingerprint,
1493 valid_token_mask: Arc::clone(&token_mask.valid_token_mask),
1494 };
1495 }
1496 }
1497 Self::AllValid { vocabulary_size }
1498 }
1499
1500 fn normalized(&self) -> Vec<u8> {
1501 match self {
1502 Self::AllValid { vocabulary_size } => vec![1_u8; *vocabulary_size],
1503 Self::Selection {
1504 vocabulary_size,
1505 valid_token_mask,
1506 ..
1507 } => {
1508 let mut output = vec![0_u8; *vocabulary_size];
1509 for (destination, source) in output.iter_mut().zip(valid_token_mask.iter().copied())
1510 {
1511 *destination = u8::from(source != 0);
1512 }
1513 output
1514 }
1515 }
1516 }
1517}
1518
1519#[derive(Debug)]
1520enum VNextResidentProductTokenMaskContent {
1521 AllValid {
1522 vocabulary_size: usize,
1523 },
1524 Selection {
1525 vocabulary_size: usize,
1526 fingerprint: u64,
1527 source_len: usize,
1528 valid_token_mask: Weak<[i8]>,
1529 },
1530}
1531
1532impl VNextResidentProductTokenMaskContent {
1533 fn from_requested(requested: &VNextProductTokenMaskContent) -> Self {
1534 match requested {
1535 VNextProductTokenMaskContent::AllValid { vocabulary_size } => Self::AllValid {
1536 vocabulary_size: *vocabulary_size,
1537 },
1538 VNextProductTokenMaskContent::Selection {
1539 vocabulary_size,
1540 fingerprint,
1541 valid_token_mask,
1542 } => Self::Selection {
1543 vocabulary_size: *vocabulary_size,
1544 fingerprint: *fingerprint,
1545 source_len: valid_token_mask.len(),
1546 valid_token_mask: Arc::downgrade(valid_token_mask),
1547 },
1548 }
1549 }
1550
1551 fn exactly_matches(&self, requested: &VNextProductTokenMaskContent) -> bool {
1552 match (self, requested) {
1553 (
1554 Self::AllValid {
1555 vocabulary_size: resident_vocabulary_size,
1556 },
1557 VNextProductTokenMaskContent::AllValid {
1558 vocabulary_size: requested_vocabulary_size,
1559 },
1560 ) => resident_vocabulary_size == requested_vocabulary_size,
1561 (
1562 Self::Selection {
1563 vocabulary_size: resident_vocabulary_size,
1564 fingerprint: resident_fingerprint,
1565 source_len,
1566 valid_token_mask: resident_mask,
1567 },
1568 VNextProductTokenMaskContent::Selection {
1569 vocabulary_size: requested_vocabulary_size,
1570 fingerprint: requested_fingerprint,
1571 valid_token_mask: requested_mask,
1572 },
1573 ) => {
1574 if resident_vocabulary_size != requested_vocabulary_size
1575 || resident_fingerprint != requested_fingerprint
1576 || *source_len != requested_mask.len()
1577 {
1578 return false;
1579 }
1580 resident_mask.upgrade().is_some_and(|resident_mask| {
1581 Arc::ptr_eq(&resident_mask, requested_mask)
1582 || resident_mask.as_ref() == requested_mask.as_ref()
1583 })
1584 }
1585 _ => false,
1586 }
1587 }
1588}
1589
1590#[derive(Debug, Clone, PartialEq, Eq)]
1591enum VNextProductTokenMaskSlotIdentity {
1592 LaneStable(Arc<LaneStableArenaSlotIdentity>),
1593 #[cfg(test)]
1594 Test(u64),
1595}
1596
1597impl VNextProductTokenMaskSlotIdentity {
1598 fn slot_id(&self) -> u64 {
1599 match self {
1600 Self::LaneStable(identity) => identity.slot_id(),
1601 #[cfg(test)]
1602 Self::Test(slot_id) => *slot_id,
1603 }
1604 }
1605}
1606
1607#[derive(Debug, Clone, PartialEq, Eq)]
1608struct VNextProductTokenMaskSlotTarget {
1609 identity: VNextProductTokenMaskSlotIdentity,
1610 participant_index: usize,
1611}
1612
1613impl VNextProductTokenMaskSlotTarget {
1614 fn cache_key(&self) -> (u64, usize) {
1615 (self.identity.slot_id(), self.participant_index)
1616 }
1617}
1618
1619#[derive(Debug)]
1620struct VNextResidentProductTokenMaskEntry {
1621 identity: VNextProductTokenMaskSlotIdentity,
1622 content: VNextResidentProductTokenMaskContent,
1623}
1624
1625#[derive(Debug, Clone)]
1626struct VNextProductTokenMaskSubmissionPlan {
1627 target: Option<VNextProductTokenMaskSlotTarget>,
1628 content: VNextProductTokenMaskContent,
1629 upload_required: bool,
1630}
1631
1632#[derive(Debug, Default)]
1633struct VNextProductTokenMaskResidency {
1634 entries: BTreeMap<(u64, usize), VNextResidentProductTokenMaskEntry>,
1635}
1636
1637impl VNextProductTokenMaskResidency {
1638 fn prepare(
1639 &mut self,
1640 target: Option<VNextProductTokenMaskSlotTarget>,
1641 content: VNextProductTokenMaskContent,
1642 ) -> VNextProductTokenMaskSubmissionPlan {
1643 let upload_required = target.as_ref().is_none_or(|target| {
1644 let key = target.cache_key();
1645 let hit = self.entries.get(&key).is_some_and(|entry| {
1646 entry.identity == target.identity && entry.content.exactly_matches(&content)
1647 });
1648 if !hit {
1649 self.entries.remove(&key);
1652 }
1653 !hit
1654 });
1655 VNextProductTokenMaskSubmissionPlan {
1656 target,
1657 content,
1658 upload_required,
1659 }
1660 }
1661
1662 fn publish(&mut self, plans: &[VNextProductTokenMaskSubmissionPlan]) {
1663 let cacheable = plans
1664 .iter()
1665 .filter(|plan| plan.upload_required && plan.target.is_some())
1666 .count();
1667 if cacheable == 0 {
1668 return;
1669 }
1670 if cacheable > MAX_PRODUCT_TOKEN_MASK_SLOT_CACHE_ENTRIES {
1671 self.entries.clear();
1672 return;
1673 }
1674 let new_entries = plans
1675 .iter()
1676 .filter(|plan| plan.upload_required)
1677 .filter_map(|plan| plan.target.as_ref())
1678 .filter(|target| !self.entries.contains_key(&target.cache_key()))
1679 .count();
1680 if self.entries.len().saturating_add(new_entries)
1681 > MAX_PRODUCT_TOKEN_MASK_SLOT_CACHE_ENTRIES
1682 {
1683 self.entries.clear();
1686 }
1687 for plan in plans {
1688 if plan.upload_required {
1689 let Some(target) = &plan.target else {
1690 continue;
1691 };
1692 self.entries.insert(
1693 target.cache_key(),
1694 VNextResidentProductTokenMaskEntry {
1695 identity: target.identity.clone(),
1696 content: VNextResidentProductTokenMaskContent::from_requested(
1697 &plan.content,
1698 ),
1699 },
1700 );
1701 }
1702 }
1703 }
1704
1705 fn clear(&mut self) {
1706 self.entries.clear();
1707 }
1708}
1709
1710struct VNextProductTokenMaskResidencyTransaction<'a> {
1711 residency: &'a Mutex<VNextProductTokenMaskResidency>,
1712 plans: Vec<VNextProductTokenMaskSubmissionPlan>,
1713 published: bool,
1714 settled: bool,
1715}
1716
1717impl<'a> VNextProductTokenMaskResidencyTransaction<'a> {
1718 fn prepare(
1719 residency: &'a Mutex<VNextProductTokenMaskResidency>,
1720 slot_identity: Option<LaneStableArenaSlotIdentity>,
1721 contents: impl IntoIterator<Item = VNextProductTokenMaskContent>,
1722 ) -> Self {
1723 let slot_identity = slot_identity
1724 .map(Arc::new)
1725 .map(VNextProductTokenMaskSlotIdentity::LaneStable);
1726 Self::prepare_with_identity(residency, slot_identity, contents)
1727 }
1728
1729 fn prepare_with_identity(
1730 residency: &'a Mutex<VNextProductTokenMaskResidency>,
1731 slot_identity: Option<VNextProductTokenMaskSlotIdentity>,
1732 contents: impl IntoIterator<Item = VNextProductTokenMaskContent>,
1733 ) -> Self {
1734 let mut ledger = residency.lock();
1735 let plans = contents
1736 .into_iter()
1737 .enumerate()
1738 .map(|(participant_index, content)| {
1739 let target =
1740 slot_identity
1741 .clone()
1742 .map(|identity| VNextProductTokenMaskSlotTarget {
1743 identity,
1744 participant_index,
1745 });
1746 ledger.prepare(target, content)
1747 })
1748 .collect();
1749 drop(ledger);
1750 Self {
1751 residency,
1752 plans,
1753 published: false,
1754 settled: false,
1755 }
1756 }
1757
1758 #[cfg(test)]
1759 fn prepare_for_test(
1760 residency: &'a Mutex<VNextProductTokenMaskResidency>,
1761 slot_id: Option<u64>,
1762 contents: impl IntoIterator<Item = VNextProductTokenMaskContent>,
1763 ) -> Self {
1764 Self::prepare_with_identity(
1765 residency,
1766 slot_id.map(VNextProductTokenMaskSlotIdentity::Test),
1767 contents,
1768 )
1769 }
1770
1771 fn plans(&self) -> &[VNextProductTokenMaskSubmissionPlan] {
1772 &self.plans
1773 }
1774
1775 fn publish(&mut self) {
1776 debug_assert!(!self.published);
1777 if self
1778 .plans
1779 .iter()
1780 .any(|plan| plan.upload_required && plan.target.is_some())
1781 {
1782 self.residency.lock().publish(&self.plans);
1783 }
1784 self.published = true;
1785 }
1786
1787 fn settle_success(&mut self) {
1788 debug_assert!(self.published);
1789 self.settled = true;
1790 }
1791
1792 fn invalidate_before_slot_release(&mut self) {
1793 debug_assert!(!self.published);
1794 self.residency.lock().clear();
1795 self.settled = true;
1796 }
1797}
1798
1799impl Drop for VNextProductTokenMaskResidencyTransaction<'_> {
1800 fn drop(&mut self) {
1801 if !self.settled {
1802 self.residency.lock().clear();
1805 }
1806 }
1807}
1808
1809#[derive(Debug, Clone, Copy)]
1810struct VNextProductRepetitionInput<'a> {
1811 token_ids: &'a [u32],
1812 penalty: f32,
1813}
1814
1815impl VNextProductRepetitionInput<'_> {
1816 const NONE: Self = Self {
1817 token_ids: &[],
1818 penalty: 1.0,
1819 };
1820
1821 fn is_active(self) -> bool {
1822 !self.token_ids.is_empty() && self.penalty != 1.0
1823 }
1824}
1825
1826fn product_output_mode_for_policies<'a>(
1827 kind: VNextExecutionWaveKind,
1828 policies: impl IntoIterator<Item = Option<&'a LogitsReturnPolicy>>,
1829) -> VNextProductOutputMode {
1830 if kind != VNextExecutionWaveKind::Decode {
1831 return VNextProductOutputMode::FullLogits;
1832 }
1833 let mut has_participant = false;
1834 for policy in policies {
1835 has_participant = true;
1836 if !matches!(policy, Some(LogitsReturnPolicy::GreedyArgmax { .. })) {
1837 return VNextProductOutputMode::FullLogits;
1838 }
1839 }
1840 if has_participant {
1841 VNextProductOutputMode::GreedyToken
1842 } else {
1843 VNextProductOutputMode::FullLogits
1844 }
1845}
1846
1847fn normalized_product_token_mask(
1848 policy: Option<&LogitsReturnPolicy>,
1849 output_mode: VNextProductOutputMode,
1850 vocabulary_size: usize,
1851) -> Vec<u8> {
1852 VNextProductTokenMaskContent::from_policy(policy, output_mode, vocabulary_size).normalized()
1853}
1854
1855fn product_repetition_input(
1856 policy: Option<&LogitsReturnPolicy>,
1857 output_mode: VNextProductOutputMode,
1858) -> VNextProductRepetitionInput<'_> {
1859 if output_mode != VNextProductOutputMode::GreedyToken {
1860 return VNextProductRepetitionInput::NONE;
1861 }
1862 let Some(LogitsReturnPolicy::GreedyArgmax {
1863 repetition_penalty: Some(repetition),
1864 ..
1865 }) = policy
1866 else {
1867 return VNextProductRepetitionInput::NONE;
1868 };
1869 if repetition.is_empty() {
1870 VNextProductRepetitionInput::NONE
1871 } else {
1872 VNextProductRepetitionInput {
1873 token_ids: repetition.token_ids(),
1874 penalty: repetition.penalty(),
1875 }
1876 }
1877}
1878
1879fn padded_repetition_token_id_bytes(token_ids: &[u32], capacity: usize) -> Result<Vec<u8>> {
1880 if token_ids.len() > capacity {
1881 return Err(FerrumError::backend(format!(
1882 "vNext sparse repetition input contains {} ids, capacity is {capacity}",
1883 token_ids.len()
1884 )));
1885 }
1886 let byte_capacity = capacity
1887 .checked_mul(std::mem::size_of::<u32>())
1888 .ok_or_else(|| FerrumError::backend("vNext repetition token-id bytes overflow usize"))?;
1889 let mut bytes = vec![0_u8; byte_capacity];
1890 for (destination, token_id) in bytes
1891 .chunks_exact_mut(std::mem::size_of::<u32>())
1892 .zip(token_ids)
1893 {
1894 destination.copy_from_slice(&token_id.to_le_bytes());
1895 }
1896 Ok(bytes)
1897}
1898
1899fn decode_selected_token(bytes: &[u8], vocabulary_size: usize) -> Result<TokenId> {
1900 let token_bytes: [u8; 4] = bytes.try_into().map_err(|_| {
1901 FerrumError::backend(format!(
1902 "vNext selected-token readback contains {} bytes, expected 4",
1903 bytes.len()
1904 ))
1905 })?;
1906 let token = u32::from_le_bytes(token_bytes);
1907 if usize::try_from(token)
1908 .ok()
1909 .is_none_or(|token| token >= vocabulary_size)
1910 {
1911 return Err(FerrumError::backend(format!(
1912 "vNext masked argmax returned invalid token {token} for vocabulary {vocabulary_size}"
1913 )));
1914 }
1915 Ok(TokenId::new(token))
1916}
1917
1918fn apply_teacher_forced_decision(
1919 outputs: &mut [ExecutorSamplingOutput],
1920 decision: VNextTeacherForcedDecision,
1921) -> Result<()> {
1922 let [output] = outputs else {
1923 return Err(FerrumError::internal(format!(
1924 "vNext teacher-forced decision requires one output, got {}",
1925 outputs.len()
1926 )));
1927 };
1928 let ExecutorSamplingOutput::FullLogits(logits) = output else {
1929 return Err(FerrumError::internal(
1930 "vNext teacher-forced decision requires full logits",
1931 ));
1932 };
1933 let token_index = usize::try_from(decision.token_id().get())
1934 .map_err(|_| FerrumError::internal("vNext teacher-forced token id exceeds usize"))?;
1935 let selected = logits.get(token_index).copied().ok_or_else(|| {
1936 FerrumError::internal(format!(
1937 "vNext teacher-forced token {} at decision {} exceeds logits width {}",
1938 decision.token_id().get(),
1939 decision.token_index(),
1940 logits.len()
1941 ))
1942 })?;
1943 if !selected.is_finite() {
1944 return Err(FerrumError::model(format!(
1945 "vNext teacher-forced token {} at decision {} has a non-finite raw logit",
1946 decision.token_id().get(),
1947 decision.token_index()
1948 )));
1949 }
1950 logits.fill(f32::NEG_INFINITY);
1951 logits[token_index] = 0.0;
1952 Ok(())
1953}
1954
1955fn nonterminal_completion_message(observation: &CompletionReadbackBatchObservation) -> String {
1956 format!("vNext completion did not reach a quiescent terminal: {observation:?}")
1957}
1958
1959fn decode_output_width(output_elements: usize, vocabulary_size: usize) -> Result<usize> {
1960 if output_elements == 1 || output_elements == vocabulary_size {
1961 return Ok(output_elements);
1962 }
1963 Err(FerrumError::model(format!(
1964 "vNext decode output contains {output_elements} elements, expected one selected token or {vocabulary_size} logits"
1965 )))
1966}
1967
1968enum VNextTerminalReadbacks {
1969 Batch(CompletionReadbackBatchRequest),
1970 Collection(CompletionReadbackCollectionRequest),
1971}
1972
1973impl VNextExecutionWaveKind {
1974 const fn as_str(self) -> &'static str {
1975 match self {
1976 Self::Prefill => "prefill",
1977 Self::Decode => "decode",
1978 }
1979 }
1980
1981 const fn reusable_execution_class(self) -> &'static str {
1982 match self {
1983 Self::Prefill => PACKED_TOKEN_REUSABLE_CLASS,
1984 Self::Decode => UNIFORM_QUERY_REUSABLE_CLASS,
1985 }
1986 }
1987}
1988
1989#[derive(Default)]
1990struct VNextStepAdmissionTimingMetrics {
1991 authority_and_policy_validate: AtomicDurationMetrics,
1992 demand_evaluate: AtomicDurationMetrics,
1993 backing_claim: AtomicDurationMetrics,
1994 logical_capacity_claim: AtomicDurationMetrics,
1995 transaction_validate_and_fingerprint: AtomicDurationMetrics,
1996 frame_capture_and_lease: AtomicDurationMetrics,
1997}
1998
1999impl VNextStepAdmissionTimingMetrics {
2000 fn record(&self, phase: StepResourceAdmissionProfilePhase, duration: Duration) {
2001 match phase {
2002 StepResourceAdmissionProfilePhase::AuthorityAndPolicyValidate => {
2003 &self.authority_and_policy_validate
2004 }
2005 StepResourceAdmissionProfilePhase::DemandEvaluate => &self.demand_evaluate,
2006 StepResourceAdmissionProfilePhase::BackingClaim => &self.backing_claim,
2007 StepResourceAdmissionProfilePhase::LogicalCapacityClaim => &self.logical_capacity_claim,
2008 StepResourceAdmissionProfilePhase::TransactionValidateAndFingerprint => {
2009 &self.transaction_validate_and_fingerprint
2010 }
2011 StepResourceAdmissionProfilePhase::FrameCaptureAndLease => {
2012 &self.frame_capture_and_lease
2013 }
2014 }
2015 .record(duration);
2016 }
2017
2018 fn snapshot(&self) -> serde_json::Value {
2019 serde_json::json!({
2020 "collection": "profile_attached_only",
2021 "authority_and_policy_validate": self.authority_and_policy_validate.snapshot(),
2022 "demand_evaluate": self.demand_evaluate.snapshot(),
2023 "backing_claim": self.backing_claim.snapshot(),
2024 "logical_capacity_claim": self.logical_capacity_claim.snapshot(),
2025 "transaction_validate_and_fingerprint": self.transaction_validate_and_fingerprint.snapshot(),
2026 "frame_capture_and_lease": self.frame_capture_and_lease.snapshot(),
2027 })
2028 }
2029
2030 fn reset(&self) {
2031 for metrics in [
2032 &self.authority_and_policy_validate,
2033 &self.demand_evaluate,
2034 &self.backing_claim,
2035 &self.logical_capacity_claim,
2036 &self.transaction_validate_and_fingerprint,
2037 &self.frame_capture_and_lease,
2038 ] {
2039 metrics.reset();
2040 }
2041 }
2042}
2043
2044#[derive(Default)]
2045struct VNextWaveTimingMetrics {
2046 resource_prepare_attempt: AtomicDurationMetrics,
2047 resource_step_request_prepare: AtomicDurationMetrics,
2048 resource_step_admission: AtomicDurationMetrics,
2049 resource_step_admission_breakdown: VNextStepAdmissionTimingMetrics,
2050 resource_submission_wave_prepare: AtomicDurationMetrics,
2051 host_encode_submit: AtomicDurationMetrics,
2052 token_upload_prepare: AtomicDurationMetrics,
2053 wave_identity_bind: AtomicDurationMetrics,
2054 provider_encode_submit: AtomicDurationMetrics,
2055 contract_validate_reserve: AtomicDurationMetrics,
2056 backing_input_encode: AtomicDurationMetrics,
2057 provider_node_encode: AtomicDurationMetrics,
2058 lane_reserve_submit_arm: AtomicDurationMetrics,
2059 lane_reserve: AtomicDurationMetrics,
2060 device_runtime_submit: AtomicDurationMetrics,
2061 device_submit_validate_prepare: AtomicDurationMetrics,
2062 device_submit_begin_timing: AtomicDurationMetrics,
2063 device_submit_enqueue_commands: AtomicDurationMetrics,
2064 device_submit_record_fence_account: AtomicDurationMetrics,
2065 reusable_execution: VNextReusableExecutionMetrics,
2066 completion_arm: AtomicDurationMetrics,
2067 completion_round_trip: AtomicDurationMetrics,
2068 host_postprocess: AtomicDurationMetrics,
2069 submitted_wave_total: AtomicDurationMetrics,
2070}
2071
2072#[derive(Default)]
2073struct VNextPreparedWaveTopologyMetrics {
2074 wave_authorities: AtomicU64,
2075 covered_nodes: AtomicU64,
2076 participant_flights: AtomicU64,
2077 node_participant_projections: AtomicU64,
2078 physical_ledger_entries: AtomicU64,
2079}
2080
2081impl VNextPreparedWaveTopologyMetrics {
2082 fn record<R: DeviceRuntime>(&self, wave: &PreparedStepSubmissionWave<R>) {
2083 self.record_counts(
2084 wave.node_count(),
2085 wave.prepared_participant_flight_count(),
2086 wave.node_participant_projection_count(),
2087 wave.physical_invocation_ledger_entry_count(),
2088 );
2089 }
2090
2091 fn record_counts(
2092 &self,
2093 covered_nodes: usize,
2094 participant_flights: usize,
2095 node_participant_projections: usize,
2096 physical_ledger_entries: usize,
2097 ) {
2098 self.wave_authorities.fetch_add(1, Ordering::Relaxed);
2099 self.covered_nodes.fetch_add(
2100 u64::try_from(covered_nodes).unwrap_or(u64::MAX),
2101 Ordering::Relaxed,
2102 );
2103 self.participant_flights.fetch_add(
2104 u64::try_from(participant_flights).unwrap_or(u64::MAX),
2105 Ordering::Relaxed,
2106 );
2107 self.node_participant_projections.fetch_add(
2108 u64::try_from(node_participant_projections).unwrap_or(u64::MAX),
2109 Ordering::Relaxed,
2110 );
2111 self.physical_ledger_entries.fetch_add(
2112 u64::try_from(physical_ledger_entries).unwrap_or(u64::MAX),
2113 Ordering::Relaxed,
2114 );
2115 }
2116
2117 fn snapshot(&self) -> serde_json::Value {
2118 serde_json::json!({
2119 "wave_authorities": self.wave_authorities.load(Ordering::Relaxed),
2120 "covered_nodes": self.covered_nodes.load(Ordering::Relaxed),
2121 "participant_flights": self.participant_flights.load(Ordering::Relaxed),
2122 "node_participant_projections": self.node_participant_projections.load(Ordering::Relaxed),
2123 "physical_ledger_entries": self.physical_ledger_entries.load(Ordering::Relaxed),
2124 })
2125 }
2126
2127 fn reset(&self) {
2128 for counter in [
2129 &self.wave_authorities,
2130 &self.covered_nodes,
2131 &self.participant_flights,
2132 &self.node_participant_projections,
2133 &self.physical_ledger_entries,
2134 ] {
2135 counter.store(0, Ordering::Relaxed);
2136 }
2137 }
2138}
2139
2140impl VNextWaveTimingMetrics {
2141 fn snapshot(&self) -> serde_json::Value {
2142 serde_json::json!({
2143 "clock": "host_monotonic",
2144 "scope": "executor_host_wall_boundaries",
2145 "resource_prepare_attempt": self.resource_prepare_attempt.snapshot(),
2146 "resource_prepare_breakdown": {
2147 "collection": "profile_attached_only",
2148 "step_request_prepare": self.resource_step_request_prepare.snapshot(),
2149 "step_admission": self.resource_step_admission.snapshot(),
2150 "step_admission_breakdown": self.resource_step_admission_breakdown.snapshot(),
2151 "submission_wave_prepare": self.resource_submission_wave_prepare.snapshot(),
2152 },
2153 "host_encode_submit": self.host_encode_submit.snapshot(),
2154 "host_encode_submit_breakdown": {
2155 "collection": "profile_attached_only",
2156 "token_upload_prepare": self.token_upload_prepare.snapshot(),
2157 "wave_identity_bind": self.wave_identity_bind.snapshot(),
2158 "provider_encode_submit": self.provider_encode_submit.snapshot(),
2159 "provider_encode_submit_breakdown": {
2160 "contract_validate_reserve": self.contract_validate_reserve.snapshot(),
2161 "backing_input_encode": self.backing_input_encode.snapshot(),
2162 "provider_node_encode": self.provider_node_encode.snapshot(),
2163 "lane_reserve_submit_arm": self.lane_reserve_submit_arm.snapshot(),
2164 "lane_reserve_submit_arm_breakdown": {
2165 "lane_reserve": self.lane_reserve.snapshot(),
2166 "device_runtime_submit": self.device_runtime_submit.snapshot(),
2167 "device_runtime_submit_breakdown": {
2168 "validate_and_prepare": self.device_submit_validate_prepare.snapshot(),
2169 "begin_timing": self.device_submit_begin_timing.snapshot(),
2170 "enqueue_commands": self.device_submit_enqueue_commands.snapshot(),
2171 "record_fence_and_account": self.device_submit_record_fence_account.snapshot(),
2172 "reusable_execution": self.reusable_execution.snapshot(),
2173 },
2174 "completion_arm": self.completion_arm.snapshot(),
2175 },
2176 },
2177 },
2178 "completion_round_trip": self.completion_round_trip.snapshot(),
2179 "host_postprocess": self.host_postprocess.snapshot(),
2180 "submitted_wave_total": self.submitted_wave_total.snapshot(),
2181 "limitations": [
2182 "resource_prepare_attempt includes capacity-deferred attempts and is outside submitted_wave_total",
2183 "resource_prepare breakdown is collected only while a typed profile sink is attached",
2184 "resource_prepare breakdown samples low-level admission attempts; retries may produce more breakdown samples than outer resource_prepare_attempt samples",
2185 "step_admission breakdown records completed admission phases; an error returned inside a phase remains in the outer step_admission interval",
2186 "resource_prepare residual includes caller-side participant construction and orchestration not attributed to the three child intervals",
2187 "host_encode_submit breakdown is collected only while a typed profile sink is attached",
2188 "provider_encode_submit breakdown covers contract validation and completion reservation, backing/input encoding, provider node encoding, and lane reserve/submit/arm",
2189 "lane_reserve_submit_arm breakdown isolates lane acquisition, DeviceRuntime::submit, and successful completion arming; failed submissions do not emit completion_arm",
2190 "device_runtime_submit breakdown isolates backend validation/preparation, timing start, ordered command enqueue, and fence/accounting for runtimes that implement typed attribution",
2191 "completion_round_trip includes async queue wait, device fence wait, and readback",
2192 "these host intervals are not kernel or device-busy time"
2193 ],
2194 })
2195 }
2196
2197 fn reset(&self) {
2198 for metrics in [
2199 &self.resource_prepare_attempt,
2200 &self.resource_step_request_prepare,
2201 &self.resource_step_admission,
2202 &self.resource_submission_wave_prepare,
2203 &self.host_encode_submit,
2204 &self.token_upload_prepare,
2205 &self.wave_identity_bind,
2206 &self.provider_encode_submit,
2207 &self.contract_validate_reserve,
2208 &self.backing_input_encode,
2209 &self.provider_node_encode,
2210 &self.lane_reserve_submit_arm,
2211 &self.lane_reserve,
2212 &self.device_runtime_submit,
2213 &self.device_submit_validate_prepare,
2214 &self.device_submit_begin_timing,
2215 &self.device_submit_enqueue_commands,
2216 &self.device_submit_record_fence_account,
2217 &self.completion_arm,
2218 &self.completion_round_trip,
2219 &self.host_postprocess,
2220 &self.submitted_wave_total,
2221 ] {
2222 metrics.reset();
2223 }
2224 self.resource_step_admission_breakdown.reset();
2225 self.reusable_execution.reset();
2226 }
2227}
2228
2229#[derive(Default)]
2230struct VNextReusableExecutionMetrics {
2231 candidate_segments: AtomicU64,
2232 captured_segments: AtomicU64,
2233 uploaded_segments: AtomicU64,
2234 cache_hit_segments: AtomicU64,
2235 cached_rejected_segments: AtomicU64,
2236 capture_rejected_segments: AtomicU64,
2237 quiescence_deferred_segments: AtomicU64,
2238 warmup_required_segments: AtomicU64,
2239 capacity_deferred_segments: AtomicU64,
2240 outside_preparation_segments: AtomicU64,
2241 evicted_segments: AtomicU64,
2242 replayed_segments: AtomicU64,
2243 replayed_commands: AtomicU64,
2244 eager_commands: AtomicU64,
2245}
2246
2247impl VNextReusableExecutionMetrics {
2248 fn record(&self, observation: DeviceReusableExecutionObservation) {
2249 self.candidate_segments
2250 .fetch_add(observation.candidate_segments(), Ordering::Relaxed);
2251 self.captured_segments
2252 .fetch_add(observation.captured_segments(), Ordering::Relaxed);
2253 self.uploaded_segments
2254 .fetch_add(observation.uploaded_segments(), Ordering::Relaxed);
2255 self.cache_hit_segments
2256 .fetch_add(observation.cache_hit_segments(), Ordering::Relaxed);
2257 self.cached_rejected_segments
2258 .fetch_add(observation.cached_rejected_segments(), Ordering::Relaxed);
2259 self.capture_rejected_segments
2260 .fetch_add(observation.capture_rejected_segments(), Ordering::Relaxed);
2261 self.quiescence_deferred_segments.fetch_add(
2262 observation.quiescence_deferred_segments(),
2263 Ordering::Relaxed,
2264 );
2265 self.warmup_required_segments
2266 .fetch_add(observation.warmup_required_segments(), Ordering::Relaxed);
2267 self.capacity_deferred_segments
2268 .fetch_add(observation.capacity_deferred_segments(), Ordering::Relaxed);
2269 self.outside_preparation_segments.fetch_add(
2270 observation.outside_preparation_segments(),
2271 Ordering::Relaxed,
2272 );
2273 self.evicted_segments
2274 .fetch_add(observation.evicted_segments(), Ordering::Relaxed);
2275 self.replayed_segments
2276 .fetch_add(observation.replayed_segments(), Ordering::Relaxed);
2277 self.replayed_commands
2278 .fetch_add(observation.replayed_commands(), Ordering::Relaxed);
2279 self.eager_commands
2280 .fetch_add(observation.eager_commands(), Ordering::Relaxed);
2281 }
2282
2283 fn snapshot(&self) -> serde_json::Value {
2284 serde_json::json!({
2285 "candidate_segments": self.candidate_segments.load(Ordering::Relaxed),
2286 "captured_segments": self.captured_segments.load(Ordering::Relaxed),
2287 "uploaded_segments": self.uploaded_segments.load(Ordering::Relaxed),
2288 "cache_hit_segments": self.cache_hit_segments.load(Ordering::Relaxed),
2289 "cached_rejected_segments": self.cached_rejected_segments.load(Ordering::Relaxed),
2290 "capture_rejected_segments": self.capture_rejected_segments.load(Ordering::Relaxed),
2291 "quiescence_deferred_segments": self.quiescence_deferred_segments.load(Ordering::Relaxed),
2292 "warmup_required_segments": self.warmup_required_segments.load(Ordering::Relaxed),
2293 "capacity_deferred_segments": self.capacity_deferred_segments.load(Ordering::Relaxed),
2294 "outside_preparation_segments": self.outside_preparation_segments.load(Ordering::Relaxed),
2295 "evicted_segments": self.evicted_segments.load(Ordering::Relaxed),
2296 "replayed_segments": self.replayed_segments.load(Ordering::Relaxed),
2297 "replayed_commands": self.replayed_commands.load(Ordering::Relaxed),
2298 "eager_commands": self.eager_commands.load(Ordering::Relaxed),
2299 })
2300 }
2301
2302 fn reset(&self) {
2303 for counter in [
2304 &self.candidate_segments,
2305 &self.captured_segments,
2306 &self.uploaded_segments,
2307 &self.cache_hit_segments,
2308 &self.cached_rejected_segments,
2309 &self.capture_rejected_segments,
2310 &self.quiescence_deferred_segments,
2311 &self.warmup_required_segments,
2312 &self.capacity_deferred_segments,
2313 &self.outside_preparation_segments,
2314 &self.evicted_segments,
2315 &self.replayed_segments,
2316 &self.replayed_commands,
2317 &self.eager_commands,
2318 ] {
2319 counter.store(0, Ordering::Relaxed);
2320 }
2321 }
2322}
2323
2324impl DeviceSubmissionTimingSink for VNextWaveTimingMetrics {
2325 const ENABLED: bool = true;
2326
2327 fn record_device_submission(&self, stage: DeviceSubmissionStage, elapsed: Duration) {
2328 match stage {
2329 DeviceSubmissionStage::ValidateAndPrepare => {
2330 self.device_submit_validate_prepare.record(elapsed)
2331 }
2332 DeviceSubmissionStage::BeginTiming => self.device_submit_begin_timing.record(elapsed),
2333 DeviceSubmissionStage::EnqueueCommands => {
2334 self.device_submit_enqueue_commands.record(elapsed)
2335 }
2336 DeviceSubmissionStage::RecordFenceAndAccount => {
2337 self.device_submit_record_fence_account.record(elapsed)
2338 }
2339 }
2340 }
2341
2342 fn record_reusable_execution(&self, observation: DeviceReusableExecutionObservation) {
2343 self.reusable_execution.record(observation);
2344 }
2345}
2346
2347impl SubmissionWaveDispatchTimingSink for VNextWaveTimingMetrics {
2348 fn record(&self, stage: SubmissionWaveDispatchStage, elapsed: Duration) {
2349 match stage {
2350 SubmissionWaveDispatchStage::ContractValidateAndReserve => {
2351 self.contract_validate_reserve.record(elapsed)
2352 }
2353 SubmissionWaveDispatchStage::BackingAndInputEncode => {
2354 self.backing_input_encode.record(elapsed)
2355 }
2356 SubmissionWaveDispatchStage::ProviderNodeEncode => {
2357 self.provider_node_encode.record(elapsed)
2358 }
2359 SubmissionWaveDispatchStage::LaneReserve => self.lane_reserve.record(elapsed),
2360 SubmissionWaveDispatchStage::DeviceRuntimeSubmit => {
2361 self.device_runtime_submit.record(elapsed)
2362 }
2363 SubmissionWaveDispatchStage::CompletionArm => self.completion_arm.record(elapsed),
2364 SubmissionWaveDispatchStage::LaneReserveSubmitAndArm => {
2365 self.lane_reserve_submit_arm.record(elapsed)
2366 }
2367 }
2368 }
2369}
2370
2371struct VNextWaveTimingSink<'metrics> {
2372 aggregate: &'metrics VNextWaveTimingMetrics,
2373 phase: &'metrics VNextWaveTimingMetrics,
2374}
2375
2376impl DeviceSubmissionTimingSink for VNextWaveTimingSink<'_> {
2377 const ENABLED: bool = true;
2378
2379 fn record_device_submission(&self, stage: DeviceSubmissionStage, elapsed: Duration) {
2380 self.aggregate.record_device_submission(stage, elapsed);
2381 self.phase.record_device_submission(stage, elapsed);
2382 }
2383
2384 fn record_reusable_execution(&self, observation: DeviceReusableExecutionObservation) {
2385 self.aggregate.record_reusable_execution(observation);
2386 self.phase.record_reusable_execution(observation);
2387 }
2388}
2389
2390impl SubmissionWaveDispatchTimingSink for VNextWaveTimingSink<'_> {
2391 fn record(&self, stage: SubmissionWaveDispatchStage, elapsed: Duration) {
2392 SubmissionWaveDispatchTimingSink::record(self.aggregate, stage, elapsed);
2393 SubmissionWaveDispatchTimingSink::record(self.phase, stage, elapsed);
2394 }
2395}
2396
2397#[derive(Default)]
2398struct VNextPhysicalSpanDurationSummary {
2399 samples: u64,
2400 total_ns: u64,
2401 max_ns: u64,
2402}
2403
2404impl VNextPhysicalSpanDurationSummary {
2405 fn record(&mut self, elapsed_ns: u64) {
2406 self.samples = self.samples.saturating_add(1);
2407 self.total_ns = self.total_ns.saturating_add(elapsed_ns);
2408 self.max_ns = self.max_ns.max(elapsed_ns);
2409 }
2410
2411 fn snapshot(&self) -> serde_json::Value {
2412 serde_json::json!({
2413 "samples": self.samples,
2414 "total_ns": self.total_ns,
2415 "average_us": if self.samples == 0 {
2416 0.0
2417 } else {
2418 self.total_ns as f64 / self.samples as f64 / 1_000.0
2419 },
2420 "max_us": self.max_ns as f64 / 1_000.0,
2421 })
2422 }
2423}
2424
2425#[derive(Default)]
2426struct VNextPhysicalSpanTimingMetrics {
2427 measured_submissions: AtomicU64,
2428 unavailable_submissions: AtomicU64,
2429 eager_commands: AtomicDurationMetrics,
2430 reusable_executables: AtomicDurationMetrics,
2431 unavailable_spans: AtomicU64,
2432 reusable_without_fingerprint: AtomicU64,
2433 fingerprint_capacity_exhausted: AtomicU64,
2434 reusable_by_fingerprint: Mutex<BTreeMap<String, VNextPhysicalSpanDurationSummary>>,
2435}
2436
2437impl VNextPhysicalSpanTimingMetrics {
2438 fn record(&self, measurement: &DeviceTimingMeasurement<DeviceSubmissionExecutionTiming>) {
2439 let timing = match measurement {
2440 DeviceTimingMeasurement::Measured(timing) => {
2441 self.measured_submissions.fetch_add(1, Ordering::Relaxed);
2442 timing
2443 }
2444 DeviceTimingMeasurement::Unavailable(_) => {
2445 self.unavailable_submissions.fetch_add(1, Ordering::Relaxed);
2446 return;
2447 }
2448 DeviceTimingMeasurement::NotRequested => return,
2449 };
2450 for span in timing.spans() {
2451 let Some(elapsed_ns) = span.measurement().elapsed_ns() else {
2452 self.unavailable_spans.fetch_add(1, Ordering::Relaxed);
2453 continue;
2454 };
2455 match span.kind() {
2456 DeviceExecutionSpanKind::EagerCommand => {
2457 self.eager_commands.record(Duration::from_nanos(elapsed_ns));
2458 }
2459 DeviceExecutionSpanKind::ReusableExecutable => {
2460 self.reusable_executables
2461 .record(Duration::from_nanos(elapsed_ns));
2462 let Some(fingerprint) = span.reusable_executable_fingerprint() else {
2463 self.reusable_without_fingerprint
2464 .fetch_add(1, Ordering::Relaxed);
2465 continue;
2466 };
2467 let mut summaries = self.reusable_by_fingerprint.lock();
2468 if let Some(summary) = summaries.get_mut(fingerprint) {
2469 summary.record(elapsed_ns);
2470 } else if summaries.len() < MAX_PROFILED_REUSABLE_EXECUTABLES {
2471 let mut summary = VNextPhysicalSpanDurationSummary::default();
2472 summary.record(elapsed_ns);
2473 summaries.insert(fingerprint.to_owned(), summary);
2474 } else {
2475 self.fingerprint_capacity_exhausted
2476 .fetch_add(1, Ordering::Relaxed);
2477 }
2478 }
2479 }
2480 }
2481 }
2482
2483 fn snapshot(&self) -> serde_json::Value {
2484 let reusable_by_fingerprint = self
2485 .reusable_by_fingerprint
2486 .lock()
2487 .iter()
2488 .map(|(fingerprint, summary)| {
2489 serde_json::json!({
2490 "reusable_executable_fingerprint": fingerprint,
2491 "timing": summary.snapshot(),
2492 })
2493 })
2494 .collect::<Vec<_>>();
2495 serde_json::json!({
2496 "scope": "physical_execution_span",
2497 "measured_submissions": self.measured_submissions.load(Ordering::Relaxed),
2498 "unavailable_submissions": self.unavailable_submissions.load(Ordering::Relaxed),
2499 "eager_commands": self.eager_commands.snapshot(),
2500 "reusable_executables": self.reusable_executables.snapshot(),
2501 "unavailable_spans": self.unavailable_spans.load(Ordering::Relaxed),
2502 "reusable_without_fingerprint": self.reusable_without_fingerprint.load(Ordering::Relaxed),
2503 "fingerprint_capacity": MAX_PROFILED_REUSABLE_EXECUTABLES,
2504 "fingerprint_capacity_exhausted": self.fingerprint_capacity_exhausted.load(Ordering::Relaxed),
2505 "reusable_by_fingerprint": reusable_by_fingerprint,
2506 })
2507 }
2508
2509 fn reset(&self) {
2510 self.measured_submissions.store(0, Ordering::Relaxed);
2511 self.unavailable_submissions.store(0, Ordering::Relaxed);
2512 self.eager_commands.reset();
2513 self.reusable_executables.reset();
2514 self.unavailable_spans.store(0, Ordering::Relaxed);
2515 self.reusable_without_fingerprint
2516 .store(0, Ordering::Relaxed);
2517 self.fingerprint_capacity_exhausted
2518 .store(0, Ordering::Relaxed);
2519 self.reusable_by_fingerprint.lock().clear();
2520 }
2521}
2522
2523#[derive(Default)]
2524struct VNextDeviceTimingMetrics {
2525 device_execution: AtomicDurationMetrics,
2526 fence_wait_host: AtomicDurationMetrics,
2527 readback_host: AtomicDurationMetrics,
2528 physical_spans: VNextPhysicalSpanTimingMetrics,
2529 readback_calls: AtomicU64,
2530 readback_bytes: AtomicU64,
2531 device_unavailable: AtomicU64,
2532 fence_wait_unavailable: AtomicU64,
2533 readback_unavailable: AtomicU64,
2534}
2535
2536impl VNextDeviceTimingMetrics {
2537 fn record(&self, receipt: &CompletionReadbackBatchReceipt) {
2538 let fence = receipt.completion().fence_timing();
2539 self.physical_spans
2540 .record(receipt.completion().submission_timing());
2541 match fence.device_execution() {
2542 DeviceTimingMeasurement::Measured(timing) => self
2543 .device_execution
2544 .record(Duration::from_nanos(timing.elapsed_ns())),
2545 DeviceTimingMeasurement::Unavailable(_) => {
2546 self.device_unavailable.fetch_add(1, Ordering::Relaxed);
2547 }
2548 DeviceTimingMeasurement::NotRequested => {}
2549 }
2550 match fence.blocking_wait_host_ns() {
2551 DeviceTimingMeasurement::Measured(nanoseconds) => self
2552 .fence_wait_host
2553 .record(Duration::from_nanos(nanoseconds)),
2554 DeviceTimingMeasurement::Unavailable(_) => {
2555 self.fence_wait_unavailable.fetch_add(1, Ordering::Relaxed);
2556 }
2557 DeviceTimingMeasurement::NotRequested => {}
2558 }
2559 if let Some(readbacks) = receipt.readback_timings() {
2560 for readback in readbacks {
2561 match readback {
2562 DeviceTimingMeasurement::Measured(timing) => {
2563 self.readback_host
2564 .record(Duration::from_nanos(timing.host_elapsed_ns()));
2565 self.readback_calls
2566 .fetch_add(u64::from(timing.calls()), Ordering::Relaxed);
2567 self.readback_bytes
2568 .fetch_add(timing.bytes(), Ordering::Relaxed);
2569 }
2570 DeviceTimingMeasurement::Unavailable(_) => {
2571 self.readback_unavailable.fetch_add(1, Ordering::Relaxed);
2572 }
2573 DeviceTimingMeasurement::NotRequested => {}
2574 }
2575 }
2576 }
2577 }
2578
2579 fn snapshot(&self) -> serde_json::Value {
2580 serde_json::json!({
2581 "scope": "exact_submission_completion",
2582 "device_execution": self.device_execution.snapshot(),
2583 "fence_wait_host": self.fence_wait_host.snapshot(),
2584 "readback_host": self.readback_host.snapshot(),
2585 "physical_submission_spans": self.physical_spans.snapshot(),
2586 "readback_calls": self.readback_calls.load(Ordering::Relaxed),
2587 "readback_bytes": self.readback_bytes.load(Ordering::Relaxed),
2588 "unavailable": {
2589 "device_execution": self.device_unavailable.load(Ordering::Relaxed),
2590 "fence_wait_host": self.fence_wait_unavailable.load(Ordering::Relaxed),
2591 "readback_host": self.readback_unavailable.load(Ordering::Relaxed),
2592 },
2593 "clocks": {
2594 "device_execution": "backend_device_event_elapsed",
2595 "fence_wait_host": "host_monotonic",
2596 "readback_host": "host_monotonic",
2597 },
2598 "limitations": [
2599 "device execution and fence host wait may overlap and must not be added",
2600 "readback host time includes backend synchronization, host allocation, and transfer",
2601 "device event elapsed has no cross-clock anchor and is diagnostic-only"
2602 ],
2603 })
2604 }
2605
2606 fn reset(&self) {
2607 self.device_execution.reset();
2608 self.fence_wait_host.reset();
2609 self.readback_host.reset();
2610 self.physical_spans.reset();
2611 for counter in [
2612 &self.readback_calls,
2613 &self.readback_bytes,
2614 &self.device_unavailable,
2615 &self.fence_wait_unavailable,
2616 &self.readback_unavailable,
2617 ] {
2618 counter.store(0, Ordering::Relaxed);
2619 }
2620 }
2621}
2622
2623#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2624enum DecodeFailureDisposition {
2625 PreserveForCapacityRetry,
2626 AbortSequence,
2627}
2628
2629impl DecodeFailureDisposition {
2630 fn from_error(error: &FerrumError) -> Self {
2631 if matches!(error, FerrumError::ResourceExhausted { .. }) {
2632 Self::PreserveForCapacityRetry
2633 } else {
2634 Self::AbortSequence
2635 }
2636 }
2637}
2638
2639impl VNextExecutorMetrics {
2640 fn wave_timing_for(&self, kind: VNextExecutionWaveKind) -> &VNextWaveTimingMetrics {
2641 match kind {
2642 VNextExecutionWaveKind::Prefill => &self.prefill_wave_timing,
2643 VNextExecutionWaveKind::Decode => &self.decode_wave_timing,
2644 }
2645 }
2646
2647 fn device_timing_for(&self, kind: VNextExecutionWaveKind) -> &VNextDeviceTimingMetrics {
2648 match kind {
2649 VNextExecutionWaveKind::Prefill => &self.prefill_device_timing,
2650 VNextExecutionWaveKind::Decode => &self.decode_device_timing,
2651 }
2652 }
2653
2654 fn record_failure(&self, message: impl Into<String>) {
2655 self.failed_waves.fetch_add(1, Ordering::Relaxed);
2656 *self.last_failure.lock() = Some(message.into());
2657 }
2658
2659 fn record_reusable_catalog_miss(&self, key: VNextReusableExecutionCatalogMissKey) {
2660 if key.reason.is_epoch_mismatch() {
2661 self.reusable_catalog_epoch_misses
2662 .fetch_add(1, Ordering::Relaxed);
2663 } else {
2664 self.reusable_catalog_misses.fetch_add(1, Ordering::Relaxed);
2665 }
2666 self.reusable_catalog_miss_ledger.lock().record(key);
2667 }
2668
2669 fn average_ms(total_us: u64, operations: u64) -> f64 {
2670 if operations == 0 {
2671 0.0
2672 } else {
2673 total_us as f64 / operations as f64 / 1000.0
2674 }
2675 }
2676
2677 fn reset_after_startup(&self) {
2678 self.prefix_cache.reset();
2679 for counter in [
2680 &self.prefill_operations,
2681 &self.prefill_frontier_narrowings,
2682 &self.decode_operations,
2683 &self.submitted_waves,
2684 &self.completed_waves,
2685 &self.failed_waves,
2686 &self.direct_reusable_waves,
2687 &self.direct_reusable_segments,
2688 &self.direct_reusable_logical_nodes,
2689 &self.direct_reusable_binding_nodes,
2690 &self.direct_reusable_fallbacks,
2691 &self.reusable_catalog_misses,
2692 &self.reusable_catalog_epoch_misses,
2693 &self.identity_waves,
2694 &self.identity_logical_nodes,
2695 &self.identity_nodes_materialized_before_submit,
2696 &self.identity_full_participant_materializations_before_submit,
2697 &self.definitely_not_submitted_retries,
2698 &self.request_deferrals,
2699 &self.sequence_deferrals,
2700 &self.extension_deferrals,
2701 &self.step_deferrals,
2702 &self.wave_deferrals,
2703 &self.backing_deferrals,
2704 &self.uploaded_bytes,
2705 &self.readback_bytes,
2706 &self.full_logits_readback_waves,
2707 &self.greedy_token_readback_waves,
2708 &self.greedy_policy_fallback_waves,
2709 &self.token_mask_upload_participants,
2710 &self.token_mask_cache_hit_participants,
2711 &self.sparse_repetition_waves,
2712 &self.sparse_repetition_participants,
2713 &self.sparse_repetition_token_ids_uploaded,
2714 &self.total_prefill_us,
2715 &self.total_decode_us,
2716 ] {
2717 counter.store(0, Ordering::Relaxed);
2718 }
2719 self.wave_timing.reset();
2720 self.prepared_wave_topology.reset();
2721 self.reusable_catalog_miss_ledger.lock().reset();
2722 self.prefill_wave_timing.reset();
2723 self.decode_wave_timing.reset();
2724 self.device_timing.reset();
2725 self.prefill_device_timing.reset();
2726 self.decode_device_timing.reset();
2727 *self.last_failure.lock() = None;
2728 }
2729}
2730
2731fn reported_allocated_bytes(budget_claimed_bytes: Option<u64>, static_bytes: u64) -> u64 {
2732 budget_claimed_bytes.unwrap_or(static_bytes)
2733}
2734
2735enum JournaledSubmission {
2736 Captured {
2737 receipt: SubmittedOperationReceipt,
2738 selected: Vec<usize>,
2739 },
2740 Suppressed {
2741 slot_id: CompletionSlotId,
2742 },
2743}
2744
2745const VNEXT_MONOTONIC_CLOCK_SOURCE: &str = "rust_std_instant";
2746
2747const fn journal_clock_anchor_required(timing_mode: DeviceTimingMode) -> bool {
2748 matches!(timing_mode, DeviceTimingMode::Kernel)
2749}
2750
2751fn system_time_unix_nanos(time: SystemTime) -> std::result::Result<i64, ExecutionEventSinkError> {
2752 let duration = time.duration_since(UNIX_EPOCH).map_err(|error| {
2753 ExecutionEventSinkError::new(format!("system clock predates Unix epoch: {error}"))
2754 })?;
2755 i64::try_from(duration.as_nanos())
2756 .map_err(|_| ExecutionEventSinkError::new("system clock Unix nanos exceed i64"))
2757}
2758
2759fn bounded_wall_anchor(wall_before: i64, wall_after: i64) -> (i64, u64) {
2760 let lower = wall_before.min(wall_after);
2761 let upper = wall_before.max(wall_after);
2762 let span = upper.saturating_sub(lower);
2763 (
2764 lower.saturating_add(span / 2),
2765 u64::try_from(span).unwrap_or(u64::MAX),
2766 )
2767}
2768
2769fn capture_monotonic_wall_anchor(
2770) -> std::result::Result<(Instant, i64, u64), ExecutionEventSinkError> {
2771 let wall_before = system_time_unix_nanos(SystemTime::now())?;
2772 let started = Instant::now();
2773 let wall_after = system_time_unix_nanos(SystemTime::now())?;
2774 let (wall_anchor_unix_nanos, max_error_nanos) = bounded_wall_anchor(wall_before, wall_after);
2775 Ok((started, wall_anchor_unix_nanos, max_error_nanos))
2776}
2777
2778struct VNextExecutionJournal {
2779 emitter: ExecutionEventEmitter<'static>,
2780 topology: TrustedExecutionTopology,
2781 active: Arc<TrustedActiveSequenceBinding>,
2782 capture_policy: ExecutionEventCapturePolicy,
2783 completed_frames: u64,
2784 started: Instant,
2785 last_timestamp_nanos: u64,
2786 root_span: SpanId,
2787 pending_submission: Option<JournaledSubmission>,
2788 first_failure: Option<IdentifiedFailure>,
2789}
2790
2791impl VNextExecutionJournal {
2792 fn error(error: impl fmt::Display) -> ExecutionEventSinkError {
2793 ExecutionEventSinkError::new(error.to_string())
2794 }
2795
2796 fn open(
2797 sink: Arc<dyn ExecutionEventSink>,
2798 plan: &ExecutionPlan,
2799 active: Arc<TrustedActiveSequenceBinding>,
2800 request_origin: ExecutorRequestOrigin,
2801 ) -> std::result::Result<Self, ExecutionEventSinkError> {
2802 let clock_anchor = journal_clock_anchor_required(sink.device_timing_mode())
2803 .then(capture_monotonic_wall_anchor)
2804 .transpose()?;
2805 let started = clock_anchor
2806 .as_ref()
2807 .map_or_else(Instant::now, |(started, _, _)| *started);
2808 let topology = TrustedExecutionTopology::from_plan(plan).map_err(Self::error)?;
2809 let root_span =
2810 SpanId::new(format!("vnext/request/{}", active.fingerprint())).map_err(Self::error)?;
2811 let capture_policy = sink.capture_policy_for_request(request_origin);
2812 let mut journal = Self {
2813 emitter: ExecutionEventEmitter::from_shared_with_capture_policy(
2814 sink,
2815 active.run_id().clone(),
2816 active.request_id().clone(),
2817 capture_policy,
2818 ),
2819 topology,
2820 active,
2821 capture_policy,
2822 completed_frames: 0,
2823 started,
2824 last_timestamp_nanos: 0,
2825 root_span,
2826 pending_submission: None,
2827 first_failure: None,
2828 };
2829 let accepted_detail = clock_anchor.map_or(
2830 ExecutionEventDetail::None,
2831 |(_, wall_anchor_unix_nanos, max_error_nanos)| {
2832 ExecutionEventDetail::MonotonicClockAnchor {
2833 clock_source: VNEXT_MONOTONIC_CLOCK_SOURCE.to_string(),
2834 wall_anchor_unix_nanos,
2835 max_error_nanos,
2836 }
2837 },
2838 );
2839 let accepted = journal.event(
2840 ExecutionPhase::Resolution,
2841 ExecutionEventKind::RequestAccepted,
2842 journal.base_parts(1, journal.root_span.clone(), None),
2843 accepted_detail,
2844 )?;
2845 let plan_span = SpanId::new(format!("{}/plan", journal.root_span)).map_err(Self::error)?;
2846 let planned_parts =
2847 journal.bind_plan(journal.base_parts(2, plan_span, Some(journal.root_span.clone())));
2848 let planned = journal.event(
2849 ExecutionPhase::Planning,
2850 ExecutionEventKind::PlanBuilt,
2851 planned_parts,
2852 ExecutionEventDetail::None,
2853 )?;
2854 let events = [accepted, planned];
2855 let contexts = [
2856 TrustedExecutionEventContext::pre_plan(
2857 journal.active.run_id(),
2858 journal.active.request_id(),
2859 ),
2860 TrustedExecutionEventContext::bound(
2861 journal.active.run_id(),
2862 journal.active.request_id(),
2863 &journal.topology,
2864 ),
2865 ];
2866 journal.emitter.emit_batch(events.into(), &contexts)?;
2867 Ok(journal)
2868 }
2869
2870 fn next_timestamp(&mut self) -> MonotonicTimestamp {
2871 let elapsed = self.started.elapsed().as_nanos().min(u64::MAX as u128) as u64;
2872 let next = elapsed.max(self.last_timestamp_nanos.saturating_add(1));
2873 self.last_timestamp_nanos = next;
2874 MonotonicTimestamp {
2875 nanos_since_run_start: next,
2876 }
2877 }
2878
2879 fn base_parts(
2880 &self,
2881 sequence: u64,
2882 span_id: SpanId,
2883 parent_span_id: Option<SpanId>,
2884 ) -> ExecutionIdentityParts {
2885 ExecutionIdentityParts {
2886 version: EXECUTION_IDENTITY_VERSION,
2887 run_id: self.active.run_id().clone(),
2888 request_id: self.active.request_id().clone(),
2889 sequence,
2890 plan_id: None,
2891 plan_hash: None,
2892 frame_id: None,
2893 node_invocation_id: None,
2894 node_id: None,
2895 operation_id: None,
2896 provider_id: None,
2897 device_id: None,
2898 resource_pool_id: None,
2899 resource_pool_identity_fingerprint: None,
2900 provisioning_run_id: None,
2901 provisioning_request_id: None,
2902 transaction_id: None,
2903 active_sequence_slot: None,
2904 admission_generation: None,
2905 activation_epoch: None,
2906 runtime_implementation_fingerprint: None,
2907 active_sequence_fingerprint: None,
2908 completed_sequence_fingerprint: None,
2909 aborted_sequence_fingerprint: None,
2910 resource_id: None,
2911 resource_generation: None,
2912 resource_batch_fingerprint: None,
2913 span_id,
2914 parent_span_id,
2915 async_links: Vec::new(),
2916 }
2917 }
2918
2919 fn bind_plan(&self, mut parts: ExecutionIdentityParts) -> ExecutionIdentityParts {
2920 parts.plan_id = Some(self.topology.plan_id().clone());
2921 parts.plan_hash = Some(self.topology.plan_hash().clone());
2922 parts.device_id = Some(self.topology.device_id().clone());
2923 parts.runtime_implementation_fingerprint = Some(
2924 self.topology
2925 .device_runtime_implementation_fingerprint()
2926 .to_owned(),
2927 );
2928 parts
2929 }
2930
2931 fn bind_active(&self, mut parts: ExecutionIdentityParts) -> ExecutionIdentityParts {
2932 let provisioning = self.active.static_provisioning_identity();
2933 parts.resource_pool_id = self.active.static_pool_id();
2934 parts.resource_pool_identity_fingerprint = self.active.static_pool_identity_fingerprint();
2935 parts.provisioning_run_id = provisioning.map(|identity| identity.run_id().clone());
2936 parts.provisioning_request_id = provisioning.map(|identity| identity.request_id().clone());
2937 parts.transaction_id = provisioning.map(|identity| identity.transaction_id().clone());
2938 parts.active_sequence_slot = Some(self.active.sequence_authority().sparse_id());
2939 parts.admission_generation = Some(self.active.sequence_authority().generation());
2940 parts.activation_epoch = Some(self.active.activation_epoch());
2941 parts.active_sequence_fingerprint = Some(self.active.fingerprint().to_owned());
2942 parts
2943 }
2944
2945 fn event(
2946 &mut self,
2947 phase: ExecutionPhase,
2948 kind: ExecutionEventKind,
2949 parts: ExecutionIdentityParts,
2950 detail: ExecutionEventDetail,
2951 ) -> std::result::Result<ExecutionEvent, ExecutionEventSinkError> {
2952 let identity = ExecutionIdentityEnvelope::new(parts).map_err(Self::error)?;
2953 ExecutionEvent::new(self.next_timestamp(), phase, kind, identity, detail)
2954 .map_err(Self::error)
2955 }
2956
2957 fn frame_event(
2958 &mut self,
2959 operation: &ExecutionIdentityEnvelope,
2960 kind: ExecutionEventKind,
2961 ) -> std::result::Result<ExecutionEvent, ExecutionEventSinkError> {
2962 let operation_parts = operation.parts();
2963 let frame_id = operation_parts
2964 .frame_id
2965 .ok_or_else(|| Self::error("operation identity lacks frame id"))?;
2966 let sequence = match kind {
2967 ExecutionEventKind::FrameStarted => operation_parts.sequence.checked_sub(2),
2968 ExecutionEventKind::FrameCompleted => operation_parts.sequence.checked_add(2),
2969 _ => None,
2970 }
2971 .ok_or_else(|| Self::error("frame event sequence overflow"))?;
2972 let frame_span =
2973 SpanId::new(format!("{}/frame/{frame_id}", self.root_span)).map_err(Self::error)?;
2974 let mut parts = self.bind_active(self.bind_plan(self.base_parts(
2975 sequence,
2976 frame_span,
2977 Some(self.root_span.clone()),
2978 )));
2979 parts.frame_id = Some(frame_id);
2980 self.event(
2981 ExecutionPhase::Execution,
2982 kind,
2983 parts,
2984 ExecutionEventDetail::None,
2985 )
2986 }
2987
2988 fn node_event(
2989 &mut self,
2990 operation: &ExecutionIdentityEnvelope,
2991 kind: ExecutionEventKind,
2992 ) -> std::result::Result<ExecutionEvent, ExecutionEventSinkError> {
2993 let mut parts = operation.parts().clone();
2994 let node_span = parts
2995 .parent_span_id
2996 .clone()
2997 .ok_or_else(|| Self::error("operation identity lacks node span"))?;
2998 let frame_id = parts
2999 .frame_id
3000 .ok_or_else(|| Self::error("operation identity lacks frame id"))?;
3001 parts.sequence = match kind {
3002 ExecutionEventKind::NodeStarted => parts.sequence.checked_sub(1),
3003 ExecutionEventKind::NodeRetired => parts.sequence.checked_add(1),
3004 _ => None,
3005 }
3006 .ok_or_else(|| Self::error("node event sequence overflow"))?;
3007 parts.span_id = node_span;
3008 parts.parent_span_id =
3009 Some(SpanId::new(format!("{}/frame/{frame_id}", self.root_span)).map_err(Self::error)?);
3010 self.event(
3011 ExecutionPhase::Execution,
3012 kind,
3013 parts,
3014 ExecutionEventDetail::None,
3015 )
3016 }
3017
3018 fn operation_event(
3019 &mut self,
3020 operation: &ExecutionIdentityEnvelope,
3021 ) -> std::result::Result<ExecutionEvent, ExecutionEventSinkError> {
3022 ExecutionEvent::new(
3023 self.next_timestamp(),
3024 ExecutionPhase::Execution,
3025 ExecutionEventKind::OperationSubmitted,
3026 operation.clone(),
3027 ExecutionEventDetail::None,
3028 )
3029 .map_err(Self::error)
3030 }
3031
3032 fn submitted(
3033 &mut self,
3034 submission: &SubmittedOperationReceipt,
3035 ) -> std::result::Result<(), ExecutionEventSinkError> {
3036 if self.pending_submission.is_some() {
3037 return Err(Self::error(
3038 "execution journal already has an in-flight physical submission",
3039 ));
3040 }
3041 if !self.capture_policy.captures_frame(self.completed_frames) {
3042 self.pending_submission = Some(JournaledSubmission::Suppressed {
3043 slot_id: submission.slot_id(),
3044 });
3045 return Ok(());
3046 }
3047 let selected = submission
3048 .participants()
3049 .iter()
3050 .enumerate()
3051 .filter_map(|(index, participant)| {
3052 let identity = participant.identity().parts();
3053 (&identity.run_id == self.active.run_id()
3054 && &identity.request_id == self.active.request_id())
3055 .then_some(index)
3056 })
3057 .collect::<Vec<_>>();
3058 let Some(&first_index) = selected.first() else {
3059 return Err(Self::error(
3060 "physical submission has no participant for this request journal",
3061 ));
3062 };
3063 let first_identity = submission.participants()[first_index].identity();
3064 let frame_started = self.frame_event(first_identity, ExecutionEventKind::FrameStarted)?;
3065 let node_started = self.node_event(first_identity, ExecutionEventKind::NodeStarted)?;
3066 let operation_submitted = self.operation_event(first_identity)?;
3067 let events = [frame_started, node_started, operation_submitted];
3068 let contexts = [
3069 TrustedExecutionEventContext::active(
3070 self.active.run_id(),
3071 self.active.request_id(),
3072 &self.topology,
3073 &self.active,
3074 ),
3075 TrustedExecutionEventContext::active(
3076 self.active.run_id(),
3077 self.active.request_id(),
3078 &self.topology,
3079 &self.active,
3080 ),
3081 TrustedExecutionEventContext::operation_submitted(
3082 self.active.run_id(),
3083 self.active.request_id(),
3084 &self.topology,
3085 &self.active,
3086 submission,
3087 ),
3088 ];
3089 self.emitter.emit_batch(events.into(), &contexts)?;
3090 self.pending_submission = Some(JournaledSubmission::Captured {
3091 receipt: submission.clone(),
3092 selected,
3093 });
3094 Ok(())
3095 }
3096
3097 fn completed(
3098 &mut self,
3099 completion: &OperationCompletionReceipt,
3100 ) -> std::result::Result<(), ExecutionEventSinkError> {
3101 let pending = self
3102 .pending_submission
3103 .take()
3104 .ok_or_else(|| Self::error("completion has no journaled physical submission"))?;
3105 let JournaledSubmission::Captured {
3106 receipt: submission,
3107 selected,
3108 } = pending
3109 else {
3110 let JournaledSubmission::Suppressed { slot_id } = pending else {
3111 unreachable!();
3112 };
3113 if completion.submission().slot_id() != slot_id {
3114 return Err(Self::error(
3115 "completion differs from the suppressed journal submission",
3116 ));
3117 }
3118 self.completed_frames = self.completed_frames.saturating_add(1);
3119 return Ok(());
3120 };
3121 if completion.submission().fingerprint() != submission.fingerprint() {
3122 return Err(Self::error(
3123 "completion differs from the journaled physical submission",
3124 ));
3125 }
3126 enum CompletionEventEvidence {
3127 Active,
3128 Submitted,
3129 Retired(usize),
3130 }
3131 let mut events = Vec::with_capacity(selected.len().saturating_mul(3));
3132 let mut evidence = Vec::with_capacity(selected.len().saturating_mul(3));
3133 for (position, participant_index) in selected.iter().copied().enumerate() {
3134 let participant = completion
3135 .participants()
3136 .get(participant_index)
3137 .ok_or_else(|| Self::error("completion participant index is missing"))?;
3138 let identity = participant.submission().identity();
3139 let retired = self.node_event(identity, ExecutionEventKind::NodeRetired)?;
3140 events.push(retired);
3141 evidence.push(CompletionEventEvidence::Retired(participant_index));
3142 if let Some(next_index) = selected.get(position + 1).copied() {
3143 let next_identity = submission.participants()[next_index].identity();
3144 let started = self.node_event(next_identity, ExecutionEventKind::NodeStarted)?;
3145 events.push(started);
3146 evidence.push(CompletionEventEvidence::Active);
3147 let submitted = self.operation_event(next_identity)?;
3148 events.push(submitted);
3149 evidence.push(CompletionEventEvidence::Submitted);
3150 }
3151 }
3152 let last_index = *selected
3153 .last()
3154 .ok_or_else(|| Self::error("completion participant set is empty"))?;
3155 let frame_completed = self.frame_event(
3156 submission.participants()[last_index].identity(),
3157 ExecutionEventKind::FrameCompleted,
3158 )?;
3159 events.push(frame_completed);
3160 evidence.push(CompletionEventEvidence::Active);
3161 let contexts = evidence
3162 .iter()
3163 .map(|evidence| match evidence {
3164 CompletionEventEvidence::Active => TrustedExecutionEventContext::active(
3165 self.active.run_id(),
3166 self.active.request_id(),
3167 &self.topology,
3168 &self.active,
3169 ),
3170 CompletionEventEvidence::Submitted => {
3171 TrustedExecutionEventContext::operation_submitted(
3172 self.active.run_id(),
3173 self.active.request_id(),
3174 &self.topology,
3175 &self.active,
3176 &submission,
3177 )
3178 }
3179 CompletionEventEvidence::Retired(participant_index) => {
3180 TrustedExecutionEventContext::node_retired(
3181 self.active.run_id(),
3182 self.active.request_id(),
3183 &self.topology,
3184 &self.active,
3185 &completion.participants()[*participant_index],
3186 )
3187 }
3188 })
3189 .collect::<Vec<_>>();
3190 self.emitter.emit_batch(events, &contexts)?;
3191 self.completed_frames = self.completed_frames.saturating_add(1);
3192 Ok(())
3193 }
3194
3195 fn observe_resource_failure(
3196 &mut self,
3197 snapshot: PlanRuntimeResourceSnapshot,
3198 code: &str,
3199 message: &str,
3200 ) -> std::result::Result<String, ExecutionEventSinkError> {
3201 if self.first_failure.is_some() {
3202 return Err(Self::error(
3203 "execution journal already observed its first failure",
3204 ));
3205 }
3206 let failed_operation = match self.pending_submission.as_ref() {
3207 Some(JournaledSubmission::Captured { receipt, selected }) => {
3208 let first = *selected
3209 .first()
3210 .ok_or_else(|| Self::error("captured submission has no request participant"))?;
3211 receipt
3212 .participants()
3213 .get(first)
3214 .ok_or_else(|| Self::error("captured failure participant is missing"))?
3215 .identity()
3216 .clone()
3217 }
3218 Some(JournaledSubmission::Suppressed { .. }) => {
3219 return Err(Self::error(
3220 "resource failure attribution requires a captured operation",
3221 ))
3222 }
3223 None => {
3224 return Err(Self::error(
3225 "resource failure attribution requires one submitted operation",
3226 ))
3227 }
3228 };
3229 let envelope = FailureEnvelope::new(FailureDomain::Resource, code, message, false)
3230 .and_then(|failure| failure.with_resource_snapshot(snapshot))
3231 .map_err(Self::error)?;
3232 let failure =
3233 IdentifiedFailure::new(failed_operation.clone(), envelope).map_err(Self::error)?;
3234 let failure_fingerprint = failure.fingerprint();
3235 let sequence = self.emitter.cursor().last_sequence().saturating_add(1);
3236 let mut parts = failed_operation.parts().clone();
3237 parts.sequence = sequence;
3238 parts.parent_span_id = Some(failed_operation.parts().span_id.clone());
3239 parts.span_id =
3240 SpanId::new(format!("{}/failure/{sequence}", self.root_span)).map_err(Self::error)?;
3241 let event = self.event(
3242 ExecutionPhase::Execution,
3243 ExecutionEventKind::FailureObserved,
3244 parts,
3245 ExecutionEventDetail::Failure(failure.clone()),
3246 )?;
3247 self.emitter.emit(
3248 event,
3249 &TrustedExecutionEventContext::failure(
3250 self.active.run_id(),
3251 self.active.request_id(),
3252 Some(&self.topology),
3253 Some(&self.active),
3254 &failure,
3255 ),
3256 )?;
3257 self.first_failure = Some(failure);
3258 Ok(failure_fingerprint)
3259 }
3260
3261 fn settle_failed_submission(
3262 &mut self,
3263 completion: &OperationCompletionReceipt,
3264 ) -> std::result::Result<(), ExecutionEventSinkError> {
3265 if self.first_failure.is_none() {
3266 return Err(Self::error(
3267 "cannot settle a failed submission before observing its failure",
3268 ));
3269 }
3270 let pending = self
3271 .pending_submission
3272 .take()
3273 .ok_or_else(|| Self::error("failed submission is no longer journaled"))?;
3274 let JournaledSubmission::Captured { receipt, .. } = pending else {
3275 return Err(Self::error(
3276 "failed submission was suppressed from the execution journal",
3277 ));
3278 };
3279 if completion.submission().fingerprint() != receipt.fingerprint() {
3280 return Err(Self::error(
3281 "settled failure differs from the journaled physical submission",
3282 ));
3283 }
3284 Ok(())
3285 }
3286
3287 fn fail_sequence(
3288 &mut self,
3289 receipt: &SequenceSessionTerminalReceipt,
3290 ) -> std::result::Result<(), ExecutionEventSinkError> {
3291 if self.pending_submission.is_some() {
3292 return Err(Self::error(
3293 "sequence failure still has an in-flight journal submission",
3294 ));
3295 }
3296 let failure = self
3297 .first_failure
3298 .clone()
3299 .ok_or_else(|| Self::error("sequence failure lacks its first observed failure"))?;
3300 let aborted = TrustedAbortedSequenceBinding::from_session_receipt(receipt, &self.active)
3301 .map_err(Self::error)?;
3302 let sequence_number = self.emitter.cursor().last_sequence().saturating_add(1);
3303 let sequence_span =
3304 SpanId::new(format!("{}/sequence-aborted", self.root_span)).map_err(Self::error)?;
3305 let mut parts = self.bind_active(self.bind_plan(self.base_parts(
3306 sequence_number,
3307 sequence_span,
3308 Some(self.root_span.clone()),
3309 )));
3310 parts.aborted_sequence_fingerprint = Some(aborted.fingerprint().to_owned());
3311 let sequence_aborted = self.event(
3312 ExecutionPhase::Completion,
3313 ExecutionEventKind::SequenceAborted,
3314 parts,
3315 ExecutionEventDetail::None,
3316 )?;
3317 self.emitter.emit(
3318 sequence_aborted,
3319 &TrustedExecutionEventContext::aborted(
3320 self.active.run_id(),
3321 self.active.request_id(),
3322 &self.topology,
3323 &self.active,
3324 &aborted,
3325 ),
3326 )?;
3327
3328 let request_sequence = self.emitter.cursor().last_sequence().saturating_add(1);
3329 let mut parts = self.bind_active(self.bind_plan(self.base_parts(
3330 request_sequence,
3331 self.root_span.clone(),
3332 None,
3333 )));
3334 parts.aborted_sequence_fingerprint = Some(aborted.fingerprint().to_owned());
3335 let request_failed = self.event(
3336 ExecutionPhase::Completion,
3337 ExecutionEventKind::RequestFailed,
3338 parts,
3339 ExecutionEventDetail::FailureTerminal {
3340 first_failure_fingerprint: failure.fingerprint(),
3341 },
3342 )?;
3343 self.emitter.emit(
3344 request_failed,
3345 &TrustedExecutionEventContext::failure_with_disposition(
3346 self.active.run_id(),
3347 self.active.request_id(),
3348 &self.topology,
3349 &self.active,
3350 None,
3351 Some(&aborted),
3352 &failure,
3353 ),
3354 )
3355 }
3356
3357 fn complete_sequence(
3358 &mut self,
3359 receipt: &SequenceSessionTerminalReceipt,
3360 input_tokens: u64,
3361 output_tokens: u64,
3362 ) -> std::result::Result<(), ExecutionEventSinkError> {
3363 if self.pending_submission.is_some() {
3364 return Err(Self::error(
3365 "sequence completed with an in-flight journal submission",
3366 ));
3367 }
3368 let completed =
3369 TrustedCompletedSequenceBinding::from_session_receipt(receipt, &self.active)
3370 .map_err(Self::error)?;
3371 let sequence_number = self.emitter.cursor().last_sequence().saturating_add(1);
3372 let sequence_span =
3373 SpanId::new(format!("{}/sequence-completed", self.root_span)).map_err(Self::error)?;
3374 let mut parts = self.bind_active(self.bind_plan(self.base_parts(
3375 sequence_number,
3376 sequence_span,
3377 Some(self.root_span.clone()),
3378 )));
3379 parts.completed_sequence_fingerprint = Some(completed.fingerprint().to_owned());
3380 let sequence_completed = self.event(
3381 ExecutionPhase::Completion,
3382 ExecutionEventKind::SequenceCompleted,
3383 parts,
3384 ExecutionEventDetail::None,
3385 )?;
3386 self.emitter.emit(
3387 sequence_completed,
3388 &TrustedExecutionEventContext::completed(
3389 self.active.run_id(),
3390 self.active.request_id(),
3391 &self.topology,
3392 &self.active,
3393 &completed,
3394 ),
3395 )?;
3396 let request_sequence = self.emitter.cursor().last_sequence().saturating_add(1);
3397 let mut parts = self.bind_active(self.bind_plan(self.base_parts(
3398 request_sequence,
3399 self.root_span.clone(),
3400 None,
3401 )));
3402 parts.completed_sequence_fingerprint = Some(completed.fingerprint().to_owned());
3403 let request_completed = self.event(
3404 ExecutionPhase::Completion,
3405 ExecutionEventKind::RequestCompleted,
3406 parts,
3407 ExecutionEventDetail::Counters {
3408 input: input_tokens,
3409 output: output_tokens,
3410 },
3411 )?;
3412 self.emitter.emit(
3413 request_completed,
3414 &TrustedExecutionEventContext::completed(
3415 self.active.run_id(),
3416 self.active.request_id(),
3417 &self.topology,
3418 &self.active,
3419 &completed,
3420 ),
3421 )
3422 }
3423}
3424
3425struct VNextSequence<R: DeviceRuntime> {
3426 prefix_capture_interests:
3427 Mutex<Vec<std::sync::Weak<prefix_cache::rendezvous::NativePrefixCapture<R>>>>,
3428 cache_id: String,
3429 request: Arc<VNextRequestRoot<R>>,
3430 session: Arc<SequenceSession<R>>,
3431 active_binding: Arc<TrustedActiveSequenceBinding>,
3432 request_origin: ExecutorRequestOrigin,
3433 tokens: Mutex<Vec<u32>>,
3434 maximum_tokens: usize,
3435 active: AtomicBool,
3436 operation: AsyncMutex<()>,
3437 events: Option<Mutex<VNextExecutionJournal>>,
3438 product_prompt_tokens: u64,
3439 replayed_output_tokens: u64,
3440 prefill_tokens_processed: AtomicUsize,
3441}
3442
3443struct PreparedVNextPrefill<R: DeviceRuntime> {
3444 step: Arc<StepResourceLease<R>>,
3445 wave: PreparedStepSubmissionWave<R>,
3446}
3447
3448struct VNextExecutionParticipant<'a, R: DeviceRuntime> {
3449 sequence: &'a Arc<VNextSequence<R>>,
3450 tokens: &'a [u32],
3451 span: &'a TokenSpanWork,
3452 logits_policy: Option<&'a LogitsReturnPolicy>,
3453}
3454
3455struct VNextDecodeCandidate<R: DeviceRuntime> {
3456 original_index: usize,
3457 sequence: Arc<VNextSequence<R>>,
3458 cache_id: String,
3459 next_token: u32,
3460 logits_policy: LogitsReturnPolicy,
3461}
3462
3463struct VNextPrefillCandidate<R: DeviceRuntime> {
3464 original_index: usize,
3465 slot: Arc<VNextPrefillSlot<R>>,
3466 sequence: Arc<VNextSequence<R>>,
3467 tokens: Vec<u32>,
3468 maximum_tokens: usize,
3469 planned_chunk: PrefillChunk,
3470}
3471
3472fn validate_sequence_completion_accounting(
3473 request_id: &RequestId,
3474 product_prompt_tokens: u64,
3475 replayed_output_tokens: u64,
3476 completion: &ExecutorSequenceCompletion,
3477) -> Result<()> {
3478 if completion.request_id() != request_id {
3479 return Err(FerrumError::request_validation(format!(
3480 "vNext completion request `{}` differs from cache owner `{request_id}`",
3481 completion.request_id()
3482 )));
3483 }
3484 if completion.input_tokens() != product_prompt_tokens {
3485 return Err(FerrumError::request_validation(format!(
3486 "vNext completion input count {} differs from admitted product prompt count {product_prompt_tokens}",
3487 completion.input_tokens()
3488 )));
3489 }
3490 if completion.output_tokens() < replayed_output_tokens {
3491 return Err(FerrumError::request_validation(format!(
3492 "vNext completion output count {} precedes recompute baseline {replayed_output_tokens}",
3493 completion.output_tokens()
3494 )));
3495 }
3496 Ok(())
3497}
3498
3499struct PendingSequenceCompletion<'a, R: DeviceRuntime> {
3503 sequence: &'a VNextSequence<R>,
3504 operation: Option<tokio::sync::MutexGuard<'a, ()>>,
3505 completed: bool,
3506}
3507
3508impl<R: DeviceRuntime> Drop for PendingSequenceCompletion<'_, R> {
3509 fn drop(&mut self) {
3510 if !self.completed {
3511 self.sequence.abort();
3514 }
3515 }
3516}
3517
3518impl<R: DeviceRuntime> VNextSequence<R> {
3519 fn request_id(&self) -> &RequestId {
3520 self.request.product_request_id()
3521 }
3522
3523 fn preempt_for_recompute(&self) -> Result<()> {
3524 if !self.active.load(Ordering::Acquire) {
3525 return Err(FerrumError::already_exists(format!(
3526 "vNext request `{}` is already terminal",
3527 self.request_id()
3528 )));
3529 }
3530 self.session
3531 .try_abort_if_quiescent()
3532 .map_err(|error| FerrumError::backend(error.to_string()))?;
3533 self.active.store(false, Ordering::Release);
3534 Ok(())
3535 }
3536
3537 fn complete(&self, completion: &ExecutorSequenceCompletion) -> Result<()> {
3538 if let Err(error) = validate_sequence_completion_accounting(
3539 self.request_id(),
3540 self.product_prompt_tokens,
3541 self.replayed_output_tokens,
3542 completion,
3543 ) {
3544 self.abort();
3545 return Err(error);
3546 }
3547 self.complete_with_counts(completion.input_tokens(), completion.output_tokens())
3548 }
3549
3550 fn complete_startup(&self) -> Result<()> {
3551 self.complete_with_counts(self.product_prompt_tokens, self.replayed_output_tokens)
3552 }
3553
3554 fn complete_with_counts(&self, input_tokens: u64, output_tokens: u64) -> Result<()> {
3555 if !self.active.swap(false, Ordering::AcqRel) {
3556 return Err(FerrumError::already_exists(format!(
3557 "vNext request `{}` is already terminal",
3558 self.request_id()
3559 )));
3560 }
3561 let receipt = self.session.try_complete().map_err(|error| {
3562 let _ = self.session.request_cancel();
3563 let _ = self.session.try_abort();
3564 FerrumError::backend(format!("vNext sequence completion: {error}"))
3565 })?;
3566 if let Some(events) = &self.events {
3567 events
3568 .lock()
3569 .complete_sequence(&receipt, input_tokens, output_tokens)
3570 .map_err(|error| {
3571 FerrumError::backend(format!("vNext execution journal completion: {error}"))
3572 })?;
3573 }
3574 Ok(())
3575 }
3576
3577 fn abort(&self) {
3578 self.active.store(false, Ordering::Release);
3579 let _ = self.session.request_cancel();
3580 let _ = self.session.try_abort();
3581 }
3582
3583 fn abort_after_observed_failure(&self) -> Result<()> {
3584 if !self.active.swap(false, Ordering::AcqRel) {
3585 return Err(FerrumError::already_exists(format!(
3586 "vNext request `{}` is already terminal",
3587 self.request_id()
3588 )));
3589 }
3590 self.session.request_cancel().map_err(|error| {
3591 FerrumError::backend(format!("vNext failure cancellation: {error}"))
3592 })?;
3593 let receipt = self
3594 .session
3595 .try_abort()
3596 .map_err(|error| FerrumError::backend(format!("vNext failure abort: {error}")))?;
3597 self.events
3598 .as_ref()
3599 .ok_or_else(|| {
3600 FerrumError::internal(
3601 "vNext diagnostic failure requires an execution event journal",
3602 )
3603 })?
3604 .lock()
3605 .fail_sequence(&receipt)
3606 .map_err(|error| {
3607 FerrumError::backend(format!("vNext execution journal failure: {error}"))
3608 })
3609 }
3610}
3611
3612impl<R: DeviceRuntime> Drop for VNextSequence<R> {
3613 fn drop(&mut self) {
3614 self.active.store(false, Ordering::Release);
3615 let _ = self.session.request_cancel();
3616 let _ = self.session.try_abort();
3617 }
3618}
3619
3620struct VNextKvCacheHandle<R: DeviceRuntime> {
3621 block_table: BlockTable,
3622 cache_id: String,
3623 sequence: Weak<VNextSequence<R>>,
3624 device: Device,
3625 num_layers: usize,
3626 num_heads: usize,
3627 head_dim: usize,
3628 maximum_tokens: usize,
3629}
3630
3631impl<R: DeviceRuntime> VNextKvCacheHandle<R> {
3632 fn new(
3633 sequence: &Arc<VNextSequence<R>>,
3634 info: &ModelInfo,
3635 attention_head_dimension: usize,
3636 tokens: usize,
3637 ) -> Self {
3638 let mut block_table = BlockTable::new(16);
3639 block_table.sequence_length = tokens;
3640 Self {
3641 block_table,
3642 cache_id: sequence.cache_id.clone(),
3643 sequence: Arc::downgrade(sequence),
3644 device: info.device.clone(),
3645 num_layers: info.num_layers,
3646 num_heads: info.num_kv_heads,
3647 head_dim: attention_head_dimension,
3648 maximum_tokens: sequence.maximum_tokens,
3649 }
3650 }
3651}
3652
3653impl<R: DeviceRuntime> fmt::Debug for VNextKvCacheHandle<R> {
3654 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
3655 formatter
3656 .debug_struct("VNextKvCacheHandle")
3657 .field("cache_id", &self.cache_id)
3658 .field("tokens", &self.block_table.sequence_length)
3659 .field("maximum_tokens", &self.maximum_tokens)
3660 .field("device", &self.device)
3661 .finish_non_exhaustive()
3662 }
3663}
3664
3665impl<R: DeviceRuntime> KvCacheHandle for VNextKvCacheHandle<R> {
3666 fn block_table(&self) -> &BlockTable {
3667 &self.block_table
3668 }
3669
3670 fn block_table_mut(&mut self) -> &mut BlockTable {
3671 &mut self.block_table
3672 }
3673
3674 fn as_any(&self) -> &dyn std::any::Any {
3675 self
3676 }
3677
3678 fn device(&self) -> Device {
3679 self.device.clone()
3680 }
3681
3682 fn num_layers(&self) -> usize {
3683 self.num_layers
3684 }
3685
3686 fn num_heads(&self) -> usize {
3687 self.num_heads
3688 }
3689
3690 fn head_dim(&self) -> usize {
3691 self.head_dim
3692 }
3693
3694 fn key_cache(&self, _layer: usize) -> Result<Option<TensorRef>> {
3695 Ok(None)
3696 }
3697
3698 fn value_cache(&self, _layer: usize) -> Result<Option<TensorRef>> {
3699 Ok(None)
3700 }
3701
3702 fn clone_handle(&self) -> Result<Arc<dyn KvCacheHandle>> {
3703 Err(FerrumError::unsupported(
3704 "vNext cache cloning requires an explicit typed copy-on-write contract",
3705 ))
3706 }
3707
3708 fn stats(&self) -> CacheHandleStats {
3709 let tokens = self.block_table.sequence_length;
3710 CacheHandleStats {
3711 memory_bytes: 0,
3712 blocks_allocated: tokens.div_ceil(self.block_table.block_size),
3713 tokens_stored: tokens,
3714 utilization: tokens as f32 / self.maximum_tokens.max(1) as f32,
3715 last_access: Instant::now(),
3716 }
3717 }
3718
3719 fn is_valid(&self) -> bool {
3720 self.sequence
3721 .upgrade()
3722 .is_some_and(|sequence| sequence.active.load(Ordering::Acquire))
3723 }
3724
3725 fn cache_id(&self) -> String {
3726 self.cache_id.clone()
3727 }
3728}
3729
3730enum DispatchOutcome<R: DeviceRuntime> {
3731 Submitted {
3732 completion: CompletionHandle<R>,
3733 attribution: Option<BoundDeviceSubmissionAttribution>,
3734 },
3735 QuiescentFailure(String),
3736 SubmissionIndeterminate {
3737 message: String,
3738 recovery: IndeterminateSubmissionHandle<R>,
3739 },
3740 PostSubmitContract {
3741 message: String,
3742 completion: CompletionHandle<R>,
3743 },
3744}
3745
3746enum VNextExecutionCapacityDecision<T> {
3747 Ready(T),
3748 Deferred(ExecutorExecutionCapacityDeferral),
3749 RequestStateDeferred(ExecutorRequestStateDeferral),
3750}
3751
3752enum VNextExecutionMaintenanceSource<'a> {
3753 Logical(&'a AdmissionDeferred),
3754 Backing(&'a DynamicBackingDeferred),
3755}
3756
3757enum VNextSequenceAdmissionDecision<R: DeviceRuntime> {
3758 Admitted(Arc<SequenceSession<R>>),
3759 Deferred(AdmissionDeferred),
3760 BackingDeferred(VNextPrefillBackingDeferral<R>),
3761 PermanentRejected(AdmissionRejected),
3762}
3763
3764enum VNextPrefillBackingDeferral<R: DeviceRuntime> {
3765 InitialSequence(InitialSequenceBackingDeferral<R>),
3766 Step(StepAdmissionBackingDeferral<R>),
3767 SubmissionWave(StepSubmissionWaveBackingDeferral<R>),
3768}
3769
3770impl<R: DeviceRuntime> VNextPrefillBackingDeferral<R> {
3771 fn evidence(&self) -> &DynamicBackingDeferred {
3772 match self {
3773 Self::InitialSequence(deferred) => deferred.evidence(),
3774 Self::Step(deferred) => deferred.evidence(),
3775 Self::SubmissionWave(deferred) => deferred.evidence(),
3776 }
3777 }
3778
3779 fn maintain(&self) -> std::result::Result<DynamicDeferredMaintenanceOutcome, VNextError> {
3780 match self {
3781 Self::InitialSequence(deferred) => deferred.maintain(),
3782 Self::Step(deferred) => deferred.maintain(),
3783 Self::SubmissionWave(deferred) => deferred.maintain(),
3784 }
3785 }
3786}
3787
3788enum PendingPrefillMaintenance<R: DeviceRuntime> {
3789 Logical(AdmissionDeferred),
3790 Backing(VNextPrefillBackingDeferral<R>),
3791}
3792
3793impl<R: DeviceRuntime> PendingPrefillMaintenance<R> {
3794 fn projection(&self, request_id: &RequestId) -> Result<ExecutorPrefillMaintenanceDeferral> {
3795 match self {
3796 Self::Logical(deferred) => {
3797 ExecutorPrefillMaintenanceDeferral::from_admission(request_id, deferred)
3798 }
3799 Self::Backing(deferred) => {
3800 ExecutorPrefillMaintenanceDeferral::from_backing(request_id, deferred.evidence())
3801 }
3802 }
3803 }
3804}
3805
3806enum VNextPrefillSlotState<R: DeviceRuntime> {
3807 Probing,
3808 Deferred {
3809 maintenance: Option<PendingPrefillMaintenance<R>>,
3810 maintaining: bool,
3811 },
3812 Ready(Arc<VNextSequence<R>>),
3813 Executing(Arc<VNextSequence<R>>),
3814 Terminal,
3815}
3816
3817enum VNextPrefillProbeResolution<R: DeviceRuntime> {
3818 Deferred(AdmissionDeferred),
3819 MaintenanceDeferred {
3820 pending: PendingPrefillMaintenance<R>,
3821 },
3822 Ready(Arc<VNextSequence<R>>),
3823 PermanentRejected(AdmissionRejected),
3824}
3825
3826impl<R: DeviceRuntime> VNextPrefillProbeResolution<R> {
3827 fn abort(self) {
3828 match self {
3829 Self::Deferred(_) => {}
3830 Self::MaintenanceDeferred { pending } => drop(pending),
3831 Self::Ready(sequence) => sequence.abort(),
3832 Self::PermanentRejected(_) => {}
3833 }
3834 }
3835}
3836
3837impl<R: DeviceRuntime> VNextPrefillSlotState<R> {
3838 fn abort(self) {
3839 match self {
3840 Self::Deferred { maintenance, .. } => drop(maintenance),
3841 Self::Ready(sequence) | Self::Executing(sequence) => sequence.abort(),
3842 Self::Probing | Self::Terminal => {}
3843 }
3844 }
3845}
3846
3847struct VNextPrefillSlot<R: DeviceRuntime> {
3848 request_id: RequestId,
3849 work_shape: ResourceWorkShape,
3850 cancelled: AtomicBool,
3851 state: Mutex<VNextPrefillSlotState<R>>,
3852}
3853
3854impl<R: DeviceRuntime> VNextPrefillSlot<R> {
3855 fn new(request_id: RequestId, work_shape: ResourceWorkShape) -> Arc<Self> {
3856 Arc::new(Self {
3857 request_id,
3858 work_shape,
3859 cancelled: AtomicBool::new(false),
3860 state: Mutex::new(VNextPrefillSlotState::Probing),
3861 })
3862 }
3863}
3864
3865impl<R: DeviceRuntime> Drop for VNextPrefillSlot<R> {
3866 fn drop(&mut self) {
3867 let state = std::mem::replace(self.state.get_mut(), VNextPrefillSlotState::Terminal);
3868 state.abort();
3869 }
3870}
3871
3872struct VNextSequenceRegistry<R: DeviceRuntime> {
3873 prefills: HashMap<RequestId, Arc<VNextPrefillSlot<R>>>,
3874 active: HashMap<String, Arc<VNextSequence<R>>>,
3875}
3876
3877impl<R: DeviceRuntime> Default for VNextSequenceRegistry<R> {
3878 fn default() -> Self {
3879 Self {
3880 prefills: HashMap::new(),
3881 active: HashMap::new(),
3882 }
3883 }
3884}
3885
3886impl<R: DeviceRuntime> VNextSequenceRegistry<R> {
3887 fn total_len(&self) -> usize {
3888 self.prefills.len() + self.active.len()
3889 }
3890
3891 fn begin_prefill_probe(
3892 &mut self,
3893 request_id: &RequestId,
3894 work_shape: &ResourceWorkShape,
3895 ) -> Result<Arc<VNextPrefillSlot<R>>> {
3896 if self
3897 .active
3898 .values()
3899 .any(|sequence| sequence.request_id() == request_id)
3900 {
3901 return Err(FerrumError::already_exists(format!(
3902 "vNext request `{request_id}` is already active"
3903 )));
3904 }
3905 if let Some(slot) = self.prefills.get(request_id).cloned() {
3906 if slot.work_shape != *work_shape {
3907 return Err(FerrumError::request_validation(format!(
3908 "vNext prefill retry for `{request_id}` differs from its deferred work shape"
3909 )));
3910 }
3911 if slot.cancelled.load(Ordering::Acquire) {
3912 return Err(FerrumError::cancelled(format!(
3913 "vNext prefill probe for `{request_id}` was cancelled"
3914 )));
3915 }
3916 let mut state = slot.state.lock();
3917 let prior = std::mem::replace(&mut *state, VNextPrefillSlotState::Probing);
3918 match prior {
3919 VNextPrefillSlotState::Deferred {
3920 maintenance: None,
3921 maintaining: false,
3922 } => {
3923 drop(state);
3924 Ok(slot)
3925 }
3926 other => {
3927 *state = other;
3928 Err(FerrumError::already_exists(format!(
3929 "vNext request `{request_id}` already retained prefill state"
3930 )))
3931 }
3932 }
3933 } else {
3934 let slot = VNextPrefillSlot::new(request_id.clone(), work_shape.clone());
3935 self.prefills.insert(request_id.clone(), Arc::clone(&slot));
3936 Ok(slot)
3937 }
3938 }
3939
3940 fn begin_prefill_execution(
3941 &mut self,
3942 request_id: &RequestId,
3943 ) -> Result<(Arc<VNextPrefillSlot<R>>, Arc<VNextSequence<R>>)> {
3944 let slot = self.prefills.get(request_id).cloned().ok_or_else(|| {
3945 FerrumError::request_validation(format!(
3946 "vNext prefill for `{request_id}` has no retained admission authority"
3947 ))
3948 })?;
3949 if slot.cancelled.load(Ordering::Acquire) {
3950 return Err(FerrumError::cancelled(format!(
3951 "vNext prefill admission for `{request_id}` is no longer active"
3952 )));
3953 }
3954 let mut state = slot.state.lock();
3955 let prior = std::mem::replace(&mut *state, VNextPrefillSlotState::Terminal);
3956 let VNextPrefillSlotState::Ready(sequence) = prior else {
3957 *state = prior;
3958 return Err(FerrumError::request_validation(format!(
3959 "vNext prefill for `{request_id}` is not ready for execution"
3960 )));
3961 };
3962 *state = VNextPrefillSlotState::Executing(Arc::clone(&sequence));
3963 drop(state);
3964 Ok((slot, sequence))
3965 }
3966
3967 fn begin_prefill_batch_execution(
3968 &mut self,
3969 request_ids: &[RequestId],
3970 ) -> Result<Vec<(Arc<VNextPrefillSlot<R>>, Arc<VNextSequence<R>>)>> {
3971 let mut prepared = Vec::with_capacity(request_ids.len());
3972 for request_id in request_ids {
3973 if prepared.iter().any(
3974 |(slot, _): &(Arc<VNextPrefillSlot<R>>, Arc<VNextSequence<R>>)| {
3975 slot.request_id == *request_id
3976 },
3977 ) {
3978 return Err(FerrumError::request_validation(
3979 "vNext batch prefill inputs contain a duplicate request",
3980 ));
3981 }
3982 let slot = self.prefills.get(request_id).cloned().ok_or_else(|| {
3983 FerrumError::request_validation(format!(
3984 "vNext prefill for `{request_id}` has no retained admission authority"
3985 ))
3986 })?;
3987 if slot.cancelled.load(Ordering::Acquire) {
3988 return Err(FerrumError::cancelled(format!(
3989 "vNext prefill admission for `{request_id}` is no longer active"
3990 )));
3991 }
3992 let sequence = match &*slot.state.lock() {
3993 VNextPrefillSlotState::Ready(sequence) => Arc::clone(sequence),
3994 _ => {
3995 return Err(FerrumError::request_validation(format!(
3996 "vNext prefill for `{request_id}` is not ready for batch execution"
3997 )))
3998 }
3999 };
4000 prepared.push((slot, sequence));
4001 }
4002
4003 let mut states = prepared
4004 .iter()
4005 .map(|(slot, _)| slot.state.lock())
4006 .collect::<Vec<_>>();
4007 if states.iter().zip(&prepared).any(|(state, (_, sequence))| {
4008 !matches!(
4009 &**state,
4010 VNextPrefillSlotState::Ready(current) if Arc::ptr_eq(current, sequence)
4011 )
4012 }) {
4013 return Err(FerrumError::internal(
4014 "vNext prefill authorities changed during atomic batch acquisition",
4015 ));
4016 }
4017 for (state, (_, sequence)) in states.iter_mut().zip(&prepared) {
4018 **state = VNextPrefillSlotState::Executing(Arc::clone(sequence));
4019 }
4020 drop(states);
4021 Ok(prepared)
4022 }
4023
4024 fn activate(
4025 &mut self,
4026 slot: &Arc<VNextPrefillSlot<R>>,
4027 sequence: &Arc<VNextSequence<R>>,
4028 ) -> Result<()> {
4029 let request_id = &slot.request_id;
4030 if slot.cancelled.load(Ordering::Acquire)
4031 || !self
4032 .prefills
4033 .get(request_id)
4034 .is_some_and(|current| Arc::ptr_eq(current, slot))
4035 {
4036 return Err(FerrumError::cancelled(format!(
4037 "vNext prefill admission for `{request_id}` is no longer active"
4038 )));
4039 }
4040 if self.active.contains_key(&sequence.cache_id) {
4041 return Err(FerrumError::already_exists(format!(
4042 "vNext cache `{}` raced with another prefill",
4043 sequence.cache_id
4044 )));
4045 }
4046 let mut state = slot.state.lock();
4047 let executing = matches!(
4048 &*state,
4049 VNextPrefillSlotState::Executing(current) if Arc::ptr_eq(current, sequence)
4050 );
4051 if !executing {
4052 return Err(FerrumError::cancelled(format!(
4053 "vNext prefill execution for `{request_id}` lost its slot authority"
4054 )));
4055 }
4056 *state = VNextPrefillSlotState::Terminal;
4057 drop(state);
4058 self.prefills.remove(request_id);
4059 self.active
4060 .insert(sequence.cache_id.clone(), Arc::clone(sequence));
4061 Ok(())
4062 }
4063
4064 fn restore_prefill_ready(
4065 &mut self,
4066 slot: &Arc<VNextPrefillSlot<R>>,
4067 sequence: &Arc<VNextSequence<R>>,
4068 ) -> Result<()> {
4069 let request_id = &slot.request_id;
4070 if slot.cancelled.load(Ordering::Acquire)
4071 || !self
4072 .prefills
4073 .get(request_id)
4074 .is_some_and(|current| Arc::ptr_eq(current, slot))
4075 {
4076 return Err(FerrumError::cancelled(format!(
4077 "vNext prefill admission for `{request_id}` is no longer active"
4078 )));
4079 }
4080 let mut state = slot.state.lock();
4081 if !matches!(
4082 &*state,
4083 VNextPrefillSlotState::Executing(current) if Arc::ptr_eq(current, sequence)
4084 ) {
4085 return Err(FerrumError::internal(format!(
4086 "vNext prefill execution for `{request_id}` lost its slot authority"
4087 )));
4088 }
4089 *state = VNextPrefillSlotState::Ready(Arc::clone(sequence));
4090 Ok(())
4091 }
4092
4093 fn restore_prefill_batch_ready(
4094 &mut self,
4095 executions: &[(&Arc<VNextPrefillSlot<R>>, &Arc<VNextSequence<R>>)],
4096 ) -> Result<()> {
4097 for (slot, sequence) in executions {
4098 if slot.cancelled.load(Ordering::Acquire)
4099 || !self
4100 .prefills
4101 .get(&slot.request_id)
4102 .is_some_and(|current| Arc::ptr_eq(current, slot))
4103 || !matches!(
4104 &*slot.state.lock(),
4105 VNextPrefillSlotState::Executing(current) if Arc::ptr_eq(current, sequence)
4106 )
4107 {
4108 return Err(FerrumError::cancelled(format!(
4109 "vNext batch prefill for `{}` lost its retained authority",
4110 slot.request_id
4111 )));
4112 }
4113 }
4114 for (slot, sequence) in executions {
4115 *slot.state.lock() = VNextPrefillSlotState::Ready(Arc::clone(sequence));
4116 }
4117 Ok(())
4118 }
4119
4120 fn commit_prefill_batch_execution(
4121 &mut self,
4122 executions: &[(&Arc<VNextPrefillSlot<R>>, &Arc<VNextSequence<R>>, bool)],
4123 ) -> Result<()> {
4124 for (index, (slot, sequence, final_chunk)) in executions.iter().enumerate() {
4125 if slot.cancelled.load(Ordering::Acquire)
4126 || !self
4127 .prefills
4128 .get(&slot.request_id)
4129 .is_some_and(|current| Arc::ptr_eq(current, slot))
4130 || !matches!(
4131 &*slot.state.lock(),
4132 VNextPrefillSlotState::Executing(current) if Arc::ptr_eq(current, sequence)
4133 )
4134 {
4135 return Err(FerrumError::cancelled(format!(
4136 "vNext batch prefill for `{}` lost its retained authority",
4137 slot.request_id
4138 )));
4139 }
4140 if *final_chunk
4141 && (self.active.contains_key(&sequence.cache_id)
4142 || executions[..index].iter().any(|(_, prior, prior_final)| {
4143 *prior_final && prior.cache_id == sequence.cache_id
4144 }))
4145 {
4146 return Err(FerrumError::already_exists(format!(
4147 "vNext cache `{}` raced with another batch prefill",
4148 sequence.cache_id
4149 )));
4150 }
4151 }
4152
4153 for (slot, sequence, final_chunk) in executions {
4154 if *final_chunk {
4155 *slot.state.lock() = VNextPrefillSlotState::Terminal;
4156 self.prefills.remove(&slot.request_id);
4157 self.active
4158 .insert(sequence.cache_id.clone(), Arc::clone(sequence));
4159 } else {
4160 *slot.state.lock() = VNextPrefillSlotState::Ready(Arc::clone(sequence));
4161 }
4162 }
4163 Ok(())
4164 }
4165
4166 fn cancel_prefill(&mut self, request_id: &RequestId) -> bool {
4167 let Some(slot) = self.prefills.get(request_id).cloned() else {
4168 return false;
4169 };
4170 slot.cancelled.store(true, Ordering::Release);
4171 let mut state = slot.state.lock();
4172 if let VNextPrefillSlotState::Executing(sequence) = &*state {
4173 let sequence = Arc::clone(sequence);
4174 drop(state);
4175 sequence.abort();
4176 return true;
4177 }
4178 let defer_cleanup = matches!(&*state, VNextPrefillSlotState::Probing)
4179 || matches!(
4180 &*state,
4181 VNextPrefillSlotState::Deferred {
4182 maintaining: true,
4183 ..
4184 }
4185 );
4186 if defer_cleanup {
4187 return true;
4188 }
4189 let prior = std::mem::replace(&mut *state, VNextPrefillSlotState::Terminal);
4190 drop(state);
4191 self.prefills.remove(request_id);
4192 prior.abort();
4193 true
4194 }
4195
4196 fn discard_exact_sequence(&mut self, sequence: &Arc<VNextSequence<R>>) -> bool {
4197 if self
4198 .active
4199 .get(&sequence.cache_id)
4200 .is_some_and(|current| Arc::ptr_eq(current, sequence))
4201 {
4202 self.active.remove(&sequence.cache_id);
4203 sequence.abort();
4204 return true;
4205 }
4206
4207 let Some(slot) = self.prefills.get(sequence.request_id()).cloned() else {
4208 return false;
4209 };
4210 if !self
4211 .prefills
4212 .get(sequence.request_id())
4213 .is_some_and(|current| Arc::ptr_eq(current, &slot))
4214 {
4215 return false;
4216 }
4217 let mut state = slot.state.lock();
4218 let owns_sequence = matches!(
4219 &*state,
4220 VNextPrefillSlotState::Ready(current)
4221 | VNextPrefillSlotState::Executing(current)
4222 if Arc::ptr_eq(current, sequence)
4223 );
4224 if !owns_sequence {
4225 return false;
4226 }
4227 *state = VNextPrefillSlotState::Terminal;
4228 slot.cancelled.store(true, Ordering::Release);
4229 drop(state);
4230 self.prefills.remove(sequence.request_id());
4231 sequence.abort();
4232 true
4233 }
4234
4235 fn write_execution_capacity_release_sources(
4236 &self,
4237 preemption: &ExecutorExecutionCapacityPreemption,
4238 sources: &mut Vec<CapacityAvailabilitySource>,
4239 ) -> Result<bool> {
4240 sources.clear();
4241 let retained = self.prefills.get(preemption.request_id()).and_then(|slot| {
4242 let state = slot.state.lock();
4243 match &*state {
4244 VNextPrefillSlotState::Ready(sequence)
4245 if sequence.cache_id == preemption.cache_id() =>
4246 {
4247 Some(Arc::clone(sequence))
4248 }
4249 _ => None,
4250 }
4251 });
4252 let sequence = retained.or_else(|| {
4253 self.active
4254 .get(preemption.cache_id())
4255 .filter(|sequence| sequence.request_id() == preemption.request_id())
4256 .cloned()
4257 });
4258 let Some(sequence) = sequence else {
4259 return Ok(false);
4260 };
4261 if !sequence.active.load(Ordering::Acquire) {
4262 return Ok(false);
4263 }
4264 sequence
4265 .session
4266 .write_release_capacity_sources(sources)
4267 .map_err(|error| FerrumError::backend(error.to_string()))?;
4268 Ok(true)
4269 }
4270
4271 fn preempt_execution_capacity(
4272 &mut self,
4273 preemption: &ExecutorExecutionCapacityPreemption,
4274 ) -> Result<ExecutorExecutionCapacityPreemptionAuthority> {
4275 let request_id = preemption.request_id();
4276 if let Some(slot) = self.prefills.get(request_id).cloned() {
4277 let mut state = slot.state.lock();
4278 let sequence = match &*state {
4279 VNextPrefillSlotState::Ready(sequence) => Arc::clone(sequence),
4280 VNextPrefillSlotState::Executing(_) => {
4281 return Err(FerrumError::internal(format!(
4282 "vNext request `{request_id}` cannot preempt an executing prefill"
4283 )));
4284 }
4285 _ => {
4286 return Err(FerrumError::request_validation(format!(
4287 "vNext request `{request_id}` has no releasable retained prefill authority"
4288 )));
4289 }
4290 };
4291 if sequence.cache_id != preemption.cache_id() {
4292 return Err(FerrumError::request_validation(format!(
4293 "vNext request `{request_id}` preemption cache mismatch: expected {}, found {}",
4294 preemption.cache_id(),
4295 sequence.cache_id
4296 )));
4297 }
4298 sequence.preempt_for_recompute()?;
4299 *state = VNextPrefillSlotState::Terminal;
4300 slot.cancelled.store(true, Ordering::Release);
4301 drop(state);
4302 self.prefills.remove(request_id);
4303 return Ok(ExecutorExecutionCapacityPreemptionAuthority::RetainedPrefill);
4304 }
4305
4306 let sequence = self
4307 .active
4308 .get(preemption.cache_id())
4309 .cloned()
4310 .ok_or_else(|| {
4311 let detail = self
4312 .active
4313 .values()
4314 .find(|sequence| sequence.request_id() == request_id)
4315 .map_or_else(
4316 || "no active sequence".to_string(),
4317 |sequence| format!("active cache is {}", sequence.cache_id),
4318 );
4319 FerrumError::request_validation(format!(
4320 "vNext request `{request_id}` preemption did not match an authority: {detail}"
4321 ))
4322 })?;
4323 if sequence.request_id() != request_id {
4324 return Err(FerrumError::request_validation(format!(
4325 "vNext cache `{}` belongs to request {}, not {request_id}",
4326 preemption.cache_id(),
4327 sequence.request_id()
4328 )));
4329 }
4330 sequence.preempt_for_recompute()?;
4331 self.active.remove(preemption.cache_id());
4332 Ok(ExecutorExecutionCapacityPreemptionAuthority::ActiveSequence)
4333 }
4334
4335 fn finish_prefill_execution(
4336 &mut self,
4337 slot: &Arc<VNextPrefillSlot<R>>,
4338 sequence: &Arc<VNextSequence<R>>,
4339 ) {
4340 if !self
4341 .prefills
4342 .get(&slot.request_id)
4343 .is_some_and(|current| Arc::ptr_eq(current, slot))
4344 {
4345 return;
4346 }
4347 let mut state = slot.state.lock();
4348 if !matches!(
4349 &*state,
4350 VNextPrefillSlotState::Executing(current) if Arc::ptr_eq(current, sequence)
4351 ) {
4352 return;
4353 }
4354 *state = VNextPrefillSlotState::Terminal;
4355 drop(state);
4356 self.prefills.remove(&slot.request_id);
4357 }
4358}
4359
4360struct VNextPrefillExecutionGuard<'a, R: DeviceRuntime> {
4361 registry: &'a Mutex<VNextSequenceRegistry<R>>,
4362 slot: Arc<VNextPrefillSlot<R>>,
4363 sequence: Arc<VNextSequence<R>>,
4364 armed: bool,
4365}
4366
4367impl<'a, R: DeviceRuntime> VNextPrefillExecutionGuard<'a, R> {
4368 fn new(
4369 registry: &'a Mutex<VNextSequenceRegistry<R>>,
4370 slot: Arc<VNextPrefillSlot<R>>,
4371 sequence: Arc<VNextSequence<R>>,
4372 ) -> Self {
4373 Self {
4374 registry,
4375 slot,
4376 sequence,
4377 armed: true,
4378 }
4379 }
4380
4381 fn disarm(&mut self) {
4382 self.armed = false;
4383 }
4384
4385 fn restore_ready(&mut self) -> Result<()> {
4386 self.registry
4387 .lock()
4388 .restore_prefill_ready(&self.slot, &self.sequence)?;
4389 self.disarm();
4390 Ok(())
4391 }
4392}
4393
4394impl<R: DeviceRuntime> Drop for VNextPrefillExecutionGuard<'_, R> {
4395 fn drop(&mut self) {
4396 if !self.armed {
4397 return;
4398 }
4399 self.registry
4400 .lock()
4401 .finish_prefill_execution(&self.slot, &self.sequence);
4402 self.sequence.abort();
4403 }
4404}
4405
4406pub struct VNextModelExecutor<R: DeviceRuntime> {
4409 info: ModelInfo,
4410 resolved_plan: ResolvedModelPlan,
4411 capability_catalog: CapabilityCatalog,
4412 runtime: Arc<R>,
4413 providers: BoundOperationProviderSet<R>,
4414 policy: ResolvedRuntimePolicy,
4415 plan_resources: Arc<PlanRuntimeResources<R>>,
4416 lane: Arc<ExecutionLane<R>>,
4417 submission_wave_identity: CompiledSubmissionWaveIdentity,
4418 completion_worker: VNextCompletionWorker,
4419 reaper: Arc<CompletionReaper<R>>,
4420 io: VNextIoBinding,
4421 maximum_model_tokens: usize,
4422 attention_head_dimension: usize,
4423 startup_memory_plan: Option<ferrum_types::StartupMemoryPlan>,
4424 run_id: RunId,
4425 family_fingerprint: String,
4426 program_fingerprint: String,
4427 static_provider_attribution: Option<StaticProviderAttributionWitness>,
4428 checkpoint_capture: Option<VNextCheckpointCapture>,
4429 static_bytes: u64,
4430 sequence_state_memory: TypedSequenceStateMemory,
4431 device_reusable_execution_enabled: bool,
4432 reusable_execution_supported: bool,
4433 reusable_execution_startup_plan: Option<VNextReusableExecutionStartupPlan>,
4434 reusable_execution_catalog: RwLock<Option<Arc<VNextReusableExecutionCatalog>>>,
4435 reusable_execution_catalog_refresh_needed: AtomicBool,
4436 startup_reusable_programs: Mutex<
4437 BTreeMap<VNextReusableExecutionDescriptor, BTreeSet<DeviceReusableExecutionProgramId>>,
4438 >,
4439 startup_preparation: Mutex<VNextStartupPreparationState>,
4440 sequences: Mutex<VNextSequenceRegistry<R>>,
4441 prefix_cache: Mutex<prefix_cache::PrefixIndex<SequenceCheckpoint<R>>>,
4442 prefix_capture_identity: Arc<()>,
4443 product_token_mask_residency: Mutex<VNextProductTokenMaskResidency>,
4444 event_sink: RwLock<Option<Arc<dyn ExecutionEventSink>>>,
4445 device_timing_mode: AtomicU8,
4446 diagnostic_fault: Option<VNextDiagnosticFault>,
4447 diagnostic_fault_armed: AtomicBool,
4448 metrics: VNextExecutorMetrics,
4449}
4450
4451impl<R: DeviceRuntime> fmt::Debug for VNextModelExecutor<R> {
4452 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
4453 formatter
4454 .debug_struct("VNextModelExecutor")
4455 .field("model_id", &self.info.model_id)
4456 .field(
4457 "plan_id",
4458 self.resolved_plan.execution_plan().payload().plan_id(),
4459 )
4460 .field("device", &self.runtime.descriptor().id)
4461 .field("maximum_model_tokens", &self.maximum_model_tokens)
4462 .field("retained_sequences", &self.sequences.lock().total_len())
4463 .finish_non_exhaustive()
4464 }
4465}
4466
4467struct VNextStartupSequence {
4468 request_id: RequestId,
4469 kv_cache: Arc<dyn KvCacheHandle>,
4470}
4471
4472struct VNextStartupSequenceGuard<'executor, R: DeviceRuntime> {
4473 executor: &'executor VNextModelExecutor<R>,
4474 pending_request: Option<RequestId>,
4475 sequences: Vec<VNextStartupSequence>,
4476}
4477
4478impl<'executor, R: DeviceRuntime> VNextStartupSequenceGuard<'executor, R> {
4479 fn new(executor: &'executor VNextModelExecutor<R>) -> Self {
4480 Self {
4481 executor,
4482 pending_request: None,
4483 sequences: Vec::new(),
4484 }
4485 }
4486
4487 fn begin_request(&mut self, request_id: RequestId) {
4488 debug_assert!(self.pending_request.is_none());
4489 self.pending_request = Some(request_id);
4490 }
4491
4492 fn activate(&mut self, kv_cache: Arc<dyn KvCacheHandle>) {
4493 let request_id = self
4494 .pending_request
4495 .take()
4496 .expect("startup sequence activation requires a pending request");
4497 self.sequences.push(VNextStartupSequence {
4498 request_id,
4499 kv_cache,
4500 });
4501 }
4502
4503 fn complete(mut self) -> Result<()> {
4504 self.cancel_pending();
4505 let retained = self.take_active_sequences();
4506 if retained.iter().any(|(request_id, sequence)| {
4507 sequence
4508 .as_ref()
4509 .is_none_or(|sequence| sequence.request_id() != request_id)
4510 }) {
4511 for sequence in retained.into_iter().filter_map(|(_, sequence)| sequence) {
4512 sequence.abort();
4513 }
4514 return Err(FerrumError::internal(
4515 "vNext startup completion lost synthetic sequence authority",
4516 ));
4517 }
4518 let sequences = retained
4519 .into_iter()
4520 .map(|(_, sequence)| {
4521 sequence.expect("startup sequence authority was checked before completion")
4522 })
4523 .collect::<Vec<_>>();
4524 for (index, sequence) in sequences.iter().enumerate() {
4525 if let Err(error) = sequence.complete_startup() {
4526 for unfinished in &sequences[index..] {
4527 unfinished.abort();
4528 }
4529 return Err(FerrumError::backend(format!(
4530 "vNext startup sequence completion: {error}"
4531 )));
4532 }
4533 }
4534 Ok(())
4535 }
4536
4537 fn cancel_pending(&mut self) {
4538 if let Some(request_id) = self.pending_request.take() {
4539 self.executor.sequences.lock().cancel_prefill(&request_id);
4540 }
4541 }
4542
4543 fn take_active_sequences(&mut self) -> Vec<(RequestId, Option<Arc<VNextSequence<R>>>)> {
4544 self.sequences
4545 .drain(..)
4546 .map(|startup| {
4547 let sequence = self
4548 .executor
4549 .sequences
4550 .lock()
4551 .active
4552 .remove(&startup.kv_cache.cache_id());
4553 (startup.request_id, sequence)
4554 })
4555 .collect()
4556 }
4557}
4558
4559impl<R: DeviceRuntime> Drop for VNextStartupSequenceGuard<'_, R> {
4560 fn drop(&mut self) {
4561 self.cancel_pending();
4562 for sequence in self
4563 .take_active_sequences()
4564 .into_iter()
4565 .filter_map(|(_, sequence)| sequence)
4566 {
4567 sequence.abort();
4568 }
4569 }
4570}
4571
4572impl<R: DeviceRuntime> VNextModelExecutor<R> {
4573 fn claim_prefill_resource_diagnostic_fault(
4574 &self,
4575 participants: &[VNextExecutionParticipant<'_, R>],
4576 kind: VNextExecutionWaveKind,
4577 ) -> bool {
4578 matches!(
4579 self.diagnostic_fault,
4580 Some(VNextDiagnosticFault::PrefillResourceAfterSubmitOnce)
4581 ) && kind == VNextExecutionWaveKind::Prefill
4582 && matches!(participants, [participant] if participant.sequence.request_origin == ExecutorRequestOrigin::Product)
4583 && self
4584 .diagnostic_fault_armed
4585 .compare_exchange(true, false, Ordering::AcqRel, Ordering::Acquire)
4586 .is_ok()
4587 }
4588
4589 fn resolve_language_io_ids(program: &ModelProgram) -> Result<VNextLanguageIoIds> {
4590 let embedding_nodes = program
4591 .blocks()
4592 .iter()
4593 .flat_map(|block| &block.nodes)
4594 .filter(|node| is_language_token_embedding_operation(node.operation_id.as_str()))
4595 .collect::<Vec<_>>();
4596 let [embedding] = embedding_nodes.as_slice() else {
4597 return Err(FerrumError::model(format!(
4598 "vNext language program requires exactly one token embedding operation, got {}",
4599 embedding_nodes.len()
4600 )));
4601 };
4602 let token_input = embedding.inputs.first().cloned().ok_or_else(|| {
4603 FerrumError::model("vNext token embedding operation has no token input")
4604 })?;
4605
4606 let argmax_nodes = program
4607 .blocks()
4608 .iter()
4609 .flat_map(|block| &block.nodes)
4610 .filter(|node| is_language_masked_argmax_operation(node.operation_id.as_str()))
4611 .collect::<Vec<_>>();
4612 let [argmax] = argmax_nodes.as_slice() else {
4613 return Err(FerrumError::model(format!(
4614 "vNext language program requires exactly one masked argmax operation, got {}",
4615 argmax_nodes.len()
4616 )));
4617 };
4618 let [logits_output, token_mask_input, repetition_token_ids_input, repetition_offsets_input, repetition_penalty_input] =
4619 argmax.inputs.as_slice()
4620 else {
4621 return Err(FerrumError::model(
4622 "vNext masked argmax operation must consume logits, a token mask, and typed sparse repetition policy",
4623 ));
4624 };
4625 let [greedy_token_output] = argmax.outputs.as_slice() else {
4626 return Err(FerrumError::model(
4627 "vNext masked argmax operation must produce one token",
4628 ));
4629 };
4630 let expected_inputs = [
4631 &token_input,
4632 token_mask_input,
4633 repetition_token_ids_input,
4634 repetition_offsets_input,
4635 repetition_penalty_input,
4636 ];
4637 if program.inputs().len() != expected_inputs.len()
4638 || expected_inputs
4639 .iter()
4640 .any(|expected| !program.inputs().contains(expected))
4641 {
4642 return Err(FerrumError::model(
4643 "vNext language program inputs must expose token ids plus typed selection and repetition policy",
4644 ));
4645 }
4646 let expected_outputs = [logits_output, greedy_token_output];
4647 if program.outputs().len() != expected_outputs.len()
4648 || expected_outputs
4649 .iter()
4650 .any(|expected| !program.outputs().contains(expected))
4651 {
4652 return Err(FerrumError::model(
4653 "vNext language program outputs must expose full logits and the selected token",
4654 ));
4655 }
4656 Ok(VNextLanguageIoIds {
4657 token_input,
4658 token_mask_input: token_mask_input.clone(),
4659 repetition_token_ids_input: repetition_token_ids_input.clone(),
4660 repetition_offsets_input: repetition_offsets_input.clone(),
4661 repetition_penalty_input: repetition_penalty_input.clone(),
4662 logits_output: logits_output.clone(),
4663 greedy_token_output: greedy_token_output.clone(),
4664 })
4665 }
4666
4667 pub fn from_runtime_composition<F>(
4668 prepared: &PreparedProductionModel,
4669 info: ModelInfo,
4670 engine_config: &EngineConfig,
4671 runtime: Arc<R>,
4672 registry: OperationRuntimeRegistry<R>,
4673 weight_materializers: WeightMaterializerRegistry,
4674 weight_materializer_selection: WeightMaterializerSelection,
4675 catalog: CapabilityCatalog,
4676 resolve_plan: F,
4677 ) -> Result<Self>
4678 where
4679 F: FnOnce(
4680 &PreparedProductionModel,
4681 &ResolvedRuntimePolicy,
4682 &CapabilityCatalog,
4683 &ProgramPlanCompilation,
4684 ) -> Result<ResolvedModelPlan>,
4685 {
4686 let config =
4687 VNextExecutorConfig::from_engine_config(engine_config, &info, runtime.as_ref())?;
4688 Self::from_runtime_composition_with_config(
4689 prepared,
4690 info,
4691 engine_config,
4692 config,
4693 runtime,
4694 registry,
4695 weight_materializers,
4696 weight_materializer_selection,
4697 catalog,
4698 resolve_plan,
4699 )
4700 }
4701
4702 #[allow(clippy::too_many_arguments)]
4703 pub fn from_runtime_composition_with_config<F>(
4704 prepared: &PreparedProductionModel,
4705 info: ModelInfo,
4706 engine_config: &EngineConfig,
4707 config: VNextExecutorConfig,
4708 runtime: Arc<R>,
4709 registry: OperationRuntimeRegistry<R>,
4710 weight_materializers: WeightMaterializerRegistry,
4711 weight_materializer_selection: WeightMaterializerSelection,
4712 catalog: CapabilityCatalog,
4713 resolve_plan: F,
4714 ) -> Result<Self>
4715 where
4716 F: FnOnce(
4717 &PreparedProductionModel,
4718 &ResolvedRuntimePolicy,
4719 &CapabilityCatalog,
4720 &ProgramPlanCompilation,
4721 ) -> Result<ResolvedModelPlan>,
4722 {
4723 let composition =
4724 VNextRuntimeComposition::new(runtime, registry, weight_materializers, catalog);
4725 composition
4726 .compile_model(
4727 prepared,
4728 info,
4729 engine_config,
4730 config,
4731 weight_materializer_selection,
4732 )?
4733 .initialize(resolve_plan)
4734 }
4735
4736 fn from_compiled_model<F>(compiled: VNextCompiledModel<'_, R>, resolve_plan: F) -> Result<Self>
4737 where
4738 F: FnOnce(
4739 &PreparedProductionModel,
4740 &ResolvedRuntimePolicy,
4741 &CapabilityCatalog,
4742 &ProgramPlanCompilation,
4743 ) -> Result<ResolvedModelPlan>,
4744 {
4745 let VNextCompiledModel {
4746 composition,
4747 prepared,
4748 info,
4749 config,
4750 compilation,
4751 language_io,
4752 checkpoint_selection,
4753 repetition_capacity,
4754 executor_startup,
4755 } = compiled;
4756 let runtime = Arc::clone(&composition.runtime);
4757 let registry = &composition.registry;
4758 let catalog = composition.catalog.clone();
4759 let attention_head_dimension = prepared.descriptor().attention_head_dimension();
4760 let family = prepared.family();
4761 let sequence_state_memory = state_memory::logical_sequence_state_memory(
4762 &family.numerical_profile().states,
4763 &family.numerical_profile().kv_storage,
4764 )?;
4765 let resolve_bind_phase = StartupPhaseTimer::start("plan_resolve_and_bind");
4766 let resolved_plan = resolve_plan(prepared, &config.runtime_policy, &catalog, &compilation)?;
4767 if resolved_plan.execution_plan() != compilation.executable().execution_plan() {
4768 return Err(FerrumError::internal(
4769 "product composition returned a different execution plan than the compiler",
4770 ));
4771 }
4772 let providers = registry
4773 .bind_plan(&resolved_plan)
4774 .map_err(|error| FerrumError::model(format!("vNext provider binding: {error}")))?;
4775 resolve_bind_phase.finish();
4776 let io = Self::resolve_io(
4777 &resolved_plan,
4778 &language_io,
4779 info.vocab_size,
4780 usize::try_from(repetition_capacity)
4781 .map_err(|_| FerrumError::config("vNext repetition capacity exceeds usize"))?,
4782 )?;
4783 let family_fingerprint = family
4784 .fingerprint()
4785 .map_err(|error| FerrumError::model(error.to_string()))?;
4786 let program_fingerprint = family
4787 .program()
4788 .fingerprint()
4789 .map_err(|error| FerrumError::model(error.to_string()))?;
4790 let static_bytes = resolved_plan
4791 .execution_plan()
4792 .payload()
4793 .memory()
4794 .static_bytes();
4795 let run_id = RunId::new(format!("run.vnext.{}", uuid::Uuid::new_v4()))
4796 .map_err(|error| FerrumError::internal(error.to_string()))?;
4797 let provision_request = RequestIdentity::new(format!("request.vnext.provision.{run_id}"))
4798 .map_err(|error| FerrumError::internal(error.to_string()))?;
4799 let provision_phase = StartupPhaseTimer::start("static_provision");
4800 let provisioned = resolved_plan
4801 .execution_plan()
4802 .provision_static(Arc::clone(&runtime), provision_request)
4803 .map_err(|error| FerrumError::device(format!("vNext static provision: {error}")))?;
4804 provision_phase.finish();
4805 let (plan_resources, static_provider_attribution) = match provisioned.into_provisioning() {
4806 StaticProvisioning::NoStatic(no_static) => (no_static.into_plan_runtime(), None),
4807 StaticProvisioning::Required(permit) => {
4808 let identity = ResourceTransactionIdentity::for_admission(
4809 permit.binding(),
4810 run_id.clone(),
4811 TransactionId::new(format!("transaction.vnext.provision.{run_id}"))
4812 .map_err(|error| FerrumError::internal(error.to_string()))?,
4813 );
4814 let driver = RuntimeResourceDriver::new(Arc::clone(&runtime))
4815 .map_err(|error| FerrumError::device(error.to_string()))?;
4816 let transaction_begin_phase =
4817 StartupPhaseTimer::start("resource_transaction_begin");
4818 let transaction = ResourceTransaction::<VNextDriver<R>, TransactionNew>::begin(
4819 driver, identity, permit,
4820 )
4821 .map_err(|error| FerrumError::device(error.to_string()))?;
4822 transaction_begin_phase.finish();
4823 let reserve_phase = StartupPhaseTimer::start("resource_reserve");
4824 let reserved = match transaction.reserve() {
4825 Ok(reserved) => reserved,
4826 Err(error) => {
4827 let message = format!("{:?}", error.failure());
4828 drop(error);
4829 return Err(FerrumError::device(format!(
4830 "vNext static reserve failed: {message}"
4831 )));
4832 }
4833 };
4834 reserve_phase.finish();
4835 let commit_phase = StartupPhaseTimer::start("resource_commit");
4836 let committed = match reserved.commit() {
4837 Ok(committed) => committed,
4838 Err(ResourceCommitTransitionError::Recoverable(error)) => {
4839 let message = format!("{:?}", error.failure());
4840 drop(error);
4841 return Err(FerrumError::device(format!(
4842 "vNext static commit failed: {message}"
4843 )));
4844 }
4845 Err(ResourceCommitTransitionError::Poisoned(error)) => {
4846 let message = format!("{:?}", error.failure());
4847 drop(error);
4848 return Err(FerrumError::device(format!(
4849 "vNext static commit was indeterminate: {message}"
4850 )));
4851 }
4852 };
4853 commit_phase.finish();
4854 let initialized = match committed.initialize_static(
4855 family,
4856 resolved_plan.execution_plan(),
4857 prepared.weights(),
4858 config.static_initialization,
4859 ) {
4860 Ok(initialized) => initialized,
4861 Err(error) => {
4862 let message = error.failure().message().to_owned();
4863 drop(error);
4864 return Err(FerrumError::device(format!(
4865 "vNext static initialization failed: {message}"
4866 )));
4867 }
4868 };
4869 log_static_initialization_receipt(initialized.receipt());
4870 let provider_attribution =
4871 StaticProviderAttributionWitness::from_completed_static_initialization(
4872 &resolved_plan,
4873 initialized.receipt(),
4874 )
4875 .map_err(|error| {
4876 FerrumError::model(format!(
4877 "vNext static provider attribution failed: {error}"
4878 ))
4879 })?;
4880 let handoff_phase = StartupPhaseTimer::start("runtime_handoff");
4881 match initialized.into_plan_runtime() {
4882 Ok(resources) => {
4883 handoff_phase.finish();
4884 (resources, provider_attribution)
4885 }
4886 Err(error) => {
4887 let message = error.error().to_string();
4888 drop(error);
4889 return Err(FerrumError::device(format!(
4890 "vNext runtime handoff failed: {message}"
4891 )));
4892 }
4893 }
4894 }
4895 };
4896 executor_startup.finish();
4897 let lane = ExecutionLane::create(Arc::clone(&runtime)).map_err(|error| {
4898 FerrumError::device(format!("vNext execution lane creation failed: {error:?}"))
4899 })?;
4900 let submission_wave_identity =
4901 OperationDispatch::compile_submission_wave_identity(&resolved_plan, &lane).map_err(
4902 |error| {
4903 FerrumError::model(format!(
4904 "vNext submission-wave identity compilation failed: {error}"
4905 ))
4906 },
4907 )?;
4908 let completion_worker = VNextCompletionWorker::new().map_err(|error| {
4909 FerrumError::device(format!("vNext completion worker creation failed: {error}"))
4910 })?;
4911 let reaper = CompletionReaper::new();
4912 let reusable_execution_supported = runtime
4913 .descriptor()
4914 .capabilities
4915 .iter()
4916 .any(|capability| capability.as_str() == DEVICE_REUSABLE_EXECUTION_CAPABILITY_ID);
4917 let reusable_execution_startup_plan =
4918 if config.device_reusable_execution_enabled && reusable_execution_supported {
4919 let reusable_memory = resolved_plan
4920 .execution_plan()
4921 .payload()
4922 .memory()
4923 .reusable_execution()
4924 .ok_or_else(|| {
4925 FerrumError::internal(
4926 "vNext reusable execution is enabled without a resolved memory plan",
4927 )
4928 })?;
4929 let program_policy = reusable_memory.program_policy().ok_or_else(|| {
4930 FerrumError::internal(
4931 "vNext reusable execution is enabled without a resolved program policy",
4932 )
4933 })?;
4934 let capture_resolution = config
4935 .reusable_execution_capture_resolution
4936 .clone()
4937 .ok_or_else(|| {
4938 FerrumError::internal(
4939 "vNext reusable execution is enabled without capture resolution evidence",
4940 )
4941 })?;
4942 Some(VNextReusableExecutionStartupPlan::resolve(
4943 program_policy,
4944 capture_resolution,
4945 config.maximum_model_tokens,
4946 &config.reusable_execution_prefill_chunks,
4947 reusable_memory.maximum_device_executables(),
4948 )?)
4949 } else {
4950 None
4951 };
4952 let checkpoint_capture = checkpoint_selection
4953 .map(|selection| {
4954 selection.bind(
4955 resolved_plan.execution_plan(),
4956 info.model_id.to_string(),
4957 family_fingerprint.clone(),
4958 program_fingerprint.clone(),
4959 &run_id,
4960 info.vocab_size,
4961 )
4962 })
4963 .transpose()?;
4964
4965 Ok(Self {
4966 info,
4967 resolved_plan,
4968 capability_catalog: catalog,
4969 runtime,
4970 providers,
4971 policy: config.runtime_policy,
4972 plan_resources,
4973 lane,
4974 submission_wave_identity,
4975 completion_worker,
4976 reaper,
4977 io,
4978 maximum_model_tokens: config.maximum_model_tokens,
4979 attention_head_dimension,
4980 startup_memory_plan: config.startup_memory_plan,
4981 run_id,
4982 family_fingerprint,
4983 program_fingerprint,
4984 static_provider_attribution,
4985 checkpoint_capture,
4986 static_bytes,
4987 sequence_state_memory,
4988 device_reusable_execution_enabled: config.device_reusable_execution_enabled,
4989 reusable_execution_supported,
4990 reusable_execution_startup_plan,
4991 reusable_execution_catalog: RwLock::new(None),
4992 reusable_execution_catalog_refresh_needed: AtomicBool::new(false),
4993 startup_reusable_programs: Mutex::new(BTreeMap::new()),
4994 startup_preparation: Mutex::new(VNextStartupPreparationState::Pending),
4995 sequences: Mutex::new(VNextSequenceRegistry::default()),
4996 prefix_cache: Mutex::new(prefix_cache::PrefixIndex::default()),
4997 prefix_capture_identity: Arc::new(()),
4998 product_token_mask_residency: Mutex::new(VNextProductTokenMaskResidency::default()),
4999 event_sink: RwLock::new(None),
5000 device_timing_mode: AtomicU8::new(DeviceTimingMode::Off as u8),
5001 diagnostic_fault: config.diagnostic_fault,
5002 diagnostic_fault_armed: AtomicBool::new(config.diagnostic_fault.is_some()),
5003 metrics: VNextExecutorMetrics::default(),
5004 })
5005 }
5006
5007 pub fn resolved_plan(&self) -> &ResolvedModelPlan {
5008 &self.resolved_plan
5009 }
5010
5011 pub fn capability_catalog(&self) -> &CapabilityCatalog {
5012 &self.capability_catalog
5013 }
5014
5015 async fn admit_startup_sequence(
5016 &self,
5017 resources: &mut VNextStartupSequenceGuard<'_, R>,
5018 input_tokens: Arc<[TokenId]>,
5019 maximum_sequence_tokens: usize,
5020 ) -> Result<bool> {
5021 let Some(request_id) = self
5022 .reserve_startup_sequence(resources, &input_tokens, maximum_sequence_tokens)
5023 .await?
5024 else {
5025 return Ok(false);
5026 };
5027 let input_token_count = input_tokens.len();
5028 let chunk = PrefillChunk::new(0, input_token_count, input_token_count)?;
5029 let Some(kv_cache) = self
5030 .execute_startup_prefill_chunk(
5031 resources,
5032 &request_id,
5033 input_tokens,
5034 maximum_sequence_tokens,
5035 chunk,
5036 "decode-sequence admission",
5037 )
5038 .await?
5039 else {
5040 return Ok(false);
5041 };
5042 resources.activate(kv_cache);
5043 Ok(true)
5044 }
5045
5046 async fn reserve_startup_sequence(
5047 &self,
5048 resources: &mut VNextStartupSequenceGuard<'_, R>,
5049 input_tokens: &[TokenId],
5050 maximum_sequence_tokens: usize,
5051 ) -> Result<Option<RequestId>> {
5052 let request_id = RequestId::new();
5053 resources.begin_request(request_id.clone());
5054 let mut maintenance_attempts = 0_u32;
5055 loop {
5056 match self.try_admit_prefill(ExecutorPrefillAdmission::for_startup(
5057 &request_id,
5058 input_tokens,
5059 maximum_sequence_tokens,
5060 ))? {
5061 ExecutorPrefillAdmissionDecision::Admitted(receipt) => {
5062 if receipt.request_id != request_id {
5063 return Err(FerrumError::internal(
5064 "vNext startup prefill admission changed request identity",
5065 ));
5066 }
5067 break;
5068 }
5069 ExecutorPrefillAdmissionDecision::MaintenanceDeferred(_) => {
5070 if maintenance_attempts >= MAX_BACKING_MAINTENANCE_ATTEMPTS {
5071 return Err(FerrumError::resource_exhausted(format!(
5072 "vNext startup prefill backing did not converge after {maintenance_attempts} attempts"
5073 )));
5074 }
5075 maintenance_attempts += 1;
5076 match self.maintain_prefill_backing(&request_id)? {
5077 ExecutorPrefillMaintenanceOutcome::Maintained { .. }
5078 | ExecutorPrefillMaintenanceOutcome::RetryAdmission { .. } => continue,
5079 ExecutorPrefillMaintenanceOutcome::WaitForRelease { .. } => {
5080 resources.cancel_pending();
5081 return Ok(None);
5082 }
5083 ExecutorPrefillMaintenanceOutcome::NoLongerPending => {
5084 return Err(FerrumError::internal(
5085 "vNext startup backing maintenance lost its retained request",
5086 ));
5087 }
5088 }
5089 }
5090 ExecutorPrefillAdmissionDecision::Deferred(_) => {
5091 resources.cancel_pending();
5092 return Ok(None);
5093 }
5094 ExecutorPrefillAdmissionDecision::PermanentRejected(rejected) => {
5095 return Err(FerrumError::resource_exhausted(format!(
5096 "vNext startup prefill was permanently rejected: {rejected:?}"
5097 )));
5098 }
5099 }
5100 }
5101 Ok(Some(request_id))
5102 }
5103
5104 async fn execute_startup_prefill_chunk(
5105 &self,
5106 resources: &mut VNextStartupSequenceGuard<'_, R>,
5107 request_id: &RequestId,
5108 input_tokens: Arc<[TokenId]>,
5109 maximum_sequence_tokens: usize,
5110 chunk: PrefillChunk,
5111 phase: &'static str,
5112 ) -> Result<Option<Arc<dyn KvCacheHandle>>> {
5113 let input = PlanRuntimePrefillInput::new(
5114 request_id.clone(),
5115 input_tokens,
5116 maximum_sequence_tokens,
5117 chunk,
5118 )?;
5119 match self
5120 .execute_plan_runtime_prefill_with_capacity_policy(
5121 &input,
5122 VNextPrefillFrontierPolicy::ExactStartup,
5123 )
5124 .await?
5125 {
5126 PlanRuntimePrefillOutcome::Completed(completion) => {
5127 let (output, planned, completed, _) = completion.into_parts();
5128 if planned != chunk || completed != chunk {
5129 return Err(FerrumError::internal(format!(
5130 "vNext startup {phase} did not complete its exact {:?} frontier",
5131 chunk.range()
5132 )));
5133 }
5134 let (authority, _) = output.into_parts();
5135 Ok(Some(authority.into_cache()))
5136 }
5137 PlanRuntimePrefillOutcome::Deferred(_) => {
5138 resources.cancel_pending();
5139 Ok(None)
5140 }
5141 }
5142 }
5143
5144 async fn execute_startup_prefill_request(
5145 &self,
5146 chunk: PrefillChunk,
5147 phase: &'static str,
5148 ) -> Result<()> {
5149 let input_tokens: Arc<[TokenId]> = (0..chunk.total_prompt_tokens())
5150 .map(|_| TokenId::new(0))
5151 .collect::<Vec<_>>()
5152 .into();
5153 let mut resources = VNextStartupSequenceGuard::new(self);
5154 let Some(request_id) = self
5155 .reserve_startup_sequence(&mut resources, &input_tokens, chunk.total_prompt_tokens())
5156 .await?
5157 else {
5158 return Err(FerrumError::resource_exhausted(format!(
5159 "vNext startup {phase} could not admit prefill chunk {:?}",
5160 chunk.range()
5161 )));
5162 };
5163 if chunk.tokens_processed() > 0 {
5164 let prefix =
5165 PrefillChunk::new(0, chunk.tokens_processed(), chunk.total_prompt_tokens())?;
5166 if self
5167 .execute_startup_prefill_chunk(
5168 &mut resources,
5169 &request_id,
5170 Arc::clone(&input_tokens),
5171 chunk.total_prompt_tokens(),
5172 prefix,
5173 phase,
5174 )
5175 .await?
5176 .is_none()
5177 {
5178 return Err(FerrumError::resource_exhausted(format!(
5179 "vNext startup {phase} deferred the prerequisite prefill chunk {:?}",
5180 prefix.range()
5181 )));
5182 }
5183 }
5184 let Some(kv_cache) = self
5185 .execute_startup_prefill_chunk(
5186 &mut resources,
5187 &request_id,
5188 input_tokens,
5189 chunk.total_prompt_tokens(),
5190 chunk,
5191 phase,
5192 )
5193 .await?
5194 else {
5195 return Err(FerrumError::resource_exhausted(format!(
5196 "vNext startup {phase} deferred prefill chunk {:?}",
5197 chunk.range()
5198 )));
5199 };
5200 resources.activate(kv_cache);
5201 if resources.sequences.len() != 1 {
5202 return Err(FerrumError::internal(format!(
5203 "vNext startup {phase} retained {} sequences for one prefill descriptor",
5204 resources.sequences.len()
5205 )));
5206 }
5207 resources.complete()
5208 }
5209
5210 async fn execute_startup_decode_pass(
5211 &self,
5212 resources: &mut VNextStartupSequenceGuard<'_, R>,
5213 input_token: TokenId,
5214 width: usize,
5215 phase: &'static str,
5216 ) -> Result<()> {
5217 if width == 0 || width > resources.sequences.len() {
5218 return Err(FerrumError::internal(format!(
5219 "vNext startup {phase} width {width} exceeds {} retained sequences",
5220 resources.sequences.len()
5221 )));
5222 }
5223 let inputs = resources
5224 .sequences
5225 .iter()
5226 .take(width)
5227 .map(|sequence| {
5228 PlanRuntimeDecodeInput::new(
5229 sequence.request_id.clone(),
5230 input_token,
5231 Arc::clone(&sequence.kv_cache),
5232 )
5233 })
5234 .collect::<Vec<_>>();
5235 match self.execute_plan_runtime_decode_batch(&inputs).await? {
5236 PlanRuntimeBatchDecodeOutcome::Completed(outputs) => {
5237 if outputs.len() != width {
5238 return Err(FerrumError::internal(format!(
5239 "vNext startup {phase} returned {} outputs for width {width}",
5240 outputs.len()
5241 )));
5242 }
5243 for (sequence, output) in resources.sequences.iter_mut().take(width).zip(outputs) {
5244 sequence.kv_cache = output.kv_cache;
5245 }
5246 Ok(())
5247 }
5248 PlanRuntimeBatchDecodeOutcome::Deferred(deferred) => {
5249 Err(FerrumError::resource_exhausted(format!(
5250 "vNext startup {phase} width {width} deferred at {:?}",
5251 deferred.stage()
5252 )))
5253 }
5254 }
5255 }
5256
5257 async fn prepare_reusable_execution_startup(
5258 &self,
5259 ) -> Result<VNextReusableExecutionStartupReport> {
5260 let started = Instant::now();
5261 let Some(plan) = self.reusable_execution_startup_plan.clone() else {
5262 self.install_reusable_execution_catalog(VNextReusableExecutionCatalog {
5263 lane_epoch: self.lane.reusable_execution_epoch(),
5264 programs: BTreeMap::new(),
5265 })?;
5266 return Ok(VNextReusableExecutionStartupReport {
5267 enabled: self.device_reusable_execution_enabled,
5268 supported: self.reusable_execution_supported,
5269 eager_fallback_required: self.device_reusable_execution_enabled
5270 && !self.reusable_execution_supported,
5271 resolved_runtime_policy_fingerprint: self.policy.fingerprint_str().to_owned(),
5272 resolved_program_policy: None,
5273 decode_width_resolution: None,
5274 maximum_device_executables: 0,
5275 requested_descriptors: Vec::new(),
5276 prepared_descriptors: Vec::new(),
5277 capture_case_receipts: Vec::new(),
5278 catalog_programs: Vec::new(),
5279 requested_decode_widths: Vec::new(),
5280 prepared_decode_widths: Vec::new(),
5281 requested_prefill_token_counts: Vec::new(),
5282 prepared_prefill_token_counts: Vec::new(),
5283 requested_prefill_chunks: Vec::new(),
5284 prepared_prefill_chunks: Vec::new(),
5285 synthetic_sequences: 0,
5286 eager_warmup_waves: 0,
5287 capture_waves: 0,
5288 replay_inventory_check_waves: 0,
5289 prepared_programs: 0,
5290 device_preparation: DeviceReusableExecutionPreparation::unsupported(),
5291 elapsed_ms: started.elapsed().as_millis().min(u64::MAX as u128) as u64,
5292 });
5293 };
5294
5295 let requested_descriptors = plan.descriptors.clone();
5296 let requested_decode_widths = plan.decode_widths();
5297 let requested_prefill_chunks = plan.prefill_chunks();
5298 let requested_prefill_token_counts = plan.prefill_token_counts();
5299 if plan.device_plan.catalog_lifetime() == ReusableExecutionCatalogLifetime::OnDemandBounded
5300 {
5301 return self.prepare_on_demand_reusable_execution(&plan, started);
5302 }
5303 for _ in 0..plan.warmup_passes {
5304 for chunk in requested_prefill_chunks.iter().copied() {
5305 self.execute_startup_prefill_request(chunk, "eager prefill warmup")
5306 .await?;
5307 }
5308 }
5309
5310 let input_tokens: Arc<[TokenId]> = Arc::from([TokenId::new(0)]);
5311 let input_token = TokenId::new(0);
5312 let mut resources = VNextStartupSequenceGuard::new(self);
5313 let requested_sequences = requested_decode_widths.first().copied().ok_or_else(|| {
5314 FerrumError::internal("vNext reusable execution plan has no decode widths")
5315 })?;
5316 if requested_sequences > MAXIMUM_REUSABLE_EXECUTION_STARTUP_CAPTURE_WIDTH {
5317 return Err(FerrumError::config(format!(
5318 "vNext reusable startup requested {requested_sequences} synthetic sequences, exceeding the independent hard bound {MAXIMUM_REUSABLE_EXECUTION_STARTUP_CAPTURE_WIDTH}"
5319 )));
5320 }
5321 for _ in 0..requested_sequences {
5322 if !self
5323 .admit_startup_sequence(
5324 &mut resources,
5325 Arc::clone(&input_tokens),
5326 plan.maximum_decode_sequence_tokens,
5327 )
5328 .await?
5329 {
5330 break;
5331 }
5332 }
5333 if resources.sequences.len() != requested_sequences {
5334 return Err(FerrumError::resource_exhausted(
5335 format!(
5336 "vNext reusable execution startup admitted {} of {requested_sequences} synthetic sequences; refusing to shrink the fingerprinted exact matrix",
5337 resources.sequences.len()
5338 ),
5339 ));
5340 }
5341 let prepared_decode_widths = requested_decode_widths.clone();
5342
5343 for _ in 0..plan.warmup_passes {
5344 for width in prepared_decode_widths.iter().copied() {
5345 self.execute_startup_decode_pass(
5346 &mut resources,
5347 input_token,
5348 width,
5349 "eager warmup",
5350 )
5351 .await?;
5352 }
5353 }
5354
5355 let configured = self
5356 .lane
5357 .configure_reusable_executables(plan.device_plan)
5358 .map_err(|error| {
5359 FerrumError::device(format!(
5360 "vNext reusable execution configuration failed: {error}"
5361 ))
5362 })?;
5363 if configured.state() != DeviceReusableExecutionPreparationState::Preparing {
5364 return Err(FerrumError::internal(format!(
5365 "vNext reusable execution capability configured as {:?}",
5366 configured.state()
5367 )));
5368 }
5369
5370 for _ in 0..plan.capture_passes {
5371 for width in prepared_decode_widths.iter().copied() {
5372 self.execute_startup_decode_pass(&mut resources, input_token, width, "capture")
5373 .await?;
5374 }
5375 }
5376 let captured = self
5377 .lane
5378 .reusable_executable_preparation()
5379 .map_err(|error| {
5380 FerrumError::device(format!(
5381 "vNext reusable execution capture inspection failed: {error}"
5382 ))
5383 })?;
5384 if captured.state() != DeviceReusableExecutionPreparationState::Preparing
5385 || captured.captured_executables() != captured.uploaded_executables()
5386 || captured.uploaded_executables() != captured.resident_executables()
5387 {
5388 return Err(FerrumError::device(format!(
5389 "vNext reusable execution capture receipt is incomplete: {captured:?}"
5390 )));
5391 }
5392 for _ in 0..plan.replay_validation_passes {
5393 for width in prepared_decode_widths.iter().copied() {
5394 self.execute_startup_decode_pass(
5395 &mut resources,
5396 input_token,
5397 width,
5398 "replay inventory check",
5399 )
5400 .await?;
5401 }
5402 }
5403 let replayed = self
5404 .lane
5405 .reusable_executable_preparation()
5406 .map_err(|error| {
5407 FerrumError::device(format!(
5408 "vNext reusable execution replay inspection failed: {error}"
5409 ))
5410 })?;
5411 if replayed.state() != DeviceReusableExecutionPreparationState::Preparing
5412 || !reusable_executable_inventory_matches(captured, replayed)
5413 {
5414 return Err(FerrumError::device(format!(
5415 "vNext replay inventory check compiled or changed executable state: before={captured:?}, after={replayed:?}"
5416 )));
5417 }
5418
5419 let synthetic_sequences = resources.sequences.len();
5420 resources.complete()?;
5421
5422 let mut captured = replayed;
5423 for _ in 0..plan.capture_passes {
5424 for chunk in requested_prefill_chunks.iter().copied() {
5425 self.execute_startup_prefill_request(chunk, "prefill capture")
5426 .await?;
5427 let prefill_captured =
5428 self.lane
5429 .reusable_executable_preparation()
5430 .map_err(|error| {
5431 FerrumError::device(format!(
5432 "vNext {:?} prefill capture inspection failed: {error}",
5433 chunk.range()
5434 ))
5435 })?;
5436 if prefill_captured.state() != DeviceReusableExecutionPreparationState::Preparing
5437 || prefill_captured.captured_executables()
5438 != prefill_captured.uploaded_executables()
5439 || prefill_captured.uploaded_executables()
5440 != prefill_captured.resident_executables()
5441 || prefill_captured.captured_executables() < captured.captured_executables()
5442 {
5443 return Err(FerrumError::device(format!(
5444 "vNext {:?} prefill capture receipt is incomplete: before={captured:?}, after={prefill_captured:?}",
5445 chunk.range()
5446 )));
5447 }
5448 captured = prefill_captured;
5449 }
5450 }
5451
5452 for _ in 0..plan.replay_validation_passes {
5453 for chunk in requested_prefill_chunks.iter().copied() {
5454 self.execute_startup_prefill_request(chunk, "fresh-request prefill replay")
5455 .await?;
5456 let prefill_replayed =
5457 self.lane
5458 .reusable_executable_preparation()
5459 .map_err(|error| {
5460 FerrumError::device(format!(
5461 "vNext {:?} fresh-request prefill replay inspection failed: {error}",
5462 chunk.range()
5463 ))
5464 })?;
5465 if prefill_replayed.state() != DeviceReusableExecutionPreparationState::Preparing
5466 || !reusable_executable_inventory_matches(captured, prefill_replayed)
5467 {
5468 return Err(FerrumError::device(format!(
5469 "vNext {:?} fresh-request prefill replay changed executable state: before={captured:?}, after={prefill_replayed:?}",
5470 chunk.range()
5471 )));
5472 }
5473 }
5474 }
5475
5476 let device_preparation = self.lane.seal_reusable_executables().map_err(|error| {
5477 FerrumError::device(format!("vNext reusable execution sealing failed: {error}"))
5478 })?;
5479 if device_preparation.state() != DeviceReusableExecutionPreparationState::Ready
5480 || device_preparation.uploaded_executables() < device_preparation.resident_executables()
5481 || !reusable_executable_inventory_matches(captured, device_preparation)
5482 {
5483 return Err(FerrumError::device(format!(
5484 "vNext reusable execution sealing produced an unusable receipt: {device_preparation:?}"
5485 )));
5486 }
5487 let catalog = self.lane.reusable_execution_catalog().map_err(|error| {
5488 FerrumError::device(format!(
5489 "vNext reusable execution catalog inspection failed: {error}"
5490 ))
5491 })?;
5492 let (catalog_epoch, catalog) = catalog.into_parts();
5493 let mut catalog_by_id = BTreeMap::new();
5494 for program in catalog {
5495 let program_id = program.program_id();
5496 if program_id.plan_hash() != self.resolved_plan.execution_plan().plan_hash()
5497 || program_id.runtime_implementation_fingerprint()
5498 != self.runtime.descriptor().runtime_implementation_fingerprint
5499 || program_id.lane_id() != self.lane.id()
5500 || program.segments().iter().any(|segment| {
5501 segment.end_node_index() as usize
5502 > self.resolved_plan.execution_plan().payload().nodes().len()
5503 })
5504 {
5505 return Err(FerrumError::internal(
5506 "vNext reusable execution catalog differs from its immutable plan or lane",
5507 ));
5508 }
5509 if catalog_by_id.insert(program_id.clone(), program).is_some() {
5510 return Err(FerrumError::internal(
5511 "vNext reusable execution catalog contains a duplicate program identity",
5512 ));
5513 }
5514 }
5515 if catalog_by_id.len() > plan.program_policy.programs().len() {
5516 return Err(FerrumError::internal(format!(
5517 "vNext reusable execution observed {} physical programs for {} budgeted startup capture cases",
5518 catalog_by_id.len(),
5519 plan.program_policy.programs().len()
5520 )));
5521 }
5522 let startup_programs = self.startup_reusable_programs.lock().clone();
5523 let requested_descriptor_set = requested_descriptors
5524 .iter()
5525 .copied()
5526 .collect::<BTreeSet<_>>();
5527 if let Some((descriptor, observed_program_count)) =
5528 reusable_startup_case_budget_violation(&requested_descriptor_set, &startup_programs)
5529 {
5530 return Err(FerrumError::internal(format!(
5531 "vNext reusable startup case {descriptor:?} observed {} physical variants; the resolved policy budgets exactly one",
5532 observed_program_count
5533 )));
5534 }
5535 let observed_program_ids = startup_programs
5536 .values()
5537 .flat_map(|programs| programs.iter())
5538 .collect::<BTreeSet<_>>();
5539 if catalog_by_id
5540 .keys()
5541 .any(|program_id| !observed_program_ids.contains(program_id))
5542 {
5543 return Err(FerrumError::internal(
5544 "vNext reusable execution catalog contains a physical program not observed by a resolved startup capture case",
5545 ));
5546 }
5547 let prepared_programs = catalog_by_id
5548 .values()
5549 .filter(|program| program.has_resident_segments())
5550 .count();
5551 if !reusable_execution_program_catalog_is_usable(device_preparation, prepared_programs) {
5552 return Err(FerrumError::internal(format!(
5553 "vNext reusable execution sealed {} resident executable segments but registered no typed programs",
5554 device_preparation.resident_executables()
5555 )));
5556 }
5557 let catalog_programs = catalog_by_id
5558 .values()
5559 .map(VNextReusableExecutionCatalogProgramReceipt::from_program)
5560 .collect::<Vec<_>>();
5561 let capture_case_receipts = requested_descriptors
5562 .iter()
5563 .copied()
5564 .map(|descriptor| {
5565 let observed = startup_programs.get(&descriptor);
5566 let observed_program_fingerprints = observed
5567 .into_iter()
5568 .flat_map(|programs| programs.iter())
5569 .map(DeviceReusableExecutionProgramId::fingerprint)
5570 .collect::<Vec<_>>();
5571 let resident_program_fingerprints = observed
5572 .into_iter()
5573 .flat_map(|programs| programs.iter())
5574 .filter(|program_id| {
5575 catalog_by_id
5576 .get(*program_id)
5577 .is_some_and(DeviceReusableExecutionProgram::has_resident_segments)
5578 })
5579 .map(DeviceReusableExecutionProgramId::fingerprint)
5580 .collect::<Vec<_>>();
5581 VNextReusableExecutionCaptureCaseReceipt {
5582 descriptor,
5583 observed_program_fingerprints,
5584 resident_program_fingerprints,
5585 }
5586 })
5587 .collect::<Vec<_>>();
5588 let prepared_descriptors = capture_case_receipts
5589 .iter()
5590 .filter(|receipt| !receipt.resident_program_fingerprints.is_empty())
5591 .map(|receipt| receipt.descriptor)
5592 .collect::<Vec<_>>();
5593 let prepared_descriptor_set = prepared_descriptors
5594 .iter()
5595 .copied()
5596 .collect::<BTreeSet<_>>();
5597 let resident_prepared_decode_widths = prepared_descriptors
5598 .iter()
5599 .filter_map(|descriptor| match descriptor {
5600 VNextReusableExecutionDescriptor::UniformDecode {
5601 request_capacity, ..
5602 } => Some(*request_capacity),
5603 VNextReusableExecutionDescriptor::Prefill { .. } => None,
5604 })
5605 .collect::<Vec<_>>();
5606 let prepared_prefill_chunks = requested_prefill_chunks
5607 .iter()
5608 .copied()
5609 .filter(|chunk| {
5610 let main = VNextReusableExecutionStartupPlan::descriptor_for_chunk(*chunk);
5611 if !prepared_descriptor_set.contains(&main) {
5612 return false;
5613 }
5614 if chunk.tokens_processed() == 0 {
5615 return true;
5616 }
5617 prepared_descriptor_set.contains(&VNextReusableExecutionDescriptor::Prefill {
5618 tokens_processed: 0,
5619 token_capacity: chunk.tokens_processed(),
5620 total_prompt_tokens: chunk.total_prompt_tokens(),
5621 request_capacity: 1,
5622 })
5623 })
5624 .collect::<Vec<_>>();
5625 let mut prepared_prefill_token_counts = prepared_prefill_chunks
5626 .iter()
5627 .map(|chunk| chunk.tokens_to_process())
5628 .collect::<Vec<_>>();
5629 prepared_prefill_token_counts.sort_unstable_by(|left, right| right.cmp(left));
5630 prepared_prefill_token_counts.dedup();
5631 let incomplete_capture_cases = prepared_descriptors.len() != requested_descriptors.len();
5632 self.install_reusable_execution_catalog(VNextReusableExecutionCatalog {
5633 lane_epoch: catalog_epoch,
5634 programs: catalog_by_id,
5635 })?;
5636 let requested_wave_shapes = prepared_decode_widths.len() + plan.prefill_wave_shapes();
5637 Ok(VNextReusableExecutionStartupReport {
5638 enabled: true,
5639 supported: true,
5640 eager_fallback_required: reusable_execution_requires_eager_fallback(device_preparation)
5641 || plan.decode_catalog_omits_admitted_widths()
5642 || incomplete_capture_cases,
5643 resolved_runtime_policy_fingerprint: self.policy.fingerprint_str().to_owned(),
5644 resolved_program_policy: Some(plan.program_policy.clone()),
5645 decode_width_resolution: Some(plan.capture_resolution.clone()),
5646 maximum_device_executables: plan.device_plan.maximum_executables(),
5647 requested_descriptors,
5648 prepared_descriptors,
5649 capture_case_receipts,
5650 catalog_programs,
5651 requested_decode_widths,
5652 prepared_decode_widths: resident_prepared_decode_widths,
5653 requested_prefill_token_counts: requested_prefill_token_counts.clone(),
5654 prepared_prefill_token_counts,
5655 requested_prefill_chunks: requested_prefill_chunks.clone(),
5656 prepared_prefill_chunks,
5657 synthetic_sequences,
5658 eager_warmup_waves: requested_wave_shapes * plan.warmup_passes,
5659 capture_waves: requested_wave_shapes * plan.capture_passes,
5660 replay_inventory_check_waves: requested_wave_shapes * plan.replay_validation_passes,
5661 prepared_programs,
5662 device_preparation,
5663 elapsed_ms: started.elapsed().as_millis().min(u64::MAX as u128) as u64,
5664 })
5665 }
5666
5667 fn reset_request_metrics_after_startup(&self) -> Result<()> {
5668 if self.sequences.lock().total_len() != 0 {
5669 return Err(FerrumError::internal(
5670 "vNext startup cleanup retained synthetic sequence authority",
5671 ));
5672 }
5673 if !self.completion_worker.reset_metrics_if_idle() {
5674 return Err(FerrumError::internal(
5675 "vNext startup cleanup left a completion task in flight",
5676 ));
5677 }
5678 self.reaper.reset_checkpoint_timings();
5679 self.product_token_mask_residency.lock().clear();
5682 self.metrics.reset_after_startup();
5683 Ok(())
5684 }
5685
5686 fn device_timing_mode(&self) -> DeviceTimingMode {
5687 match self.device_timing_mode.load(Ordering::Acquire) {
5688 value if value == DeviceTimingMode::Verification as u8 => {
5689 DeviceTimingMode::Verification
5690 }
5691 value if value == DeviceTimingMode::Kernel as u8 => DeviceTimingMode::Kernel,
5692 value if value == DeviceTimingMode::Replay as u8 => DeviceTimingMode::Replay,
5693 value if value == DeviceTimingMode::Completion as u8 => DeviceTimingMode::Completion,
5694 _ => DeviceTimingMode::Off,
5695 }
5696 }
5697
5698 fn host_dispatch_timing_enabled(&self) -> bool {
5699 self.device_timing_mode() != DeviceTimingMode::Off
5700 }
5701
5702 fn token_mask_residency_eligible(
5703 executable: &impl ExecutablePlanView,
5704 token_mask_input: &ResolvedValueBinding,
5705 ) -> bool {
5706 let [component] = token_mask_input.storage().components() else {
5707 return false;
5708 };
5709 let memory = executable.execution_plan().payload().memory();
5710 let Some(descriptor) = memory
5711 .dynamic_descriptors()
5712 .iter()
5713 .find(|descriptor| descriptor.base_resource_id() == component.resource_id())
5714 else {
5715 return false;
5716 };
5717 if token_mask_input.usage() != BufferUsage::Activations
5718 || descriptor.lifetime() != AllocationLifetime::Step
5719 || descriptor.kind() != &AllocationKind::Value
5720 || descriptor.usage() != BufferUsage::Activations
5721 || descriptor.element_type() != ElementType::U8
5722 || descriptor.initialization() != StateInitialization::None
5723 || !matches!(
5724 descriptor.demand(),
5725 DynamicResourceDemand::ActualSequences { .. }
5726 )
5727 {
5728 return false;
5729 }
5730 let mut matching_slots = memory
5731 .dynamic_pools()
5732 .iter()
5733 .flat_map(DynamicBackingPoolSpec::step_resource_slots)
5734 .filter(|slot| slot.resource_ids().contains(&component.resource_id()));
5735 let Some(slot) = matching_slots.next() else {
5736 return false;
5737 };
5738 matching_slots.next().is_none()
5739 && slot.kind() == StepResourceSlotKind::Dedicated
5740 && slot.resource_ids() == std::slice::from_ref(component.resource_id())
5741 }
5742
5743 fn resolve_io(
5744 executable: &impl ExecutablePlanView,
5745 language_io: &VNextLanguageIoIds,
5746 expected_vocab: usize,
5747 expected_repetition_capacity: usize,
5748 ) -> Result<VNextIoBinding> {
5749 let nodes = executable.execution_plan().payload().nodes();
5750 let input_matches = nodes
5751 .iter()
5752 .flat_map(|node| node.values().iter().map(move |value| (node.id(), value)))
5753 .filter(|(_, value)| {
5754 value.value_id() == &language_io.token_input
5755 && value.role() == ResolvedValueRole::Input
5756 })
5757 .collect::<Vec<_>>();
5758 let token_mask_input_matches = nodes
5759 .iter()
5760 .flat_map(|node| node.values().iter().map(move |value| (node.id(), value)))
5761 .filter(|(_, value)| {
5762 value.value_id() == &language_io.token_mask_input
5763 && value.role() == ResolvedValueRole::Input
5764 })
5765 .collect::<Vec<_>>();
5766 let repetition_token_ids_input_matches = nodes
5767 .iter()
5768 .flat_map(|node| node.values().iter().map(move |value| (node.id(), value)))
5769 .filter(|(_, value)| {
5770 value.value_id() == &language_io.repetition_token_ids_input
5771 && value.role() == ResolvedValueRole::Input
5772 })
5773 .collect::<Vec<_>>();
5774 let repetition_offsets_input_matches = nodes
5775 .iter()
5776 .flat_map(|node| node.values().iter().map(move |value| (node.id(), value)))
5777 .filter(|(_, value)| {
5778 value.value_id() == &language_io.repetition_offsets_input
5779 && value.role() == ResolvedValueRole::Input
5780 })
5781 .collect::<Vec<_>>();
5782 let repetition_penalty_input_matches = nodes
5783 .iter()
5784 .flat_map(|node| node.values().iter().map(move |value| (node.id(), value)))
5785 .filter(|(_, value)| {
5786 value.value_id() == &language_io.repetition_penalty_input
5787 && value.role() == ResolvedValueRole::Input
5788 })
5789 .collect::<Vec<_>>();
5790 let logits_output_matches = nodes
5791 .iter()
5792 .flat_map(|node| node.values().iter().map(move |value| (node.id(), value)))
5793 .filter(|(_, value)| {
5794 value.value_id() == &language_io.logits_output
5795 && value.role() == ResolvedValueRole::Output
5796 })
5797 .collect::<Vec<_>>();
5798 let greedy_token_output_matches = nodes
5799 .iter()
5800 .flat_map(|node| node.values().iter().map(move |value| (node.id(), value)))
5801 .filter(|(_, value)| {
5802 value.value_id() == &language_io.greedy_token_output
5803 && value.role() == ResolvedValueRole::Output
5804 })
5805 .collect::<Vec<_>>();
5806 let [(input_node_id, input)] = input_matches.as_slice() else {
5807 return Err(FerrumError::model(
5808 "compiled vNext plan must bind the token input exactly once",
5809 ));
5810 };
5811 let [(token_mask_input_node_id, token_mask_input)] = token_mask_input_matches.as_slice()
5812 else {
5813 return Err(FerrumError::model(
5814 "compiled vNext plan must bind the token-selection mask exactly once",
5815 ));
5816 };
5817 let [(repetition_token_ids_input_node_id, repetition_token_ids_input)] =
5818 repetition_token_ids_input_matches.as_slice()
5819 else {
5820 return Err(FerrumError::model(
5821 "compiled vNext plan must bind sparse repetition token ids exactly once",
5822 ));
5823 };
5824 let [(repetition_offsets_input_node_id, repetition_offsets_input)] =
5825 repetition_offsets_input_matches.as_slice()
5826 else {
5827 return Err(FerrumError::model(
5828 "compiled vNext plan must bind sparse repetition offsets exactly once",
5829 ));
5830 };
5831 let [(repetition_penalty_input_node_id, repetition_penalty_input)] =
5832 repetition_penalty_input_matches.as_slice()
5833 else {
5834 return Err(FerrumError::model(
5835 "compiled vNext plan must bind sparse repetition penalty exactly once",
5836 ));
5837 };
5838 let [(output_node_id, output)] = logits_output_matches.as_slice() else {
5839 return Err(FerrumError::model(
5840 "compiled vNext plan must bind the logits output exactly once",
5841 ));
5842 };
5843 let [(greedy_token_output_node_id, greedy_token_output)] =
5844 greedy_token_output_matches.as_slice()
5845 else {
5846 return Err(FerrumError::model(
5847 "compiled vNext plan must bind the selected-token output exactly once",
5848 ));
5849 };
5850 if input.tensor().element_type() != ElementType::U32 {
5851 return Err(FerrumError::model(
5852 "compiled vNext token input must use U32 elements",
5853 ));
5854 }
5855 let expected_vocab_u64 = u64::try_from(expected_vocab)
5856 .map_err(|_| FerrumError::model("vNext vocabulary exceeds u64"))?;
5857 if token_mask_input.tensor().element_type() != ElementType::U8
5858 || token_mask_input.tensor().dimensions() != [expected_vocab_u64]
5859 || !matches!(
5860 token_mask_input.tensor().layout(),
5861 ResolvedTensorLayout::Contiguous
5862 )
5863 {
5864 return Err(FerrumError::model(
5865 "compiled vNext token-selection mask must be contiguous U8[vocab]",
5866 ));
5867 }
5868 let expected_repetition_capacity_u64 = u64::try_from(expected_repetition_capacity)
5869 .map_err(|_| FerrumError::model("vNext repetition capacity exceeds u64"))?;
5870 let contiguous = |value: &ResolvedValueBinding| {
5871 matches!(value.tensor().layout(), ResolvedTensorLayout::Contiguous)
5872 };
5873 if repetition_token_ids_input.tensor().element_type() != ElementType::U32
5874 || repetition_token_ids_input.tensor().dimensions()
5875 != [expected_repetition_capacity_u64]
5876 || !contiguous(repetition_token_ids_input)
5877 {
5878 return Err(FerrumError::model(
5879 "compiled vNext sparse repetition ids must be contiguous U32[capacity]",
5880 ));
5881 }
5882 if repetition_offsets_input.tensor().element_type() != ElementType::U32
5883 || repetition_offsets_input.tensor().dimensions() != [2]
5884 || !contiguous(repetition_offsets_input)
5885 {
5886 return Err(FerrumError::model(
5887 "compiled vNext sparse repetition offsets must be contiguous U32[2]",
5888 ));
5889 }
5890 if repetition_penalty_input.tensor().element_type() != ElementType::F32
5891 || repetition_penalty_input.tensor().dimensions() != [1]
5892 || !contiguous(repetition_penalty_input)
5893 {
5894 return Err(FerrumError::model(
5895 "compiled vNext sparse repetition penalty must be contiguous F32[1]",
5896 ));
5897 }
5898 let [component] = output.storage().components() else {
5899 return Err(FerrumError::model(
5900 "compiled vNext logits output must use one physical component",
5901 ));
5902 };
5903 let output_elements_u64 = output
5904 .tensor()
5905 .dimensions()
5906 .iter()
5907 .try_fold(1_u64, |total, extent| total.checked_mul(*extent))
5908 .ok_or_else(|| FerrumError::model("vNext logits element count overflows u64"))?;
5909 let output_elements = usize::try_from(output_elements_u64)
5910 .map_err(|_| FerrumError::model("vNext logits exceed host address space"))?;
5911 if output_elements != expected_vocab {
5912 return Err(FerrumError::model(format!(
5913 "compiled vNext logits contain {output_elements} elements, expected vocabulary {expected_vocab}"
5914 )));
5915 }
5916 let output_element_type = output.tensor().element_type();
5917 if !matches!(
5918 output_element_type,
5919 ElementType::F16 | ElementType::Bf16 | ElementType::F32
5920 ) {
5921 return Err(FerrumError::model(format!(
5922 "compiled vNext logits use unsupported element type {output_element_type:?}"
5923 )));
5924 }
5925 let output_layout = HostTransferLayout::new(output_element_type, output_elements_u64)
5926 .map_err(|error| FerrumError::model(error.to_string()))?;
5927 let [greedy_token_component] = greedy_token_output.storage().components() else {
5928 return Err(FerrumError::model(
5929 "compiled vNext selected-token output must use one physical component",
5930 ));
5931 };
5932 if greedy_token_output.tensor().element_type() != ElementType::U32
5933 || greedy_token_output.tensor().dimensions() != [1]
5934 {
5935 return Err(FerrumError::model(
5936 "compiled vNext selected-token output must be U32[1]",
5937 ));
5938 }
5939 let greedy_token_output_layout = HostTransferLayout::new(ElementType::U32, 1)
5940 .map_err(|error| FerrumError::model(error.to_string()))?;
5941 let token_mask_residency_eligible =
5942 Self::token_mask_residency_eligible(executable, token_mask_input);
5943 Ok(VNextIoBinding {
5944 input_node_id: (*input_node_id).clone(),
5945 input_ordinal: input.ordinal(),
5946 token_mask_input_node_id: (*token_mask_input_node_id).clone(),
5947 token_mask_input_ordinal: token_mask_input.ordinal(),
5948 token_mask_residency_eligible,
5949 repetition_token_ids_input_node_id: (*repetition_token_ids_input_node_id).clone(),
5950 repetition_token_ids_input_ordinal: repetition_token_ids_input.ordinal(),
5951 repetition_offsets_input_node_id: (*repetition_offsets_input_node_id).clone(),
5952 repetition_offsets_input_ordinal: repetition_offsets_input.ordinal(),
5953 repetition_penalty_input_node_id: (*repetition_penalty_input_node_id).clone(),
5954 repetition_penalty_input_ordinal: repetition_penalty_input.ordinal(),
5955 repetition_capacity: expected_repetition_capacity,
5956 output_node_id: (*output_node_id).clone(),
5957 output_resource_id: component.resource_id().clone(),
5958 output_offset_bytes: component.offset_bytes(),
5959 output_layout,
5960 output_element_type,
5961 output_elements,
5962 greedy_token_output_node_id: (*greedy_token_output_node_id).clone(),
5963 greedy_token_output_resource_id: greedy_token_component.resource_id().clone(),
5964 greedy_token_output_offset_bytes: greedy_token_component.offset_bytes(),
5965 greedy_token_output_layout,
5966 })
5967 }
5968
5969 fn fail_prefill_probe(&self, slot: &Arc<VNextPrefillSlot<R>>) {
5970 let mut sequences = self.sequences.lock();
5971 if !sequences
5972 .prefills
5973 .get(&slot.request_id)
5974 .is_some_and(|current| Arc::ptr_eq(current, slot))
5975 {
5976 return;
5977 }
5978 let mut state = slot.state.lock();
5979 if matches!(&*state, VNextPrefillSlotState::Probing) {
5980 *state = VNextPrefillSlotState::Terminal;
5981 drop(state);
5982 sequences.prefills.remove(&slot.request_id);
5983 }
5984 }
5985
5986 fn publish_prefill_probe(
5987 &self,
5988 slot: &Arc<VNextPrefillSlot<R>>,
5989 resolution: VNextPrefillProbeResolution<R>,
5990 ) -> Result<ExecutorPrefillAdmissionDecision> {
5991 let projection = match &resolution {
5992 VNextPrefillProbeResolution::MaintenanceDeferred { pending, .. } => {
5993 pending.projection(&slot.request_id).map(Some)
5994 }
5995 _ => Ok(None),
5996 };
5997 let projection = match projection {
5998 Ok(projection) => projection,
5999 Err(error) => {
6000 resolution.abort();
6001 self.fail_prefill_probe(slot);
6002 return Err(error);
6003 }
6004 };
6005 let mut sequences = self.sequences.lock();
6006 let current = sequences
6007 .prefills
6008 .get(&slot.request_id)
6009 .is_some_and(|current| Arc::ptr_eq(current, slot));
6010 let mut state = slot.state.lock();
6011 if !current
6012 || slot.cancelled.load(Ordering::Acquire)
6013 || !matches!(&*state, VNextPrefillSlotState::Probing)
6014 {
6015 let prior = std::mem::replace(&mut *state, VNextPrefillSlotState::Terminal);
6016 drop(state);
6017 if current {
6018 sequences.prefills.remove(&slot.request_id);
6019 }
6020 drop(sequences);
6021 prior.abort();
6022 resolution.abort();
6023 return Err(FerrumError::cancelled(format!(
6024 "vNext prefill probe for `{}` lost its request authority",
6025 slot.request_id
6026 )));
6027 }
6028
6029 let (decision, terminal) = match resolution {
6030 VNextPrefillProbeResolution::Deferred(deferred) => {
6031 *state = VNextPrefillSlotState::Deferred {
6032 maintenance: None,
6033 maintaining: false,
6034 };
6035 (ExecutorPrefillAdmissionDecision::Deferred(deferred), false)
6036 }
6037 VNextPrefillProbeResolution::MaintenanceDeferred { pending } => {
6038 *state = VNextPrefillSlotState::Deferred {
6039 maintenance: Some(pending),
6040 maintaining: false,
6041 };
6042 (
6043 ExecutorPrefillAdmissionDecision::MaintenanceDeferred(
6044 projection.expect("maintenance projection was constructed"),
6045 ),
6046 false,
6047 )
6048 }
6049 VNextPrefillProbeResolution::Ready(sequence) => {
6050 *state = VNextPrefillSlotState::Ready(sequence);
6051 (
6052 ExecutorPrefillAdmissionDecision::Admitted(ExecutorPrefillAdmissionReceipt {
6053 request_id: slot.request_id.clone(),
6054 }),
6055 false,
6056 )
6057 }
6058 VNextPrefillProbeResolution::PermanentRejected(rejected) => {
6059 *state = VNextPrefillSlotState::Terminal;
6060 (
6061 ExecutorPrefillAdmissionDecision::PermanentRejected(rejected),
6062 true,
6063 )
6064 }
6065 };
6066 drop(state);
6067 if terminal {
6068 sequences.prefills.remove(&slot.request_id);
6069 }
6070 Ok(decision)
6071 }
6072
6073 fn resolve_prefill_probe(
6074 &self,
6075 request_id: &RequestId,
6076 request_origin: ExecutorRequestOrigin,
6077 maximum_tokens: usize,
6078 tokens: Vec<u32>,
6079 product_prompt_tokens: usize,
6080 replayed_output_tokens: usize,
6081 work: ResourceWorkShape,
6082 ) -> Result<VNextPrefillProbeResolution<R>> {
6083 let product_prompt_tokens = u64::try_from(product_prompt_tokens).map_err(|_| {
6084 FerrumError::request_validation("product prompt token count exceeds u64")
6085 })?;
6086 let replayed_output_tokens = u64::try_from(replayed_output_tokens).map_err(|_| {
6087 FerrumError::request_validation("replayed output token count exceeds u64")
6088 })?;
6089 let identity = RequestIdentity::new(format!(
6090 "request.{}.{request_id}",
6091 request_origin.namespace()
6092 ))
6093 .map_err(|error| FerrumError::internal(error.to_string()))?;
6094 let session = match self.try_admit_sequence(identity.clone(), work)? {
6095 VNextSequenceAdmissionDecision::Admitted(session) => session,
6096 VNextSequenceAdmissionDecision::Deferred(deferred) => {
6097 if deferred.action() == DeferredAction::AwaitBackingGrowth {
6098 return Ok(VNextPrefillProbeResolution::MaintenanceDeferred {
6099 pending: PendingPrefillMaintenance::Logical(deferred),
6100 });
6101 }
6102 return Ok(VNextPrefillProbeResolution::Deferred(deferred));
6103 }
6104 VNextSequenceAdmissionDecision::BackingDeferred(deferred) => {
6105 return Ok(VNextPrefillProbeResolution::MaintenanceDeferred {
6106 pending: PendingPrefillMaintenance::Backing(deferred),
6107 });
6108 }
6109 VNextSequenceAdmissionDecision::PermanentRejected(rejected) => {
6110 return Ok(VNextPrefillProbeResolution::PermanentRejected(rejected));
6111 }
6112 };
6113 let request = match VNextRequestRoot::bind_initial(request_id.clone(), &identity, &session)
6114 {
6115 Ok(request) => request,
6116 Err(error) => {
6117 return Err(terminalize_unsubmitted_session(&session, error));
6118 }
6119 };
6120 let active_binding = match TrustedActiveSequenceBinding::from_session(&session) {
6121 Ok(active_binding) => Arc::new(active_binding),
6122 Err(error) => {
6123 return Err(terminalize_unsubmitted_session(
6124 &session,
6125 FerrumError::backend(error.to_string()),
6126 ));
6127 }
6128 };
6129 let events = match self.execution_journal(&active_binding, request_origin) {
6130 Ok(events) => events,
6131 Err(error) => {
6132 return Err(terminalize_unsubmitted_session(&session, error));
6133 }
6134 };
6135 let sequence = Arc::new(VNextSequence {
6136 prefix_capture_interests: Mutex::new(Vec::new()),
6137 cache_id: format!(
6138 "vnext-cache-{request_id}-{}-{}",
6139 session.sequence_authority().sparse_id(),
6140 session.sequence_authority().generation()
6141 ),
6142 request,
6143 session,
6144 active_binding,
6145 request_origin,
6146 tokens: Mutex::new(tokens),
6147 maximum_tokens,
6148 active: AtomicBool::new(true),
6149 operation: AsyncMutex::new(()),
6150 events: events.map(Mutex::new),
6151 product_prompt_tokens,
6152 replayed_output_tokens,
6153 prefill_tokens_processed: AtomicUsize::new(0),
6154 });
6155
6156 Ok(VNextPrefillProbeResolution::Ready(sequence))
6157 }
6158
6159 fn current_execution_capacity_epochs(&self) -> Result<ExecutorAdmissionEpochs> {
6160 self.plan_resources
6161 .dynamic_pool_status()
6162 .map(|status| ExecutorAdmissionEpochs::from_capacity(status.epochs()))
6163 .map_err(|error| FerrumError::backend(error.to_string()))
6164 }
6165
6166 fn deferred(scope: &str, deferred: &AdmissionDeferred) -> FerrumError {
6167 FerrumError::resource_exhausted(format!(
6168 "vNext {scope} deferred with action {:?} until capacity epoch changes: {:?}",
6169 deferred.action(),
6170 deferred.blockers()
6171 ))
6172 }
6173
6174 fn try_admit_sequence(
6175 &self,
6176 request_id: RequestIdentity,
6177 work: ResourceWorkShape,
6178 ) -> Result<VNextSequenceAdmissionDecision<R>> {
6179 let binding = self
6180 .plan_resources
6181 .trusted_runtime_binding()
6182 .map_err(|error| FerrumError::backend(error.to_string()))?;
6183 let request = RequestResourceAdmissionRequest::new(
6184 work.clone(),
6185 AdmissionFitPolicy::FullInputMustFit,
6186 AdmissionPressureAction::WaitForRelease,
6187 )
6188 .map_err(|error| FerrumError::backend(error.to_string()))?;
6189 let sequence = SequenceResourceAdmissionRequest::new(
6190 work,
6191 self.policy.admission().sequence_fit_policy,
6192 AdmissionPressureAction::WaitForRelease,
6193 )
6194 .map_err(|error| FerrumError::backend(error.to_string()))?;
6195 let mut prefix_maintenance = self.prefix_pressure_maintenance();
6196 let mut prefix_backing_attempts = 0;
6197 let sequence = loop {
6198 match binding
6199 .try_admit_initial_sequence(
6200 request.clone(),
6201 sequence.clone(),
6202 self.run_id.clone(),
6203 request_id.clone(),
6204 )
6205 .map_err(|error| FerrumError::backend(error.to_string()))?
6206 {
6207 InitialSequenceResourceAdmissionDecision::Admitted(sequence) => break sequence,
6208 InitialSequenceResourceAdmissionDecision::Deferred(deferred) => {
6209 match self.recover_prefix_pressure(&mut prefix_maintenance, &deferred)? {
6210 prefix_cache::PrefixPressureRecovery::Maintained(_)
6211 | prefix_cache::PrefixPressureRecovery::Evicted => continue,
6212 prefix_cache::PrefixPressureRecovery::Unchanged => {}
6213 }
6214 self.metrics
6215 .sequence_deferrals
6216 .fetch_add(1, Ordering::Relaxed);
6217 return Ok(VNextSequenceAdmissionDecision::Deferred(deferred));
6218 }
6219 InitialSequenceResourceAdmissionDecision::BackingDeferred(deferred) => {
6220 if prefix_maintenance.allows_backing_attempt(prefix_backing_attempts)
6221 && self.prefix_checkpoint_may_block(deferred.evidence().wait_condition())
6222 {
6223 prefix_backing_attempts += 1;
6224 match deferred
6225 .maintain()
6226 .map_err(|error| FerrumError::backend(error.to_string()))?
6227 {
6228 DynamicDeferredMaintenanceOutcome::Maintained(_)
6229 | DynamicDeferredMaintenanceOutcome::RetryAdmission { .. } => continue,
6230 DynamicDeferredMaintenanceOutcome::WaitForRelease {
6231 wait_condition,
6232 ..
6233 } => {
6234 if prefix_maintenance.eviction_after_wait(self, &wait_condition) {
6235 continue;
6236 }
6237 }
6238 }
6239 }
6240 self.metrics
6241 .backing_deferrals
6242 .fetch_add(1, Ordering::Relaxed);
6243 return Ok(VNextSequenceAdmissionDecision::BackingDeferred(
6244 VNextPrefillBackingDeferral::InitialSequence(deferred),
6245 ));
6246 }
6247 InitialSequenceResourceAdmissionDecision::PermanentRejected(rejected) => {
6248 return Ok(VNextSequenceAdmissionDecision::PermanentRejected(rejected));
6249 }
6250 }
6251 };
6252 let session = sequence
6253 .open_session()
6254 .map_err(|error| FerrumError::backend(error.to_string()))?;
6255 Ok(VNextSequenceAdmissionDecision::Admitted(session))
6256 }
6257
6258 fn execution_journal(
6259 &self,
6260 active: &Arc<TrustedActiveSequenceBinding>,
6261 request_origin: ExecutorRequestOrigin,
6262 ) -> Result<Option<VNextExecutionJournal>> {
6263 let Some(sink) = self.event_sink.read().clone() else {
6264 return Ok(None);
6265 };
6266 VNextExecutionJournal::open(
6267 sink,
6268 self.resolved_plan.execution_plan(),
6269 Arc::clone(active),
6270 request_origin,
6271 )
6272 .map(Some)
6273 .map_err(|error| FerrumError::backend(format!("vNext execution journal: {error}")))
6274 }
6275
6276 fn execution_maintenance_decision<'a>(
6277 &self,
6278 stage: ExecutorExecutionCapacityStage,
6279 outcome: DynamicDeferredMaintenanceOutcome,
6280 source: VNextExecutionMaintenanceSource<'_>,
6281 participants: impl IntoIterator<Item = &'a VNextSequence<R>>,
6282 progress_receipts: &mut Vec<DynamicPoolGrowthBatchReceipt>,
6283 prefix_maintenance: &mut prefix_cache::PrefixPressureMaintenance,
6284 ) -> Result<Option<ExecutorExecutionCapacityDeferral>> {
6285 match outcome {
6286 DynamicDeferredMaintenanceOutcome::RetryAdmission { .. } => Ok(None),
6287 DynamicDeferredMaintenanceOutcome::Maintained(receipt) => {
6288 progress_receipts.push(receipt.clone());
6289 let Some(sink) = self.event_sink.read().clone() else {
6290 return Ok(None);
6291 };
6292 if !sink.records_execution_resource_maintenance() {
6293 return Ok(None);
6294 }
6295 let stage = match stage {
6296 ExecutorExecutionCapacityStage::SequenceExtension => {
6297 ExecutionResourceMaintenanceStage::SequenceExtension
6298 }
6299 ExecutorExecutionCapacityStage::StepAdmission => {
6300 ExecutionResourceMaintenanceStage::StepAdmission
6301 }
6302 ExecutorExecutionCapacityStage::SubmissionWave => {
6303 ExecutionResourceMaintenanceStage::SubmissionWave
6304 }
6305 };
6306 let maintenance = BoundExecutionResourceMaintenance::bind(
6307 stage,
6308 participants
6309 .into_iter()
6310 .map(|sequence| sequence.active_binding.as_ref()),
6311 receipt,
6312 )
6313 .map_err(|error| {
6314 FerrumError::backend(format!(
6315 "vNext execution resource maintenance binding: {error}"
6316 ))
6317 })?;
6318 sink.record_execution_resource_maintenance(maintenance)
6319 .map_err(|error| {
6320 FerrumError::backend(format!(
6321 "vNext execution resource maintenance event: {error}"
6322 ))
6323 })?;
6324 Ok(None)
6325 }
6326 DynamicDeferredMaintenanceOutcome::WaitForRelease {
6327 current_epochs,
6328 wait_condition,
6329 pressure,
6330 maintenance_boundary,
6331 } => {
6332 if prefix_maintenance.eviction_after_wait(self, &wait_condition) {
6333 return Ok(None);
6334 }
6335 match source {
6336 VNextExecutionMaintenanceSource::Logical(source) => {
6337 ExecutorExecutionCapacityDeferral::from_admission_maintenance(
6338 source,
6339 ExecutorAdmissionEpochs::from_capacity(current_epochs),
6340 wait_condition,
6341 pressure,
6342 maintenance_boundary,
6343 stage,
6344 )
6345 }
6346 VNextExecutionMaintenanceSource::Backing(source) => {
6347 ExecutorExecutionCapacityDeferral::from_backing_maintenance(
6348 source,
6349 ExecutorAdmissionEpochs::from_capacity(current_epochs),
6350 wait_condition,
6351 pressure,
6352 maintenance_boundary,
6353 stage,
6354 )
6355 }
6356 }
6357 .map(Some)
6358 }
6359 }
6360 }
6361
6362 fn bind_execution_maintenance_retry(
6363 &self,
6364 deferral: ExecutorExecutionCapacityDeferral,
6365 attempts: u32,
6366 receipts: &[DynamicPoolGrowthBatchReceipt],
6367 affected_request_ids: Vec<RequestId>,
6368 ) -> Result<ExecutorExecutionCapacityDeferral> {
6369 if receipts.is_empty() {
6370 return Ok(deferral);
6371 }
6372 let status = self
6373 .plan_resources
6374 .dynamic_pool_status()
6375 .map_err(|error| FerrumError::backend(error.to_string()))?;
6376 deferral.with_relevant_maintenance_retry(
6377 attempts,
6378 receipts,
6379 status.pools(),
6380 affected_request_ids,
6381 )
6382 }
6383
6384 fn execution_capacity_error(deferral: &ExecutorExecutionCapacityDeferral) -> FerrumError {
6385 FerrumError::resource_exhausted(format!(
6386 "vNext {:?} is waiting for an exact capacity source change: {:?}",
6387 deferral.stage(),
6388 deferral.wait_condition().observed()
6389 ))
6390 }
6391
6392 fn execution_deferral_error(deferral: &ExecutorExecutionDeferral) -> FerrumError {
6393 match deferral {
6394 ExecutorExecutionDeferral::Capacity(deferral) => {
6395 Self::execution_capacity_error(deferral)
6396 }
6397 ExecutorExecutionDeferral::RequestState(deferral) => {
6398 FerrumError::resource_exhausted(format!(
6399 "vNext {:?} is waiting for Request-state hazards: {:?}",
6400 deferral.stage(),
6401 deferral.hazard().blockers()
6402 ))
6403 }
6404 }
6405 }
6406
6407 fn extend_sequence_with_capacity(
6408 &self,
6409 sequence: &VNextSequence<R>,
6410 target: ResourceWorkShape,
6411 ) -> Result<VNextExecutionCapacityDecision<()>> {
6412 let mut prefix_maintenance = self.prefix_pressure_maintenance();
6413 let mut backing_attempts = 0;
6414 let mut maintenance_receipts = Vec::new();
6415 let mut rechecks = 0;
6416 loop {
6417 if !sequence.active.load(Ordering::Acquire) {
6418 return Err(FerrumError::cancelled(
6419 "vNext sequence was released while awaiting capacity",
6420 ));
6421 }
6422 let request = SequenceResourceExtensionRequest::new(
6423 target.clone(),
6424 AdmissionPressureAction::WaitForRelease,
6425 )
6426 .map_err(|error| FerrumError::backend(error.to_string()))?;
6427 match sequence
6428 .session
6429 .try_ensure_backing_covers(request)
6430 .map_err(|error| FerrumError::backend(error.to_string()))?
6431 {
6432 SequenceResourceExtensionDecision::Current(_)
6433 | SequenceResourceExtensionDecision::Extended(_) => {
6434 return Ok(VNextExecutionCapacityDecision::Ready(()))
6435 }
6436 SequenceResourceExtensionDecision::RetryRequired(_) => {
6437 if rechecks >= MAX_EXTENSION_RECHECKS {
6438 return Err(FerrumError::resource_exhausted(
6439 "vNext sequence extension is waiting for the prior frame to retire",
6440 ));
6441 }
6442 rechecks += 1;
6443 std::thread::yield_now();
6444 }
6445 SequenceResourceExtensionDecision::Deferred(deferred) => {
6446 match self.recover_prefix_pressure(&mut prefix_maintenance, &deferred)? {
6447 prefix_cache::PrefixPressureRecovery::Maintained(receipt) => {
6448 if !receipt.growths().is_empty() {
6449 let _ = self.execution_maintenance_decision(
6450 ExecutorExecutionCapacityStage::SequenceExtension,
6451 DynamicDeferredMaintenanceOutcome::Maintained(receipt),
6452 VNextExecutionMaintenanceSource::Logical(&deferred),
6453 std::iter::once(sequence),
6454 &mut maintenance_receipts,
6455 &mut prefix_maintenance,
6456 )?;
6457 }
6458 continue;
6459 }
6460 prefix_cache::PrefixPressureRecovery::Evicted => continue,
6461 prefix_cache::PrefixPressureRecovery::Unchanged => {}
6462 }
6463 self.metrics
6464 .extension_deferrals
6465 .fetch_add(1, Ordering::Relaxed);
6466 if deferred.action() == DeferredAction::WaitForRelease {
6467 return ExecutorExecutionCapacityDeferral::from_admission(
6468 &deferred,
6469 ExecutorExecutionCapacityStage::SequenceExtension,
6470 )
6471 .map(VNextExecutionCapacityDecision::Deferred);
6472 }
6473 if deferred.action() != DeferredAction::AwaitBackingGrowth {
6474 return Err(Self::deferred("sequence extension", &deferred));
6475 }
6476 if !prefix_maintenance.allows_backing_attempt(backing_attempts) {
6477 let deferral = ExecutorExecutionCapacityDeferral::from_pending_maintenance(
6478 &deferred,
6479 ExecutorExecutionCapacityStage::SequenceExtension,
6480 )?;
6481 return self
6482 .bind_execution_maintenance_retry(
6483 deferral,
6484 backing_attempts,
6485 &maintenance_receipts,
6486 vec![sequence.request_id().clone()],
6487 )
6488 .map(VNextExecutionCapacityDecision::Deferred);
6489 }
6490 backing_attempts += 1;
6491 let outcome = self
6492 .plan_resources
6493 .maintain_for_admission_deferred(&deferred)
6494 .map_err(|error| FerrumError::backend(error.to_string()))?;
6495 if let Some(deferred) = self.execution_maintenance_decision(
6496 ExecutorExecutionCapacityStage::SequenceExtension,
6497 outcome,
6498 VNextExecutionMaintenanceSource::Logical(&deferred),
6499 std::iter::once(sequence),
6500 &mut maintenance_receipts,
6501 &mut prefix_maintenance,
6502 )? {
6503 return Ok(VNextExecutionCapacityDecision::Deferred(deferred));
6504 }
6505 }
6506 SequenceResourceExtensionDecision::BackingDeferred(deferred) => {
6507 self.metrics
6508 .backing_deferrals
6509 .fetch_add(1, Ordering::Relaxed);
6510 if !prefix_maintenance.allows_backing_attempt(backing_attempts) {
6511 let deferral = ExecutorExecutionCapacityDeferral::from_backing(
6512 deferred.evidence(),
6513 ExecutorExecutionCapacityStage::SequenceExtension,
6514 )?;
6515 return self
6516 .bind_execution_maintenance_retry(
6517 deferral,
6518 backing_attempts,
6519 &maintenance_receipts,
6520 vec![sequence.request_id().clone()],
6521 )
6522 .map(VNextExecutionCapacityDecision::Deferred);
6523 }
6524 backing_attempts += 1;
6525 let outcome = deferred
6526 .maintain()
6527 .map_err(|error| FerrumError::backend(error.to_string()))?;
6528 if let Some(deferred) = self.execution_maintenance_decision(
6529 ExecutorExecutionCapacityStage::SequenceExtension,
6530 outcome,
6531 VNextExecutionMaintenanceSource::Backing(deferred.evidence()),
6532 std::iter::once(sequence),
6533 &mut maintenance_receipts,
6534 &mut prefix_maintenance,
6535 )? {
6536 return Ok(VNextExecutionCapacityDecision::Deferred(deferred));
6537 }
6538 }
6539 SequenceResourceExtensionDecision::PermanentRejected(rejected) => {
6540 return Err(FerrumError::request_validation(format!(
6541 "vNext sequence extension exceeds the configured fit ceiling: {rejected:?}"
6542 )))
6543 }
6544 }
6545 }
6546 }
6547
6548 fn extend_sequence(
6549 &self,
6550 sequence: &VNextSequence<R>,
6551 target: ResourceWorkShape,
6552 ) -> Result<()> {
6553 match self.extend_sequence_with_capacity(sequence, target)? {
6554 VNextExecutionCapacityDecision::Ready(()) => Ok(()),
6555 VNextExecutionCapacityDecision::Deferred(deferred) => {
6556 Err(Self::execution_capacity_error(&deferred))
6557 }
6558 VNextExecutionCapacityDecision::RequestStateDeferred(_) => Err(FerrumError::internal(
6559 "sequence extension unexpectedly produced a Request-state deferral",
6560 )),
6561 }
6562 }
6563
6564 fn try_begin_step_once(
6565 &self,
6566 batch: &ExecutionBatchParticipants<R>,
6567 span: &TokenSpanWork,
6568 ) -> Result<StepResourceAdmissionDecision<R>> {
6569 self.try_begin_step_for_spans(
6570 batch,
6571 std::slice::from_ref(span),
6572 VNextExecutionWaveKind::Decode,
6573 )
6574 }
6575
6576 fn try_begin_step_for_spans(
6577 &self,
6578 batch: &ExecutionBatchParticipants<R>,
6579 spans: &[TokenSpanWork],
6580 kind: VNextExecutionWaveKind,
6581 ) -> Result<StepResourceAdmissionDecision<R>> {
6582 let timing_enabled = self.host_dispatch_timing_enabled();
6583 let phase_timing = self.metrics.wave_timing_for(kind);
6584 let request = {
6585 let _timing = self
6586 .metrics
6587 .wave_timing
6588 .resource_step_request_prepare
6589 .start_if(timing_enabled);
6590 let _phase_timing = phase_timing
6591 .resource_step_request_prepare
6592 .start_if(timing_enabled);
6593 let work_shape = Arc::new(
6594 batch
6595 .bind_work_shape(spans.to_vec())
6596 .map_err(|error| FerrumError::backend(error.to_string()))?,
6597 );
6598 let reusable_bucket_id = self
6599 .resolved_plan
6600 .execution_plan()
6601 .payload()
6602 .memory()
6603 .reusable_execution()
6604 .and_then(|plan| {
6605 plan.buckets().iter().find(|resolved| {
6606 let bucket = resolved.bucket();
6607 bucket.class_id().as_str() == kind.reusable_execution_class()
6608 && bucket.capacity().covers(
6609 work_shape.immediate_sequences(),
6610 work_shape.immediate_tokens(),
6611 work_shape.immediate_pages(),
6612 )
6613 })
6614 })
6615 .map(|resolved| resolved.bucket().bucket_id().clone());
6616 let request = StepResourceAdmissionRequest::new(
6617 work_shape,
6618 AdmissionFitPolicy::ImmediateOnly,
6619 AdmissionPressureAction::WaitForRelease,
6620 )
6621 .map_err(|error| FerrumError::backend(error.to_string()))?;
6622 match reusable_bucket_id {
6623 Some(bucket_id) => request.with_reusable_execution_bucket(bucket_id),
6624 None => request,
6625 }
6626 };
6627 {
6628 let _timing = self
6629 .metrics
6630 .wave_timing
6631 .resource_step_admission
6632 .start_if(timing_enabled);
6633 let _phase_timing = phase_timing
6634 .resource_step_admission
6635 .start_if(timing_enabled);
6636 let decision = if timing_enabled {
6637 batch.try_begin_step_profiled(request, &self.lane, |phase, duration| {
6638 self.metrics
6639 .wave_timing
6640 .resource_step_admission_breakdown
6641 .record(phase, duration);
6642 phase_timing
6643 .resource_step_admission_breakdown
6644 .record(phase, duration);
6645 })
6646 } else {
6647 batch.try_begin_step(request, &self.lane)
6648 };
6649 decision.map_err(|error| FerrumError::backend(error.to_string()))
6650 }
6651 }
6652
6653 fn begin_step(
6654 &self,
6655 batch: &ExecutionBatchParticipants<R>,
6656 sequence: &Arc<VNextSequence<R>>,
6657 span: &TokenSpanWork,
6658 ) -> Result<Arc<StepResourceLease<R>>> {
6659 self.begin_step_for_spans(
6660 batch,
6661 std::slice::from_ref(sequence),
6662 std::slice::from_ref(span),
6663 )
6664 }
6665
6666 fn begin_step_for_spans(
6667 &self,
6668 batch: &ExecutionBatchParticipants<R>,
6669 sequences: &[Arc<VNextSequence<R>>],
6670 spans: &[TokenSpanWork],
6671 ) -> Result<Arc<StepResourceLease<R>>> {
6672 match self.begin_step_for_spans_with_capacity(
6673 batch,
6674 sequences,
6675 spans,
6676 VNextExecutionWaveKind::Decode,
6677 )? {
6678 VNextExecutionCapacityDecision::Ready(step) => Ok(step),
6679 VNextExecutionCapacityDecision::Deferred(deferred) => {
6680 Err(Self::execution_capacity_error(&deferred))
6681 }
6682 VNextExecutionCapacityDecision::RequestStateDeferred(_) => Err(FerrumError::internal(
6683 "step admission unexpectedly produced a Request-state deferral",
6684 )),
6685 }
6686 }
6687
6688 fn begin_step_for_spans_with_capacity(
6689 &self,
6690 batch: &ExecutionBatchParticipants<R>,
6691 sequences: &[Arc<VNextSequence<R>>],
6692 spans: &[TokenSpanWork],
6693 kind: VNextExecutionWaveKind,
6694 ) -> Result<VNextExecutionCapacityDecision<Arc<StepResourceLease<R>>>> {
6695 if sequences.is_empty()
6696 || sequences.len() != batch.sessions().len()
6697 || batch
6698 .sessions()
6699 .iter()
6700 .zip(sequences)
6701 .any(|(session, sequence)| !Arc::ptr_eq(session, &sequence.session))
6702 {
6703 return Err(FerrumError::internal(
6704 "vNext step maintenance participants differ from the canonical batch",
6705 ));
6706 }
6707 let mut prefix_maintenance = self.prefix_pressure_maintenance();
6708 let mut backing_attempts = 0;
6709 let mut maintenance_receipts = Vec::new();
6710 loop {
6711 match self.try_begin_step_for_spans(batch, spans, kind)? {
6712 StepResourceAdmissionDecision::Admitted(step) => {
6713 return Ok(VNextExecutionCapacityDecision::Ready(step))
6714 }
6715 StepResourceAdmissionDecision::Deferred(deferred) => {
6716 self.metrics.step_deferrals.fetch_add(1, Ordering::Relaxed);
6717 if deferred.action() == DeferredAction::WaitForRelease {
6718 return ExecutorExecutionCapacityDeferral::from_admission(
6719 &deferred,
6720 ExecutorExecutionCapacityStage::StepAdmission,
6721 )
6722 .map(VNextExecutionCapacityDecision::Deferred);
6723 }
6724 if deferred.action() != DeferredAction::AwaitBackingGrowth {
6725 return Err(Self::deferred("step admission", &deferred));
6726 }
6727 if !prefix_maintenance.allows_backing_attempt(backing_attempts) {
6728 let deferral = ExecutorExecutionCapacityDeferral::from_pending_maintenance(
6729 &deferred,
6730 ExecutorExecutionCapacityStage::StepAdmission,
6731 )?;
6732 return self
6733 .bind_execution_maintenance_retry(
6734 deferral,
6735 backing_attempts,
6736 &maintenance_receipts,
6737 sequences
6738 .iter()
6739 .map(|sequence| sequence.request_id().clone())
6740 .collect(),
6741 )
6742 .map(VNextExecutionCapacityDecision::Deferred);
6743 }
6744 backing_attempts += 1;
6745 let outcome = self
6746 .plan_resources
6747 .maintain_for_admission_deferred(&deferred)
6748 .map_err(|error| FerrumError::backend(error.to_string()))?;
6749 if let Some(deferred) = self.execution_maintenance_decision(
6750 ExecutorExecutionCapacityStage::StepAdmission,
6751 outcome,
6752 VNextExecutionMaintenanceSource::Logical(&deferred),
6753 sequences.iter().map(Arc::as_ref),
6754 &mut maintenance_receipts,
6755 &mut prefix_maintenance,
6756 )? {
6757 return Ok(VNextExecutionCapacityDecision::Deferred(deferred));
6758 }
6759 }
6760 StepResourceAdmissionDecision::BackingDeferred(deferred) => {
6761 self.metrics
6762 .backing_deferrals
6763 .fetch_add(1, Ordering::Relaxed);
6764 if !prefix_maintenance.allows_backing_attempt(backing_attempts) {
6765 let deferral = ExecutorExecutionCapacityDeferral::from_backing(
6766 deferred.evidence(),
6767 ExecutorExecutionCapacityStage::StepAdmission,
6768 )?;
6769 return self
6770 .bind_execution_maintenance_retry(
6771 deferral,
6772 backing_attempts,
6773 &maintenance_receipts,
6774 sequences
6775 .iter()
6776 .map(|sequence| sequence.request_id().clone())
6777 .collect(),
6778 )
6779 .map(VNextExecutionCapacityDecision::Deferred);
6780 }
6781 backing_attempts += 1;
6782 let outcome = deferred
6783 .maintain()
6784 .map_err(|error| FerrumError::backend(error.to_string()))?;
6785 if let Some(deferred) = self.execution_maintenance_decision(
6786 ExecutorExecutionCapacityStage::StepAdmission,
6787 outcome,
6788 VNextExecutionMaintenanceSource::Backing(deferred.evidence()),
6789 sequences.iter().map(Arc::as_ref),
6790 &mut maintenance_receipts,
6791 &mut prefix_maintenance,
6792 )? {
6793 return Ok(VNextExecutionCapacityDecision::Deferred(deferred));
6794 }
6795 }
6796 StepResourceAdmissionDecision::PermanentRejected(rejected) => {
6797 return Err(FerrumError::backend(format!(
6798 "vNext execution step exceeds its immutable plan: {rejected:?}"
6799 )))
6800 }
6801 }
6802 }
6803 }
6804
6805 fn try_prepare_wave_once(
6806 &self,
6807 step: &Arc<StepResourceLease<R>>,
6808 span: &TokenSpanWork,
6809 ) -> Result<StepSubmissionWaveAdmissionDecision<R>> {
6810 self.try_prepare_wave_for_spans(step, std::slice::from_ref(span))
6811 }
6812
6813 fn try_prepare_wave_for_spans(
6814 &self,
6815 step: &Arc<StepResourceLease<R>>,
6816 spans: &[TokenSpanWork],
6817 ) -> Result<StepSubmissionWaveAdmissionDecision<R>> {
6818 let work_shape = step
6819 .shared_all_invocation_work_shape(spans)
6820 .map_err(|error| FerrumError::backend(error.to_string()))?;
6821 step.try_prepare_full_plan_submission_wave(
6822 work_shape,
6823 AdmissionFitPolicy::ImmediateOnly,
6824 AdmissionPressureAction::WaitForRelease,
6825 )
6826 .map_err(|error| FerrumError::backend(error.to_string()))
6827 }
6828
6829 fn validate_step_maintenance_participants(
6830 step: &StepResourceLease<R>,
6831 sequences: &[Arc<VNextSequence<R>>],
6832 ) -> Result<()> {
6833 if sequences.is_empty()
6834 || step.participant_count() as usize != sequences.len()
6835 || step
6836 .participants()
6837 .zip(sequences)
6838 .any(|(resources, sequence)| !Arc::ptr_eq(resources, sequence.session.resources()))
6839 {
6840 return Err(FerrumError::internal(
6841 "vNext submission-wave maintenance participants differ from the exact step",
6842 ));
6843 }
6844 Ok(())
6845 }
6846
6847 fn prepare_wave(
6848 &self,
6849 step: &Arc<StepResourceLease<R>>,
6850 sequence: &Arc<VNextSequence<R>>,
6851 span: &TokenSpanWork,
6852 ) -> Result<PreparedStepSubmissionWave<R>> {
6853 self.prepare_wave_for_spans(
6854 step,
6855 std::slice::from_ref(sequence),
6856 std::slice::from_ref(span),
6857 )
6858 }
6859
6860 fn prepare_wave_for_spans(
6861 &self,
6862 step: &Arc<StepResourceLease<R>>,
6863 sequences: &[Arc<VNextSequence<R>>],
6864 spans: &[TokenSpanWork],
6865 ) -> Result<PreparedStepSubmissionWave<R>> {
6866 match self.prepare_wave_for_spans_with_capacity(
6867 step,
6868 sequences,
6869 spans,
6870 VNextExecutionWaveKind::Decode,
6871 )? {
6872 VNextExecutionCapacityDecision::Ready(wave) => Ok(wave),
6873 VNextExecutionCapacityDecision::Deferred(deferred) => {
6874 Err(Self::execution_capacity_error(&deferred))
6875 }
6876 VNextExecutionCapacityDecision::RequestStateDeferred(deferred) => {
6877 Err(FerrumError::resource_exhausted(format!(
6878 "vNext synchronous submission wave is waiting for Request-state hazards: {:?}",
6879 deferred.hazard().blockers()
6880 )))
6881 }
6882 }
6883 }
6884
6885 fn prepare_wave_for_spans_with_capacity(
6886 &self,
6887 step: &Arc<StepResourceLease<R>>,
6888 sequences: &[Arc<VNextSequence<R>>],
6889 spans: &[TokenSpanWork],
6890 kind: VNextExecutionWaveKind,
6891 ) -> Result<VNextExecutionCapacityDecision<PreparedStepSubmissionWave<R>>> {
6892 if sequences.len() != spans.len() {
6893 return Err(FerrumError::internal(
6894 "vNext submission-wave maintenance participants differ from the work spans",
6895 ));
6896 }
6897 Self::validate_step_maintenance_participants(step, sequences)?;
6898 let timing_enabled = self.host_dispatch_timing_enabled();
6899 let phase_timing = self.metrics.wave_timing_for(kind);
6900 let _timing = self
6901 .metrics
6902 .wave_timing
6903 .resource_submission_wave_prepare
6904 .start_if(timing_enabled);
6905 let _phase_timing = phase_timing
6906 .resource_submission_wave_prepare
6907 .start_if(timing_enabled);
6908 let mut prefix_maintenance = self.prefix_pressure_maintenance();
6909 let mut backing_attempts = 0;
6910 let mut maintenance_receipts = Vec::new();
6911 loop {
6912 match self.try_prepare_wave_for_spans(step, spans)? {
6913 StepSubmissionWaveAdmissionDecision::Prepared(wave) => {
6914 self.metrics.prepared_wave_topology.record(&wave);
6915 return Ok(VNextExecutionCapacityDecision::Ready(wave));
6916 }
6917 StepSubmissionWaveAdmissionDecision::Deferred(deferred) => {
6918 self.metrics.wave_deferrals.fetch_add(1, Ordering::Relaxed);
6919 if deferred.action() == DeferredAction::WaitForRelease {
6920 return ExecutorExecutionCapacityDeferral::from_admission(
6921 &deferred,
6922 ExecutorExecutionCapacityStage::SubmissionWave,
6923 )
6924 .map(VNextExecutionCapacityDecision::Deferred);
6925 }
6926 if deferred.action() != DeferredAction::AwaitBackingGrowth {
6927 return Err(Self::deferred("submission wave", &deferred));
6928 }
6929 if !prefix_maintenance.allows_backing_attempt(backing_attempts) {
6930 let deferral = ExecutorExecutionCapacityDeferral::from_pending_maintenance(
6931 &deferred,
6932 ExecutorExecutionCapacityStage::SubmissionWave,
6933 )?;
6934 return self
6935 .bind_execution_maintenance_retry(
6936 deferral,
6937 backing_attempts,
6938 &maintenance_receipts,
6939 sequences
6940 .iter()
6941 .map(|sequence| sequence.request_id().clone())
6942 .collect(),
6943 )
6944 .map(VNextExecutionCapacityDecision::Deferred);
6945 }
6946 backing_attempts += 1;
6947 let outcome = self
6948 .plan_resources
6949 .maintain_for_admission_deferred(&deferred)
6950 .map_err(|error| FerrumError::backend(error.to_string()))?;
6951 if let Some(deferred) = self.execution_maintenance_decision(
6952 ExecutorExecutionCapacityStage::SubmissionWave,
6953 outcome,
6954 VNextExecutionMaintenanceSource::Logical(&deferred),
6955 sequences.iter().map(Arc::as_ref),
6956 &mut maintenance_receipts,
6957 &mut prefix_maintenance,
6958 )? {
6959 return Ok(VNextExecutionCapacityDecision::Deferred(deferred));
6960 }
6961 }
6962 StepSubmissionWaveAdmissionDecision::BackingDeferred(deferred) => {
6963 self.metrics
6964 .backing_deferrals
6965 .fetch_add(1, Ordering::Relaxed);
6966 if !prefix_maintenance.allows_backing_attempt(backing_attempts) {
6967 let deferral = ExecutorExecutionCapacityDeferral::from_backing(
6968 deferred.evidence(),
6969 ExecutorExecutionCapacityStage::SubmissionWave,
6970 )?;
6971 return self
6972 .bind_execution_maintenance_retry(
6973 deferral,
6974 backing_attempts,
6975 &maintenance_receipts,
6976 sequences
6977 .iter()
6978 .map(|sequence| sequence.request_id().clone())
6979 .collect(),
6980 )
6981 .map(VNextExecutionCapacityDecision::Deferred);
6982 }
6983 backing_attempts += 1;
6984 let outcome = deferred
6985 .maintain()
6986 .map_err(|error| FerrumError::backend(error.to_string()))?;
6987 if let Some(deferred) = self.execution_maintenance_decision(
6988 ExecutorExecutionCapacityStage::SubmissionWave,
6989 outcome,
6990 VNextExecutionMaintenanceSource::Backing(deferred.evidence()),
6991 sequences.iter().map(Arc::as_ref),
6992 &mut maintenance_receipts,
6993 &mut prefix_maintenance,
6994 )? {
6995 return Ok(VNextExecutionCapacityDecision::Deferred(deferred));
6996 }
6997 }
6998 StepSubmissionWaveAdmissionDecision::PermanentRejected(rejected) => {
6999 return Err(FerrumError::backend(format!(
7000 "vNext submission wave exceeds its immutable plan: {rejected:?}"
7001 )))
7002 }
7003 StepSubmissionWaveAdmissionDecision::RequestStateDeferred(deferred) => {
7004 let mut request_ids = Vec::new();
7005 for sequence in sequences {
7006 let request_authority = sequence.session.resources().request_authority();
7007 if deferred
7008 .blockers()
7009 .iter()
7010 .any(|blocker| blocker.request() == request_authority)
7011 && !request_ids.contains(sequence.request_id())
7012 {
7013 request_ids.push(sequence.request_id().clone());
7014 }
7015 }
7016 return ExecutorRequestStateDeferral::new(
7017 ExecutorExecutionCapacityStage::SubmissionWave,
7018 request_ids,
7019 deferred,
7020 )
7021 .map(VNextExecutionCapacityDecision::RequestStateDeferred);
7022 }
7023 StepSubmissionWaveAdmissionDecision::RequestStateSplitRequired(split) => {
7024 return Err(FerrumError::internal(format!(
7025 "vNext product executor reached an unsplit sibling Request-state wave for request {:?}: {:?}",
7026 split.request(),
7027 split.resource_ids()
7028 )))
7029 }
7030 StepSubmissionWaveAdmissionDecision::RequestStatePoisoned(poison) => {
7031 return Err(FerrumError::backend(format!(
7032 "vNext Request-state resource is poisoned: {poison:?}"
7033 )))
7034 }
7035 }
7036 }
7037 }
7038
7039 fn product_output_mode(
7040 participants: &[VNextExecutionParticipant<'_, R>],
7041 kind: VNextExecutionWaveKind,
7042 ) -> VNextProductOutputMode {
7043 product_output_mode_for_policies(
7044 kind,
7045 participants
7046 .iter()
7047 .map(|participant| participant.logits_policy),
7048 )
7049 }
7050
7051 fn startup_reusable_descriptor(
7052 participants: &[VNextExecutionParticipant<'_, R>],
7053 kind: VNextExecutionWaveKind,
7054 ) -> Option<VNextReusableExecutionDescriptor> {
7055 match kind {
7056 VNextExecutionWaveKind::Decode
7057 if !participants.is_empty()
7058 && participants
7059 .iter()
7060 .all(|participant| participant.span.immediate_tokens() == 1) =>
7061 {
7062 Some(VNextReusableExecutionDescriptor::uniform_decode(
7063 participants.len(),
7064 ))
7065 }
7066 VNextExecutionWaveKind::Prefill if participants.len() == 1 => {
7067 let participant = &participants[0];
7068 let range = participant.span.immediate_token_range();
7069 Some(VNextReusableExecutionDescriptor::Prefill {
7070 tokens_processed: usize::try_from(range.start).ok()?,
7071 token_capacity: usize::try_from(range.end.checked_sub(range.start)?).ok()?,
7072 total_prompt_tokens: participant.tokens.len(),
7073 request_capacity: 1,
7074 })
7075 }
7076 VNextExecutionWaveKind::Prefill | VNextExecutionWaveKind::Decode => None,
7077 }
7078 }
7079
7080 fn record_startup_reusable_program(
7081 &self,
7082 participants: &[VNextExecutionParticipant<'_, R>],
7083 kind: VNextExecutionWaveKind,
7084 program_id: &DeviceReusableExecutionProgramId,
7085 ) {
7086 if let Some(descriptor) = Self::startup_reusable_descriptor(participants, kind) {
7087 self.startup_reusable_programs
7088 .lock()
7089 .entry(descriptor)
7090 .or_default()
7091 .insert(program_id.clone());
7092 }
7093 }
7094
7095 fn dispatch_participant_wave(
7096 &self,
7097 participants: &[VNextExecutionParticipant<'_, R>],
7098 wave: PreparedStepSubmissionWave<R>,
7099 kind: VNextExecutionWaveKind,
7100 output_mode: VNextProductOutputMode,
7101 token_mask_plans: &[VNextProductTokenMaskSubmissionPlan],
7102 ) -> DispatchOutcome<R> {
7103 if participants.is_empty() || participants.len() != token_mask_plans.len() {
7104 return DispatchOutcome::QuiescentFailure(
7105 "vNext submission wave requires matching participants and token-mask decisions"
7106 .to_owned(),
7107 );
7108 }
7109 let active_bindings = || {
7110 participants
7111 .iter()
7112 .map(|participant| participant.sequence.active_binding.as_ref())
7113 };
7114 let timing_enabled = self.host_dispatch_timing_enabled();
7115 let phase_timing = self.metrics.wave_timing_for(kind);
7116 let mut uploads = match {
7117 let _timing = self
7118 .metrics
7119 .wave_timing
7120 .token_upload_prepare
7121 .start_if(timing_enabled);
7122 let _phase_timing = phase_timing.token_upload_prepare.start_if(timing_enabled);
7123 participants
7124 .iter()
7125 .enumerate()
7126 .map(|(participant_index, participant)| {
7127 let range = participant.span.immediate_token_range();
7128 let host_range = Range {
7129 start: usize::try_from(range.start).map_err(|_| {
7130 FerrumError::backend(
7131 "vNext token upload start exceeds host address space",
7132 )
7133 })?,
7134 end: usize::try_from(range.end).map_err(|_| {
7135 FerrumError::backend(
7136 "vNext token upload end exceeds host address space",
7137 )
7138 })?,
7139 };
7140 let host_tokens =
7141 participant.tokens.get(host_range.clone()).ok_or_else(|| {
7142 FerrumError::backend(format!(
7143 "vNext token upload range {host_range:?} exceeds host token length {}",
7144 participant.tokens.len()
7145 ))
7146 })?;
7147 let host_bytes = host_tokens
7148 .iter()
7149 .flat_map(|token| token.to_le_bytes())
7150 .collect::<Vec<_>>();
7151 let logical_offset_bytes = range
7152 .start
7153 .checked_mul(ElementType::U32.size_bytes())
7154 .ok_or_else(|| {
7155 FerrumError::backend("vNext token upload offset overflows u64")
7156 })?;
7157 let source_layout = HostTransferLayout::new(
7158 ElementType::U32,
7159 participant.span.immediate_tokens(),
7160 )
7161 .map_err(|error| FerrumError::backend(error.to_string()))?;
7162 let participant_index = u32::try_from(participant_index).map_err(|_| {
7163 FerrumError::backend("vNext token upload participant index exceeds u32")
7164 })?;
7165 SubmissionWaveInputUpload::new(
7166 self.io.input_node_id.clone(),
7167 participant_index,
7168 self.io.input_ordinal,
7169 logical_offset_bytes,
7170 source_layout,
7171 host_bytes,
7172 )
7173 .map_err(|error| FerrumError::backend(error.to_string()))
7174 })
7175 .collect::<Result<Vec<_>>>()
7176 } {
7177 Ok(uploads) => uploads,
7178 Err(error) => return DispatchOutcome::QuiescentFailure(error.to_string()),
7179 };
7180 let token_mask_elements = match u64::try_from(self.io.output_elements) {
7181 Ok(elements) => elements,
7182 Err(_) => {
7183 return DispatchOutcome::QuiescentFailure(
7184 "vNext token-mask length exceeds u64".to_owned(),
7185 )
7186 }
7187 };
7188 let token_mask_layout = match HostTransferLayout::new(ElementType::U8, token_mask_elements)
7189 {
7190 Ok(layout) => layout,
7191 Err(error) => return DispatchOutcome::QuiescentFailure(error.to_string()),
7192 };
7193 let token_mask_uploads = participants
7194 .iter()
7195 .zip(token_mask_plans)
7196 .enumerate()
7197 .map(|(participant_index, (_, plan))| {
7198 if !plan.upload_required {
7199 return Ok(None);
7200 }
7201 SubmissionWaveInputUpload::new(
7202 self.io.token_mask_input_node_id.clone(),
7203 u32::try_from(participant_index).map_err(|_| {
7204 FerrumError::backend(
7205 "vNext token-mask upload participant index exceeds u32",
7206 )
7207 })?,
7208 self.io.token_mask_input_ordinal,
7209 0,
7210 token_mask_layout,
7211 plan.content.normalized(),
7212 )
7213 .map(Some)
7214 .map_err(|error| FerrumError::backend(error.to_string()))
7215 })
7216 .collect::<Result<Vec<_>>>();
7217 match token_mask_uploads {
7218 Ok(token_mask_uploads) => uploads.extend(token_mask_uploads.into_iter().flatten()),
7219 Err(error) => return DispatchOutcome::QuiescentFailure(error.to_string()),
7220 }
7221 let repetition_uploads = (|| -> Result<_> {
7222 let repetition_capacity = u64::try_from(self.io.repetition_capacity)
7223 .map_err(|_| FerrumError::backend("vNext repetition capacity exceeds u64"))?;
7224 let repetition_token_id_layout =
7225 HostTransferLayout::new(ElementType::U32, repetition_capacity)
7226 .map_err(|error| FerrumError::backend(error.to_string()))?;
7227 let repetition_offset_layout = HostTransferLayout::new(ElementType::U32, 2)
7228 .map_err(|error| FerrumError::backend(error.to_string()))?;
7229 let repetition_penalty_layout = HostTransferLayout::new(ElementType::F32, 1)
7230 .map_err(|error| FerrumError::backend(error.to_string()))?;
7231 let mut token_id_uploads = Vec::with_capacity(participants.len());
7232 let mut offset_uploads = Vec::with_capacity(participants.len());
7233 let mut penalty_uploads = Vec::with_capacity(participants.len());
7234 for (participant_index, participant) in participants.iter().enumerate() {
7235 let repetition = product_repetition_input(participant.logits_policy, output_mode);
7236 if !repetition.penalty.is_finite() || repetition.penalty <= 0.0 {
7237 return Err(FerrumError::backend(
7238 "vNext sparse repetition penalty must be finite and positive",
7239 ));
7240 }
7241 if repetition.token_ids.len() > self.io.repetition_capacity {
7242 return Err(FerrumError::backend(format!(
7243 "vNext sparse repetition input contains {} ids, capacity is {}",
7244 repetition.token_ids.len(),
7245 self.io.repetition_capacity
7246 )));
7247 }
7248 if repetition.token_ids.iter().any(|token| {
7249 usize::try_from(*token).map_or(true, |token| token >= self.io.output_elements)
7250 }) {
7251 return Err(FerrumError::backend(
7252 "vNext sparse repetition input contains an out-of-vocabulary token",
7253 ));
7254 }
7255 let participant_index = u32::try_from(participant_index).map_err(|_| {
7256 FerrumError::backend("vNext repetition participant index exceeds u32")
7257 })?;
7258 let repetition_count = u32::try_from(repetition.token_ids.len()).map_err(|_| {
7259 FerrumError::backend("vNext repetition token count exceeds u32")
7260 })?;
7261 if repetition_count != 0 {
7262 token_id_uploads.push(
7263 SubmissionWaveInputUpload::new(
7264 self.io.repetition_token_ids_input_node_id.clone(),
7265 participant_index,
7266 self.io.repetition_token_ids_input_ordinal,
7267 0,
7268 repetition_token_id_layout,
7269 padded_repetition_token_id_bytes(
7270 repetition.token_ids,
7271 self.io.repetition_capacity,
7272 )?,
7273 )
7274 .map_err(|error| FerrumError::backend(error.to_string()))?,
7275 );
7276 }
7277 offset_uploads.push(
7278 SubmissionWaveInputUpload::new(
7279 self.io.repetition_offsets_input_node_id.clone(),
7280 participant_index,
7281 self.io.repetition_offsets_input_ordinal,
7282 0,
7283 repetition_offset_layout,
7284 [0_u32, repetition_count]
7285 .into_iter()
7286 .flat_map(u32::to_le_bytes)
7287 .collect(),
7288 )
7289 .map_err(|error| FerrumError::backend(error.to_string()))?,
7290 );
7291 penalty_uploads.push(
7292 SubmissionWaveInputUpload::new(
7293 self.io.repetition_penalty_input_node_id.clone(),
7294 participant_index,
7295 self.io.repetition_penalty_input_ordinal,
7296 0,
7297 repetition_penalty_layout,
7298 repetition.penalty.to_le_bytes().to_vec(),
7299 )
7300 .map_err(|error| FerrumError::backend(error.to_string()))?,
7301 );
7302 }
7303 Ok((token_id_uploads, offset_uploads, penalty_uploads))
7304 })();
7305 match repetition_uploads {
7306 Ok((token_id_uploads, offset_uploads, penalty_uploads)) => {
7307 uploads.extend(token_id_uploads);
7308 uploads.extend(offset_uploads);
7309 uploads.extend(penalty_uploads);
7310 }
7311 Err(error) => return DispatchOutcome::QuiescentFailure(error.to_string()),
7312 }
7313 let uploaded_bytes = uploads.iter().fold(0_u64, |total, upload| {
7314 total.saturating_add(upload.source_layout().byte_len().unwrap_or(0))
7315 });
7316 self.metrics
7317 .uploaded_bytes
7318 .fetch_add(uploaded_bytes, Ordering::Relaxed);
7319
7320 let mut wave = wave;
7321 let mut retries = 0;
7322 let mut reusable_direct_attempted = false;
7323 loop {
7324 let identity = match {
7325 let _timing = self
7326 .metrics
7327 .wave_timing
7328 .wave_identity_bind
7329 .start_if(timing_enabled);
7330 let _phase_timing = phase_timing.wave_identity_bind.start_if(timing_enabled);
7331 OperationDispatch::bind_compiled_submission_wave_identity(
7332 &self.submission_wave_identity,
7333 active_bindings(),
7334 &wave,
7335 &self.lane,
7336 )
7337 } {
7338 Ok(identity) => identity,
7339 Err(error) => return DispatchOutcome::QuiescentFailure(error.to_string()),
7340 };
7341 let timing_sink = VNextWaveTimingSink {
7342 aggregate: &self.metrics.wave_timing,
7343 phase: phase_timing,
7344 };
7345 let device_timing_mode = self.device_timing_mode();
7346 let execution_policy = submission_execution_policy_for_timing(device_timing_mode);
7347 let mut reusable_catalog_miss = None;
7348 let catalog_snapshot = self.reusable_execution_catalog.read().clone();
7349 let catalog = catalog_snapshot.as_deref();
7350 let reusable_program = if !reusable_program_identity_required(
7351 self.reusable_execution_startup_plan.is_some(),
7352 catalog.is_some(),
7353 device_timing_mode.direct_reusable_execution_allowed(),
7354 reusable_direct_attempted,
7355 ) {
7356 None
7357 } else {
7358 let program_id = match OperationDispatch::reusable_execution_program_id_for_wave(
7359 self.providers.providers(),
7360 &self.resolved_plan,
7361 &wave,
7362 &self.lane,
7363 ) {
7364 Ok(program_id) => program_id,
7365 Err(error) => {
7366 return DispatchOutcome::QuiescentFailure(error.to_string());
7367 }
7368 };
7369 match program_id {
7370 Some(program_id) => {
7371 if catalog.is_none() {
7372 self.record_startup_reusable_program(participants, kind, &program_id);
7373 }
7374 match catalog {
7375 Some(catalog)
7376 if catalog.lane_epoch != self.lane.reusable_execution_epoch() =>
7377 {
7378 reusable_catalog_miss =
7379 Some(VNextReusableExecutionCatalogMissKey::from_program_id(
7380 &program_id,
7381 VNextReusableExecutionCatalogMissReason::EpochMismatch,
7382 ));
7383 None
7384 }
7385 Some(catalog) if catalog.programs.is_empty() => {
7386 reusable_catalog_miss =
7387 Some(VNextReusableExecutionCatalogMissKey::from_program_id(
7388 &program_id,
7389 VNextReusableExecutionCatalogMissReason::CatalogEmpty,
7390 ));
7391 None
7392 }
7393 Some(catalog) => match catalog.programs.get(&program_id) {
7394 Some(program) if program.has_resident_segments() => Some(program),
7395 Some(_) => {
7396 reusable_catalog_miss = Some(
7397 VNextReusableExecutionCatalogMissKey::from_program_id(
7398 &program_id,
7399 VNextReusableExecutionCatalogMissReason::ProgramNonResident,
7400 ),
7401 );
7402 None
7403 }
7404 None => {
7405 reusable_catalog_miss = Some(
7406 VNextReusableExecutionCatalogMissKey::from_program_id(
7407 &program_id,
7408 VNextReusableExecutionCatalogMissReason::ProgramAbsent,
7409 ),
7410 );
7411 None
7412 }
7413 },
7414 None => None,
7415 }
7416 }
7417 None if catalog.is_some() => {
7418 reusable_catalog_miss = Some(
7419 VNextReusableExecutionCatalogMissKey::without_program_identity(
7420 wave.claimed_backing().work_shape(),
7421 VNextReusableExecutionCatalogMissReason::ProgramIdentityUnavailable,
7422 ),
7423 );
7424 None
7425 }
7426 None => None,
7427 }
7428 };
7429 reusable_direct_attempted |= reusable_program.is_some();
7430 if reusable_program.is_none() && self.on_demand_reusable_execution_enabled() {
7431 self.reusable_execution_catalog_refresh_needed
7432 .store(true, Ordering::Release);
7433 }
7434 let reusable_program_stats = reusable_program.map(|program| {
7435 (
7436 program.segments().len() as u64,
7437 program
7438 .segments()
7439 .iter()
7440 .map(|segment| {
7441 u64::from(segment.end_node_index() - segment.start_node_index())
7442 })
7443 .sum::<u64>(),
7444 program.per_wave_binding_node_indices().len() as u64,
7445 )
7446 });
7447 let submission = {
7448 let _timing = self
7449 .metrics
7450 .wave_timing
7451 .provider_encode_submit
7452 .start_if(timing_enabled);
7453 let _phase_timing = phase_timing.provider_encode_submit.start_if(timing_enabled);
7454 if let Some(reusable_program) = reusable_program {
7455 if timing_enabled {
7456 OperationDispatch::encode_and_submit_reusable_wave_with_inputs_and_timing(
7457 self.providers.providers(),
7458 &self.resolved_plan,
7459 &identity,
7460 active_bindings(),
7461 device_timing_mode,
7462 &uploads,
7463 reusable_program,
7464 execution_policy,
7465 &timing_sink,
7466 wave,
7467 &self.lane,
7468 &self.reaper,
7469 )
7470 .map(ProfiledSubmissionHandle::into_parts)
7471 } else {
7472 OperationDispatch::encode_and_submit_reusable_wave_with_inputs(
7473 self.providers.providers(),
7474 &self.resolved_plan,
7475 &identity,
7476 active_bindings(),
7477 device_timing_mode,
7478 &uploads,
7479 reusable_program,
7480 wave,
7481 &self.lane,
7482 &self.reaper,
7483 )
7484 .map(|completion| (completion, None))
7485 }
7486 } else if timing_enabled {
7487 OperationDispatch::encode_and_submit_wave_with_inputs_and_timing(
7488 self.providers.providers(),
7489 &self.resolved_plan,
7490 &identity,
7491 active_bindings(),
7492 device_timing_mode,
7493 &uploads,
7494 execution_policy,
7495 &timing_sink,
7496 wave,
7497 &self.lane,
7498 &self.reaper,
7499 )
7500 .map(ProfiledSubmissionHandle::into_parts)
7501 } else {
7502 OperationDispatch::encode_and_submit_wave_with_inputs(
7503 self.providers.providers(),
7504 &self.resolved_plan,
7505 &identity,
7506 active_bindings(),
7507 device_timing_mode,
7508 &uploads,
7509 wave,
7510 &self.lane,
7511 &self.reaper,
7512 )
7513 .map(|completion| (completion, None))
7514 }
7515 };
7516 match submission {
7517 Ok((completion, attribution)) => {
7518 let identity_materialization = identity.materialization_snapshot();
7519 self.metrics.submitted_waves.fetch_add(1, Ordering::Relaxed);
7520 self.metrics.identity_waves.fetch_add(1, Ordering::Relaxed);
7521 self.metrics.identity_logical_nodes.fetch_add(
7522 u64::from(identity_materialization.logical_nodes()),
7523 Ordering::Relaxed,
7524 );
7525 self.metrics
7526 .identity_nodes_materialized_before_submit
7527 .fetch_add(
7528 u64::from(identity_materialization.materialized_nodes()),
7529 Ordering::Relaxed,
7530 );
7531 self.metrics
7532 .identity_full_participant_materializations_before_submit
7533 .fetch_add(
7534 u64::from(identity_materialization.full_participant_projection()),
7535 Ordering::Relaxed,
7536 );
7537 if let Some((segments, logical_nodes, binding_nodes)) = reusable_program_stats {
7538 self.metrics
7539 .direct_reusable_waves
7540 .fetch_add(1, Ordering::Relaxed);
7541 self.metrics
7542 .direct_reusable_segments
7543 .fetch_add(segments, Ordering::Relaxed);
7544 self.metrics
7545 .direct_reusable_logical_nodes
7546 .fetch_add(logical_nodes, Ordering::Relaxed);
7547 self.metrics
7548 .direct_reusable_binding_nodes
7549 .fetch_add(binding_nodes, Ordering::Relaxed);
7550 } else if let Some(miss) = reusable_catalog_miss {
7551 self.metrics.record_reusable_catalog_miss(miss);
7552 }
7553 return DispatchOutcome::Submitted {
7554 completion,
7555 attribution,
7556 };
7557 }
7558 Err(SubmissionWaveDispatchError::DefinitelyNotSubmitted { failures, retry })
7559 if retries < MAX_DEFINITELY_NOT_SUBMITTED_RETRIES =>
7560 {
7561 retries += 1;
7562 if reusable_program_stats.is_some() {
7563 self.metrics
7564 .direct_reusable_fallbacks
7565 .fetch_add(1, Ordering::Relaxed);
7566 }
7567 self.metrics
7568 .definitely_not_submitted_retries
7569 .fetch_add(1, Ordering::Relaxed);
7570 match retry.retry() {
7571 Ok(retry_wave) => wave = retry_wave,
7572 Err(error) => {
7573 return DispatchOutcome::QuiescentFailure(format!(
7574 "vNext wave retry authority failed after {failures:?}: {error}"
7575 ))
7576 }
7577 }
7578 }
7579 Err(error @ SubmissionWaveDispatchError::DefinitelyNotSubmitted { .. })
7580 | Err(error @ SubmissionWaveDispatchError::Contract(_))
7581 | Err(error @ SubmissionWaveDispatchError::Provider(_))
7582 | Err(error @ SubmissionWaveDispatchError::Initialization(_))
7583 | Err(error @ SubmissionWaveDispatchError::InputUpload(_)) => {
7584 return DispatchOutcome::QuiescentFailure(error.to_string())
7585 }
7586 Err(SubmissionWaveDispatchError::SubmissionIndeterminate { recovery }) => {
7587 return DispatchOutcome::SubmissionIndeterminate {
7588 message: "vNext wave submission is indeterminate".to_owned(),
7589 recovery,
7590 }
7591 }
7592 Err(SubmissionWaveDispatchError::PostSubmitContract { error, completion }) => {
7593 return DispatchOutcome::PostSubmitContract {
7594 message: error.to_string(),
7595 completion,
7596 }
7597 }
7598 }
7599 }
7600 }
7601
7602 async fn abort_step(
7603 &self,
7604 step: Arc<StepResourceLease<R>>,
7605 message: impl Into<String>,
7606 ) -> FerrumError {
7607 let message = message.into();
7608 self.metrics.record_failure(message.clone());
7609 match step.try_abort() {
7610 Ok(_) => FerrumError::backend(message),
7611 Err(failure) => FerrumError::backend(format!(
7612 "{message}; vNext step abort failed: {}",
7613 failure.error()
7614 )),
7615 }
7616 }
7617
7618 fn abort_unsubmitted_step(
7619 &self,
7620 step: Arc<StepResourceLease<R>>,
7621 error: FerrumError,
7622 ) -> FerrumError {
7623 if !matches!(&error, FerrumError::ResourceExhausted { .. }) {
7624 self.metrics.record_failure(error.to_string());
7625 }
7626 match step.try_abort() {
7627 Ok(_) => error,
7628 Err(failure) => FerrumError::backend(format!(
7629 "{error}; vNext unsubmitted step abort failed: {}",
7630 failure.error()
7631 )),
7632 }
7633 }
7634
7635 fn rollback_unsubmitted_step(
7636 &self,
7637 step: Arc<StepResourceLease<R>>,
7638 context: &'static str,
7639 ) -> Result<()> {
7640 let rollback_failure = match step.try_rollback_unsubmitted() {
7641 Ok(_) => return Ok(()),
7642 Err(failure) => failure,
7643 };
7644 let rollback_error = rollback_failure.error().to_string();
7645 let step = rollback_failure.into_step();
7646 match step.try_abort() {
7647 Ok(_) => {
7648 let message = format!(
7649 "{context} rollback failed: {rollback_error}; exact step was aborted fail-closed"
7650 );
7651 self.metrics.record_failure(message.clone());
7652 Err(FerrumError::backend(message))
7653 }
7654 Err(abort_failure) => {
7655 let abort_error = abort_failure.error().to_string();
7656 let step = abort_failure.into_step();
7657 drop(step);
7658 let message = format!(
7659 "{context} rollback failed: {rollback_error}; explicit abort failed: {abort_error}; exact step authority was released to fail-closed Drop"
7660 );
7661 self.metrics.record_failure(message.clone());
7662 Err(FerrumError::backend(message))
7663 }
7664 }
7665 }
7666
7667 fn checkpoint_token_evidence_enabled(&self) -> bool {
7668 self.prefix_restore_enabled()
7669 }
7670
7671 fn retain_checkpoint_token_evidence(
7672 &self,
7673 span: TokenSpanWork,
7674 tokens: &[u32],
7675 ) -> Result<TokenSpanWork> {
7676 prefix_cache::retain_token_evidence(self.resolved_plan.execution_plan(), span, tokens)
7677 }
7678
7679 async fn execute_step(
7680 &self,
7681 sequence: &Arc<VNextSequence<R>>,
7682 tokens: &[u32],
7683 span: TokenSpanWork,
7684 logits_policy: &LogitsReturnPolicy,
7685 ) -> Result<ExecutorSamplingOutput> {
7686 let span = self.retain_checkpoint_token_evidence(span, tokens)?;
7689 let prepared = {
7690 let _timing = self.metrics.wave_timing.resource_prepare_attempt.start();
7691 let _phase_timing = self
7692 .metrics
7693 .decode_wave_timing
7694 .resource_prepare_attempt
7695 .start();
7696 let batch = ExecutionBatchParticipants::new(vec![Arc::clone(&sequence.session)])
7697 .map_err(|error| FerrumError::backend(error.to_string()))?;
7698 let step = self.begin_step(&batch, sequence, &span)?;
7699 let wave = match self.prepare_wave(&step, sequence, &span) {
7700 Ok(wave) => wave,
7701 Err(error) => return Err(self.abort_unsubmitted_step(step, error)),
7702 };
7703 PreparedVNextPrefill { step, wave }
7704 };
7705 self.execute_prepared_step(
7706 sequence,
7707 tokens,
7708 span,
7709 prepared,
7710 VNextExecutionWaveKind::Decode,
7711 Some(logits_policy),
7712 )
7713 .await
7714 }
7715
7716 async fn execute_batch_step(
7717 &self,
7718 batch: &ExecutionBatchParticipants<R>,
7719 sequences: &[Arc<VNextSequence<R>>],
7720 token_batches: &[Vec<u32>],
7721 spans: &[TokenSpanWork],
7722 kind: VNextExecutionWaveKind,
7723 logits_policies: Option<&[LogitsReturnPolicy]>,
7724 ) -> Result<VNextExecutionCapacityDecision<Vec<ExecutorSamplingOutput>>> {
7725 if sequences.is_empty()
7726 || sequences.len() != token_batches.len()
7727 || sequences.len() != spans.len()
7728 || logits_policies.is_some_and(|policies| policies.len() != sequences.len())
7729 || (kind == VNextExecutionWaveKind::Decode) != logits_policies.is_some()
7730 || sequences.len() != batch.sessions().len()
7731 || batch
7732 .sessions()
7733 .iter()
7734 .zip(sequences)
7735 .any(|(session, sequence)| !Arc::ptr_eq(session, &sequence.session))
7736 {
7737 return Err(FerrumError::internal(
7738 "vNext decode batch differs from its canonical participant set",
7739 ));
7740 }
7741 let retained_spans = if self.checkpoint_token_evidence_enabled() {
7742 Some(
7743 spans
7744 .iter()
7745 .zip(token_batches)
7746 .map(|(span, tokens)| {
7747 self.retain_checkpoint_token_evidence(span.clone(), tokens)
7748 })
7749 .collect::<Result<Vec<_>>>()?,
7750 )
7751 } else {
7752 None
7753 };
7754 let spans = retained_spans.as_deref().unwrap_or(spans);
7757 let prepared = {
7758 let _timing = self.metrics.wave_timing.resource_prepare_attempt.start();
7759 let _phase_timing = self
7760 .metrics
7761 .wave_timing_for(kind)
7762 .resource_prepare_attempt
7763 .start();
7764 let step =
7765 match self.begin_step_for_spans_with_capacity(batch, sequences, spans, kind)? {
7766 VNextExecutionCapacityDecision::Ready(step) => step,
7767 VNextExecutionCapacityDecision::Deferred(deferred) => {
7768 return Ok(VNextExecutionCapacityDecision::Deferred(deferred))
7769 }
7770 VNextExecutionCapacityDecision::RequestStateDeferred(deferred) => {
7771 return Ok(VNextExecutionCapacityDecision::RequestStateDeferred(
7772 deferred,
7773 ))
7774 }
7775 };
7776 let wave_decision =
7777 match self.prepare_wave_for_spans_with_capacity(&step, sequences, spans, kind) {
7778 Ok(decision) => decision,
7779 Err(error) => {
7780 if let Err(cleanup_error) = self
7781 .rollback_unsubmitted_step(step, "vNext failed-wave unsubmitted step")
7782 {
7783 return Err(FerrumError::backend(format!("{error}; {cleanup_error}")));
7784 }
7785 return Err(error);
7786 }
7787 };
7788 let wave = match wave_decision {
7789 VNextExecutionCapacityDecision::Ready(wave) => wave,
7790 VNextExecutionCapacityDecision::Deferred(deferred) => {
7791 self.rollback_unsubmitted_step(
7792 step,
7793 "vNext capacity-deferred unsubmitted step",
7794 )?;
7795 return Ok(VNextExecutionCapacityDecision::Deferred(deferred));
7796 }
7797 VNextExecutionCapacityDecision::RequestStateDeferred(deferred) => {
7798 self.rollback_unsubmitted_step(
7799 step,
7800 "vNext readiness-deferred unsubmitted step",
7801 )?;
7802 return Ok(VNextExecutionCapacityDecision::RequestStateDeferred(
7803 deferred,
7804 ));
7805 }
7806 };
7807 PreparedVNextPrefill { step, wave }
7808 };
7809 let participants = sequences
7810 .iter()
7811 .zip(token_batches)
7812 .zip(spans)
7813 .enumerate()
7814 .map(
7815 |(participant_index, ((sequence, tokens), span))| VNextExecutionParticipant {
7816 sequence,
7817 tokens,
7818 span,
7819 logits_policy: logits_policies.map(|policies| &policies[participant_index]),
7820 },
7821 )
7822 .collect::<Vec<_>>();
7823 self.execute_prepared_participants(&participants, prepared, kind)
7824 .await
7825 .map(VNextExecutionCapacityDecision::Ready)
7826 }
7827
7828 async fn execute_prepared_step(
7829 &self,
7830 sequence: &Arc<VNextSequence<R>>,
7831 tokens: &[u32],
7832 span: TokenSpanWork,
7833 prepared: PreparedVNextPrefill<R>,
7834 kind: VNextExecutionWaveKind,
7835 logits_policy: Option<&LogitsReturnPolicy>,
7836 ) -> Result<ExecutorSamplingOutput> {
7837 let participant = VNextExecutionParticipant {
7838 sequence,
7839 tokens,
7840 span: &span,
7841 logits_policy,
7842 };
7843 let mut logits = self
7844 .execute_prepared_participants(std::slice::from_ref(&participant), prepared, kind)
7845 .await?;
7846 logits
7847 .pop()
7848 .ok_or_else(|| FerrumError::internal("vNext single execution returned no logits"))
7849 }
7850
7851 async fn execute_prepared_participants(
7852 &self,
7853 participants: &[VNextExecutionParticipant<'_, R>],
7854 prepared: PreparedVNextPrefill<R>,
7855 kind: VNextExecutionWaveKind,
7856 ) -> Result<Vec<ExecutorSamplingOutput>> {
7857 let _execution_timing = self.metrics.wave_timing.submitted_wave_total.start();
7858 let phase_timing = self.metrics.wave_timing_for(kind);
7859 let _phase_execution_timing = phase_timing.submitted_wave_total.start();
7860 let PreparedVNextPrefill { step, wave } = prepared;
7861 let capture_claim = match (kind, self.checkpoint_capture.as_ref()) {
7862 (VNextExecutionWaveKind::Prefill, Some(capture)) => {
7863 let first = participants.first();
7864 let is_final_prefill = first.is_some_and(|participant| {
7865 participant.span.immediate_token_range().end
7866 == participant.span.full_input_tokens()
7867 });
7868 capture.claim_prefill_wave(
7869 participants.len(),
7870 first.map(|participant| participant.sequence.request_id()),
7871 first.map(|participant| participant.tokens),
7872 is_final_prefill,
7873 )?
7874 }
7875 (VNextExecutionWaveKind::Decode, Some(capture)) => capture.claim_decode_wave(
7876 participants.len(),
7877 participants
7878 .first()
7879 .map(|participant| participant.sequence.request_id()),
7880 participants.first().map(|participant| participant.tokens),
7881 )?,
7882 (_, None) => None,
7883 };
7884 let teacher_forced_decision = match (capture_claim, self.checkpoint_capture.as_ref()) {
7885 (Some(claim), Some(capture)) => capture.teacher_forced_decision(claim)?,
7886 _ => None,
7887 };
7888 let output_mode = if teacher_forced_decision.is_some() {
7889 VNextProductOutputMode::FullLogits
7890 } else {
7891 Self::product_output_mode(participants, kind)
7892 };
7893 let readbacks =
7894 self.prepare_terminal_readbacks(participants, capture_claim, output_mode)?;
7895 let (mut token_mask_residency, dispatch) = {
7896 let _timing = self.metrics.wave_timing.host_encode_submit.start();
7897 let _phase_timing = phase_timing.host_encode_submit.start();
7898 let token_mask_slot_identity = self
7904 .io
7905 .token_mask_residency_eligible
7906 .then(|| {
7907 wave.step_resources()
7908 .claimed_backing()
7909 .lane_stable_slot_identity()
7910 })
7911 .flatten();
7912 let token_mask_residency = VNextProductTokenMaskResidencyTransaction::prepare(
7913 &self.product_token_mask_residency,
7914 token_mask_slot_identity,
7915 participants.iter().map(|participant| {
7916 VNextProductTokenMaskContent::from_policy(
7917 participant.logits_policy,
7918 output_mode,
7919 self.io.output_elements,
7920 )
7921 }),
7922 );
7923 let dispatch = self.dispatch_participant_wave(
7924 participants,
7925 wave,
7926 kind,
7927 output_mode,
7928 token_mask_residency.plans(),
7929 );
7930 (token_mask_residency, dispatch)
7931 };
7932 let mut execution_event_error = None;
7933 let (completion, attribution) = match dispatch {
7934 DispatchOutcome::Submitted {
7935 completion,
7936 attribution,
7937 } => (completion, attribution),
7938 DispatchOutcome::QuiescentFailure(message) => {
7939 token_mask_residency.invalidate_before_slot_release();
7940 return Err(self.abort_step(step, message).await);
7941 }
7942 DispatchOutcome::SubmissionIndeterminate { message, recovery } => {
7943 let reaper = Arc::clone(&self.reaper);
7944 let recovered = self
7945 .completion_worker
7946 .execute(VNextCompletionTaskKind::IndeterminateRecovery, move || {
7947 let recovered = recovery.recover_by_draining_lane();
7948 drop(reaper);
7949 recovered
7950 })
7951 .await
7952 .map_err(|error| FerrumError::backend(format!("{message}: {error}")))?;
7953 match recovered {
7954 Ok(_) => {
7955 token_mask_residency.invalidate_before_slot_release();
7956 return Err(self.abort_step(step, message).await);
7957 }
7958 Err(error) => {
7959 token_mask_residency.invalidate_before_slot_release();
7960 self.metrics
7961 .record_failure(format!("{message}; recovery failed: {error}"));
7962 return Err(FerrumError::backend(format!(
7963 "{message}; recovery failed: {error}"
7964 )));
7965 }
7966 }
7967 }
7968 DispatchOutcome::PostSubmitContract {
7969 message,
7970 completion,
7971 } => {
7972 let reaper = Arc::clone(&self.reaper);
7973 let observed = self
7974 .completion_worker
7975 .execute(VNextCompletionTaskKind::PostSubmitDrain, move || {
7976 let observed = completion.wait();
7977 drop(reaper);
7978 observed
7979 })
7980 .await
7981 .map_err(|error| FerrumError::backend(format!("{message}: {error}")))?;
7982 match observed {
7983 Ok(CompletionObservation::Terminal(_)) => {
7984 token_mask_residency.invalidate_before_slot_release();
7985 return Err(self.abort_step(step, message).await);
7986 }
7987 Ok(other) => {
7988 token_mask_residency.invalidate_before_slot_release();
7989 self.metrics.record_failure(format!(
7990 "{message}; post-submit drain remained nonterminal: {other:?}"
7991 ));
7992 return Err(FerrumError::backend(format!(
7993 "{message}; post-submit drain remained nonterminal"
7994 )));
7995 }
7996 Err(error) => {
7997 token_mask_residency.invalidate_before_slot_release();
7998 self.metrics
7999 .record_failure(format!("{message}; drain failed: {error}"));
8000 return Err(FerrumError::backend(format!(
8001 "{message}; drain failed: {error}"
8002 )));
8003 }
8004 }
8005 }
8006 };
8007 for participant in participants {
8008 if let Some(events) = &participant.sequence.events {
8009 if let Err(error) = events.lock().submitted(completion.receipt()) {
8010 execution_event_error.get_or_insert_with(|| error.to_string());
8011 }
8012 }
8013 }
8014 let diagnostic_failure_requested =
8015 self.claim_prefill_resource_diagnostic_fault(participants, kind);
8016 let mut diagnostic_failure_observed = false;
8017 if diagnostic_failure_requested {
8018 let observation = self
8019 .plan_runtime_resource_snapshot()
8020 .and_then(|snapshot| {
8021 snapshot.ok_or_else(|| {
8022 FerrumError::internal(
8023 "vNext diagnostic resource failure lacks a plan runtime snapshot",
8024 )
8025 })
8026 })
8027 .and_then(|snapshot| {
8028 participants[0]
8029 .sequence
8030 .events
8031 .as_ref()
8032 .ok_or_else(|| {
8033 FerrumError::internal(
8034 "vNext diagnostic resource failure lacks an execution journal",
8035 )
8036 })?
8037 .lock()
8038 .observe_resource_failure(
8039 snapshot,
8040 "diagnostic_resource_after_submit",
8041 "typed diagnostic resource failure after prefill submission",
8042 )
8043 .map_err(|error| FerrumError::backend(error.to_string()))
8044 });
8045 match observation {
8046 Ok(_) => diagnostic_failure_observed = true,
8047 Err(error) => {
8048 execution_event_error.get_or_insert_with(|| error.to_string());
8049 }
8050 }
8051 }
8052
8053 let reaper = Arc::clone(&self.reaper);
8054 let observation = {
8055 let _timing = self.metrics.wave_timing.completion_round_trip.start();
8056 let _phase_timing = phase_timing.completion_round_trip.start();
8057 self.completion_worker
8058 .execute(VNextCompletionTaskKind::WaveReadback, move || {
8059 let observation = match readbacks {
8060 VNextTerminalReadbacks::Batch(request) => {
8061 completion.wait_with_readbacks(request)
8062 }
8063 VNextTerminalReadbacks::Collection(request) => {
8064 completion.wait_with_readback_collection(request)
8065 }
8066 };
8067 drop(reaper);
8068 observation
8069 })
8070 .await
8071 .map_err(|error| {
8072 FerrumError::backend(format!("vNext completion task failed: {error}"))
8073 })?
8074 .map_err(|error| FerrumError::backend(error.to_string()))?
8075 };
8076 let _postprocess_timing = self.metrics.wave_timing.host_postprocess.start();
8077 let _phase_postprocess_timing = phase_timing.host_postprocess.start();
8078 let receipt = match observation {
8079 CompletionReadbackBatchObservation::Terminal(receipt) => receipt,
8080 other => {
8081 let message = nonterminal_completion_message(&other);
8082 self.metrics.record_failure(message.clone());
8083 token_mask_residency.invalidate_before_slot_release();
8084 return Err(FerrumError::backend(message));
8085 }
8086 };
8087 self.refresh_on_demand_reusable_execution_catalog()?;
8088 self.metrics.device_timing.record(&receipt);
8089 self.metrics.device_timing_for(kind).record(&receipt);
8090 if let Some(attribution) = attribution {
8091 match attribution.bind_terminal_timing(receipt.completion().submission_timing().clone())
8092 {
8093 Ok(attribution) => {
8094 let sink = self.event_sink.read().clone();
8095 if let Some(sink) = sink {
8096 if let Err(error) = sink.record_device_submission_attribution(&attribution)
8097 {
8098 execution_event_error.get_or_insert_with(|| error.to_string());
8099 }
8100 }
8101 }
8102 Err(error) => {
8103 execution_event_error.get_or_insert_with(|| error.to_string());
8104 }
8105 }
8106 } else if !matches!(
8107 receipt.completion().submission_timing(),
8108 DeviceTimingMeasurement::NotRequested
8109 ) {
8110 let sink = self.event_sink.read().clone();
8111 if let Some(sink) = sink {
8112 if let Err(error) =
8113 sink.record_physical_device_submission_timing(receipt.completion())
8114 {
8115 execution_event_error.get_or_insert_with(|| error.to_string());
8116 }
8117 }
8118 }
8119 if !diagnostic_failure_requested {
8120 for participant in participants {
8121 if let Some(events) = &participant.sequence.events {
8122 if let Err(error) = events.lock().completed(receipt.completion()) {
8123 execution_event_error.get_or_insert_with(|| error.to_string());
8124 }
8125 }
8126 }
8127 }
8128 if !matches!(
8129 receipt.completion().disposition(),
8130 OperationCompletionDisposition::Succeeded
8131 ) {
8132 let message = format!(
8133 "vNext device wave failed: {:?}",
8134 receipt.completion().disposition()
8135 );
8136 token_mask_residency.invalidate_before_slot_release();
8137 drop(receipt);
8138 return Err(self.abort_step(step, message).await);
8139 }
8140 token_mask_residency.publish();
8144 token_mask_residency.settle_success();
8145 if diagnostic_failure_requested {
8146 if diagnostic_failure_observed {
8147 if let Err(error) = participants[0]
8148 .sequence
8149 .events
8150 .as_ref()
8151 .expect("observed diagnostic failure has an execution journal")
8152 .lock()
8153 .settle_failed_submission(receipt.completion())
8154 {
8155 execution_event_error.get_or_insert_with(|| error.to_string());
8156 }
8157 }
8158 drop(receipt);
8159 if let Err(failure) = step.try_retire_normal() {
8160 execution_event_error.get_or_insert_with(|| {
8161 format!(
8162 "vNext diagnostic step retirement failed: {}",
8163 failure.error()
8164 )
8165 });
8166 }
8167 if let Some(error) = execution_event_error {
8168 participants[0].sequence.abort();
8169 let message =
8170 format!("vNext diagnostic resource failure attribution failed closed: {error}");
8171 self.metrics.record_failure(message.clone());
8172 return Err(FerrumError::backend(message));
8173 }
8174 participants[0].sequence.abort_after_observed_failure()?;
8175 let message = "typed diagnostic resource failure after prefill submission".to_string();
8176 self.metrics.record_failure(message.clone());
8177 return Err(FerrumError::resource_exhausted(message));
8178 }
8179 let processed = (|| -> Result<(
8180 Vec<ExecutorSamplingOutput>,
8181 u64,
8182 Vec<VNextCheckpointArtifactRecord>,
8183 Vec<VNextCheckpointProductOutputRecord>,
8184 )> {
8185 let mut logits = vec![None; participants.len()];
8186 let mut readback_bytes = 0_u64;
8187 let mut checkpoint_records = Vec::new();
8188 let mut product_output_records = Vec::new();
8189 for disposition in receipt.dispositions() {
8190 let CompletionReadbackDisposition::Succeeded(output) = disposition else {
8191 return Err(FerrumError::backend(format!(
8192 "vNext terminal readback failed: {disposition:?}"
8193 )));
8194 };
8195 readback_bytes = readback_bytes
8196 .saturating_add(output.request().output_layout().byte_len().unwrap_or(0));
8197 let request = output.request();
8198 let is_product_output = match output_mode {
8199 VNextProductOutputMode::FullLogits => {
8200 request.node_id() == &self.io.output_node_id
8201 && request.resource_id() == &self.io.output_resource_id
8202 && request.logical_offset_bytes() == self.io.output_offset_bytes
8203 && request.output_layout() == self.io.output_layout
8204 }
8205 VNextProductOutputMode::GreedyToken => {
8206 request.node_id() == &self.io.greedy_token_output_node_id
8207 && request.resource_id() == &self.io.greedy_token_output_resource_id
8208 && request.logical_offset_bytes()
8209 == self.io.greedy_token_output_offset_bytes
8210 && request.output_layout() == self.io.greedy_token_output_layout
8211 }
8212 };
8213 let checkpoint = capture_claim
8214 .and_then(|_| self.checkpoint_capture.as_ref())
8215 .and_then(|capture| capture.checkpoint_for_output(output));
8216 if !is_product_output && checkpoint.is_none() {
8217 return Err(FerrumError::internal(format!(
8218 "vNext terminal readback returned unowned node/resource {}/{}",
8219 request.node_id(),
8220 request.resource_id()
8221 )));
8222 }
8223 if is_product_output {
8224 let participant_index =
8225 usize::try_from(request.participant_index()).map_err(|_| {
8226 FerrumError::internal(
8227 "vNext product-output participant index exceeds usize",
8228 )
8229 })?;
8230 let slot = logits.get_mut(participant_index).ok_or_else(|| {
8231 FerrumError::internal(
8232 "vNext product-output participant index exceeds submitted participants",
8233 )
8234 })?;
8235 if slot.is_some() {
8236 return Err(FerrumError::internal(
8237 "vNext terminal readback returned duplicate participant output",
8238 ));
8239 }
8240 *slot = Some(self.decode_product_output(output.bytes(), output_mode)?);
8241 if let (Some(capture_claim), Some(capture)) =
8242 (capture_claim, self.checkpoint_capture.as_ref())
8243 {
8244 if capture.captures_product_output() {
8245 let participant =
8246 participants.get(participant_index).ok_or_else(|| {
8247 FerrumError::internal(
8248 "vNext product-output checkpoint participant index exceeds submitted participants",
8249 )
8250 })?;
8251 product_output_records.push(capture.write_product_output(
8252 capture_claim,
8253 participant.sequence.request_id(),
8254 participant.span,
8255 output_mode.checkpoint_mode(),
8256 output,
8257 )?);
8258 }
8259 }
8260 }
8261 if let (Some(capture_claim), Some(capture), Some(checkpoint)) =
8262 (capture_claim, self.checkpoint_capture.as_ref(), checkpoint)
8263 {
8264 let participant_index =
8265 usize::try_from(request.participant_index()).map_err(|_| {
8266 FerrumError::internal(
8267 "vNext checkpoint participant index exceeds usize",
8268 )
8269 })?;
8270 let participant = participants.get(participant_index).ok_or_else(|| {
8271 FerrumError::internal(
8272 "vNext checkpoint participant index exceeds submitted participants",
8273 )
8274 })?;
8275 checkpoint_records.push(capture.write_output(
8276 capture_claim,
8277 participant.sequence.request_id(),
8278 participant.span,
8279 checkpoint,
8280 output,
8281 )?);
8282 }
8283 }
8284 let logits = logits
8285 .into_iter()
8286 .enumerate()
8287 .map(|(participant_index, logits)| {
8288 logits.ok_or_else(|| {
8289 FerrumError::internal(format!(
8290 "vNext terminal readback omitted participant {participant_index} product output"
8291 ))
8292 })
8293 })
8294 .collect::<Result<Vec<_>>>()?;
8295 Ok((
8296 logits,
8297 readback_bytes,
8298 checkpoint_records,
8299 product_output_records,
8300 ))
8301 })();
8302 let (mut logits, readback_bytes, checkpoint_records, product_output_records) =
8303 match processed {
8304 Ok(processed) => processed,
8305 Err(error) => {
8306 drop(receipt);
8307 return Err(self.abort_step(step, error.to_string()).await);
8308 }
8309 };
8310 if let (Some(capture_claim), Some(capture)) =
8311 (capture_claim, self.checkpoint_capture.as_ref())
8312 {
8313 if let Err(error) = capture.finish_wave(
8314 capture_claim,
8315 participants.len(),
8316 receipt.completion().fingerprint(),
8317 receipt.fingerprint(),
8318 checkpoint_records,
8319 product_output_records,
8320 ) {
8321 drop(receipt);
8322 return Err(self.abort_step(step, error.to_string()).await);
8323 }
8324 }
8325 if let Some(decision) = teacher_forced_decision {
8326 if let Err(error) = apply_teacher_forced_decision(&mut logits, decision) {
8327 drop(receipt);
8328 return Err(self.abort_step(step, error.to_string()).await);
8329 }
8330 }
8331 self.metrics
8332 .readback_bytes
8333 .fetch_add(readback_bytes, Ordering::Relaxed);
8334 for plan in token_mask_residency.plans() {
8335 if plan.upload_required {
8336 self.metrics
8337 .token_mask_upload_participants
8338 .fetch_add(1, Ordering::Relaxed);
8339 } else {
8340 self.metrics
8341 .token_mask_cache_hit_participants
8342 .fetch_add(1, Ordering::Relaxed);
8343 }
8344 }
8345 let (sparse_repetition_participants, sparse_repetition_token_ids) = participants
8346 .iter()
8347 .map(|participant| product_repetition_input(participant.logits_policy, output_mode))
8348 .filter(|input| input.is_active())
8349 .fold((0_u64, 0_u64), |(participants, token_ids), input| {
8350 (
8351 participants.saturating_add(1),
8352 token_ids.saturating_add(input.token_ids.len() as u64),
8353 )
8354 });
8355 if sparse_repetition_participants != 0 {
8356 self.metrics
8357 .sparse_repetition_waves
8358 .fetch_add(1, Ordering::Relaxed);
8359 self.metrics
8360 .sparse_repetition_participants
8361 .fetch_add(sparse_repetition_participants, Ordering::Relaxed);
8362 self.metrics
8363 .sparse_repetition_token_ids_uploaded
8364 .fetch_add(sparse_repetition_token_ids, Ordering::Relaxed);
8365 }
8366 match output_mode {
8367 VNextProductOutputMode::FullLogits => {
8368 self.metrics
8369 .full_logits_readback_waves
8370 .fetch_add(1, Ordering::Relaxed);
8371 if kind == VNextExecutionWaveKind::Decode
8372 && participants.iter().any(|participant| {
8373 matches!(
8374 participant.logits_policy,
8375 Some(LogitsReturnPolicy::GreedyArgmax { .. })
8376 )
8377 })
8378 {
8379 self.metrics
8380 .greedy_policy_fallback_waves
8381 .fetch_add(1, Ordering::Relaxed);
8382 }
8383 }
8384 VNextProductOutputMode::GreedyToken => {
8385 self.metrics
8386 .greedy_token_readback_waves
8387 .fetch_add(1, Ordering::Relaxed);
8388 }
8389 }
8390 drop(receipt);
8391 match step.try_retire_normal() {
8392 Ok(_) => {
8393 self.metrics.completed_waves.fetch_add(1, Ordering::Relaxed);
8394 if let Some(error) = execution_event_error {
8395 let message = format!("vNext execution event emission failed: {error}");
8396 self.metrics.record_failure(message.clone());
8397 Err(FerrumError::backend(message))
8398 } else {
8399 Ok(logits)
8400 }
8401 }
8402 Err(failure) => {
8403 let message = format!("vNext step retirement failed: {}", failure.error());
8404 self.metrics.record_failure(message.clone());
8405 Err(FerrumError::backend(message))
8406 }
8407 }
8408 }
8409
8410 fn prepare_terminal_readbacks(
8411 &self,
8412 participants: &[VNextExecutionParticipant<'_, R>],
8413 capture_claim: Option<VNextCheckpointClaim>,
8414 output_mode: VNextProductOutputMode,
8415 ) -> Result<VNextTerminalReadbacks> {
8416 let (output_node_id, output_resource_id, output_offset_bytes, output_layout) =
8417 match output_mode {
8418 VNextProductOutputMode::FullLogits => (
8419 &self.io.output_node_id,
8420 &self.io.output_resource_id,
8421 self.io.output_offset_bytes,
8422 self.io.output_layout,
8423 ),
8424 VNextProductOutputMode::GreedyToken => (
8425 &self.io.greedy_token_output_node_id,
8426 &self.io.greedy_token_output_resource_id,
8427 self.io.greedy_token_output_offset_bytes,
8428 self.io.greedy_token_output_layout,
8429 ),
8430 };
8431 let product_readbacks = participants
8432 .iter()
8433 .enumerate()
8434 .map(|(participant_index, _)| {
8435 CompletionReadbackRequest::new(
8436 output_node_id.clone(),
8437 u32::try_from(participant_index).map_err(|_| {
8438 FerrumError::backend("vNext readback participant index exceeds u32")
8439 })?,
8440 output_resource_id.clone(),
8441 output_offset_bytes,
8442 output_layout,
8443 )
8444 .map_err(|error| FerrumError::backend(error.to_string()))
8445 })
8446 .collect::<Result<Vec<_>>>()?;
8447 let product_readbacks = CompletionReadbackBatchRequest::new(product_readbacks)
8448 .map_err(|error| FerrumError::backend(error.to_string()))?;
8449 let mut readback_batches = vec![product_readbacks.clone()];
8450 if capture_claim.is_some() {
8451 let capture = self.checkpoint_capture.as_ref().ok_or_else(|| {
8452 FerrumError::internal("vNext checkpoint capture index has no capture owner")
8453 })?;
8454 let token_spans = participants
8455 .iter()
8456 .map(|participant| participant.span)
8457 .collect::<Vec<_>>();
8458 for batch in
8459 capture.readback_batches(self.resolved_plan.execution_plan(), &token_spans)?
8460 {
8461 let first = &batch.requests()[0];
8462 let product_first = &product_readbacks.requests()[0];
8463 if first.node_id() == product_first.node_id()
8464 && first.resource_id() == product_first.resource_id()
8465 && first.logical_offset_bytes() == product_first.logical_offset_bytes()
8466 {
8467 if batch != product_readbacks {
8468 return Err(FerrumError::internal(
8469 "retained output readback differs from the product output layout",
8470 ));
8471 }
8472 continue;
8473 }
8474 readback_batches.push(batch);
8475 }
8476 }
8477 if readback_batches.len() == 1 {
8478 return Ok(VNextTerminalReadbacks::Batch(
8479 readback_batches.pop().ok_or_else(|| {
8480 FerrumError::internal("vNext terminal readback batch disappeared")
8481 })?,
8482 ));
8483 }
8484 CompletionReadbackCollectionRequest::new(readback_batches)
8485 .map(VNextTerminalReadbacks::Collection)
8486 .map_err(|error| FerrumError::backend(error.to_string()))
8487 }
8488
8489 fn decode_logits(bytes: &[u8], element_type: ElementType) -> Result<Vec<f32>> {
8490 match element_type {
8491 ElementType::F16 => Ok(bytes
8492 .chunks_exact(2)
8493 .map(|chunk| {
8494 half::f16::from_bits(u16::from_le_bytes([chunk[0], chunk[1]])).to_f32()
8495 })
8496 .collect()),
8497 ElementType::Bf16 => Ok(bytes
8498 .chunks_exact(2)
8499 .map(|chunk| {
8500 half::bf16::from_bits(u16::from_le_bytes([chunk[0], chunk[1]])).to_f32()
8501 })
8502 .collect()),
8503 ElementType::F32 => Ok(bytes
8504 .chunks_exact(4)
8505 .map(|chunk| f32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
8506 .collect()),
8507 other => Err(FerrumError::backend(format!(
8508 "unsupported vNext logits element type {other:?}"
8509 ))),
8510 }
8511 }
8512
8513 fn decode_product_output(
8514 &self,
8515 bytes: &[u8],
8516 output_mode: VNextProductOutputMode,
8517 ) -> Result<ExecutorSamplingOutput> {
8518 match output_mode {
8519 VNextProductOutputMode::FullLogits => ExecutorSamplingOutput::full_logits(
8520 Self::decode_logits(bytes, self.io.output_element_type)?,
8521 ),
8522 VNextProductOutputMode::GreedyToken => Ok(ExecutorSamplingOutput::greedy_token(
8523 decode_selected_token(bytes, self.io.output_elements)?,
8524 )),
8525 }
8526 }
8527
8528 fn prefill_tensor(&self, logits: Vec<f32>) -> Result<TensorRef> {
8529 let tensor = candle_core::Tensor::from_vec(
8530 logits,
8531 (1, 1, self.io.output_elements),
8532 &candle_core::Device::Cpu,
8533 )
8534 .map_err(|error| FerrumError::model(format!("vNext prefill logits tensor: {error}")))?;
8535 Ok(common::wrap_tensor(tensor))
8536 }
8537
8538 fn decode_tensor(&self, logits: Vec<f32>) -> Result<TensorRef> {
8539 let output_elements = decode_output_width(logits.len(), self.io.output_elements)?;
8540 let tensor =
8541 candle_core::Tensor::from_vec(logits, (1, output_elements), &candle_core::Device::Cpu)
8542 .map_err(|error| {
8543 FerrumError::model(format!("vNext decode logits tensor: {error}"))
8544 })?;
8545 Ok(common::wrap_tensor(tensor))
8546 }
8547
8548 fn cache_handle(
8549 &self,
8550 sequence: &Arc<VNextSequence<R>>,
8551 tokens: usize,
8552 ) -> Arc<dyn KvCacheHandle> {
8553 Arc::new(VNextKvCacheHandle::new(
8554 sequence,
8555 &self.info,
8556 self.attention_head_dimension,
8557 tokens,
8558 ))
8559 }
8560
8561 fn sequence_for_cache(&self, cache_id: &str) -> Result<Arc<VNextSequence<R>>> {
8562 self.sequences
8563 .lock()
8564 .active
8565 .get(cache_id)
8566 .cloned()
8567 .ok_or_else(|| {
8568 FerrumError::not_found(format!("vNext cache `{cache_id}` is not active"))
8569 })
8570 }
8571
8572 fn abort_decode_candidates(&self, candidates: &[VNextDecodeCandidate<R>]) {
8573 {
8574 let mut registry = self.sequences.lock();
8575 for candidate in candidates {
8576 if registry
8577 .active
8578 .get(&candidate.cache_id)
8579 .is_some_and(|current| Arc::ptr_eq(current, &candidate.sequence))
8580 {
8581 registry.active.remove(&candidate.cache_id);
8582 }
8583 }
8584 }
8585 for candidate in candidates {
8586 candidate.sequence.abort();
8587 }
8588 }
8589
8590 async fn execute_plan_runtime_decode_batch(
8591 &self,
8592 inputs: &[PlanRuntimeDecodeInput],
8593 ) -> Result<PlanRuntimeBatchDecodeOutcome> {
8594 let started = Instant::now();
8595 if inputs.is_empty() {
8596 return Ok(PlanRuntimeBatchDecodeOutcome::Completed(Vec::new()));
8597 }
8598
8599 let mut candidates = Vec::with_capacity(inputs.len());
8600 for (original_index, input) in inputs.iter().enumerate() {
8601 let cache_id = input.kv_cache.cache_id();
8602 let sequence = self.sequence_for_cache(&cache_id)?;
8603 if &input.request_id != sequence.request_id() {
8604 return Err(FerrumError::request_validation(
8605 "vNext batch-decode request identity differs from its cache owner",
8606 ));
8607 }
8608 candidates.push(VNextDecodeCandidate {
8609 original_index,
8610 sequence,
8611 cache_id,
8612 next_token: input.input_token.get(),
8613 logits_policy: input.logits_policy.clone(),
8614 });
8615 }
8616
8617 let batch = ExecutionBatchParticipants::new(
8618 candidates
8619 .iter()
8620 .map(|candidate| Arc::clone(&candidate.sequence.session))
8621 .collect(),
8622 )
8623 .map_err(|error| FerrumError::request_validation(error.to_string()))?;
8624 let mut candidates_by_authority = BTreeMap::new();
8625 for candidate in candidates {
8626 let authority = candidate.sequence.session.sequence_authority();
8627 if candidates_by_authority
8628 .insert(authority, candidate)
8629 .is_some()
8630 {
8631 return Err(FerrumError::request_validation(
8632 "vNext batch-decode inputs contain a duplicate sequence",
8633 ));
8634 }
8635 }
8636 let canonical_candidates = batch
8637 .sessions()
8638 .iter()
8639 .map(|session| {
8640 candidates_by_authority
8641 .remove(&session.sequence_authority())
8642 .ok_or_else(|| {
8643 FerrumError::internal(
8644 "vNext canonical decode participant is absent from its input batch",
8645 )
8646 })
8647 })
8648 .collect::<Result<Vec<_>>>()?;
8649 if !candidates_by_authority.is_empty() {
8650 return Err(FerrumError::internal(
8651 "vNext decode input is absent from its canonical participant batch",
8652 ));
8653 }
8654
8655 let mut operation_guards = Vec::with_capacity(canonical_candidates.len());
8656 for candidate in &canonical_candidates {
8657 operation_guards.push(candidate.sequence.operation.lock().await);
8658 }
8659
8660 let mut token_batches = Vec::with_capacity(canonical_candidates.len());
8661 let mut previous_lengths = Vec::with_capacity(canonical_candidates.len());
8662 let mut spans = Vec::with_capacity(canonical_candidates.len());
8663 for candidate in &canonical_candidates {
8664 if !candidate.sequence.active.load(Ordering::Acquire) {
8665 return Err(FerrumError::cancelled(format!(
8666 "vNext cache `{}` is no longer active",
8667 candidate.cache_id
8668 )));
8669 }
8670 let (tokens, previous_len) = {
8671 let current = candidate.sequence.tokens.lock();
8672 let previous_len = current.len();
8673 if previous_len >= candidate.sequence.maximum_tokens {
8674 return Err(FerrumError::request_validation(format!(
8675 "vNext sequence reached its {} token ceiling",
8676 candidate.sequence.maximum_tokens
8677 )));
8678 }
8679 let mut tokens = current.clone();
8680 tokens.push(candidate.next_token);
8681 (tokens, previous_len)
8682 };
8683 let extension_span = TokenSpanWork::from_token_ids(&tokens, 0..tokens.len())
8684 .map_err(|error| FerrumError::backend(error.to_string()))?;
8685 let extension = ResourceWorkShape::single(extension_span)
8686 .map_err(|error| FerrumError::backend(error.to_string()))?;
8687 match self.extend_sequence_with_capacity(&candidate.sequence, extension) {
8688 Ok(VNextExecutionCapacityDecision::Ready(())) => {}
8689 Ok(VNextExecutionCapacityDecision::Deferred(deferred)) => {
8690 return Ok(PlanRuntimeBatchDecodeOutcome::Deferred(deferred.into()));
8691 }
8692 Ok(VNextExecutionCapacityDecision::RequestStateDeferred(_)) => {
8693 return Err(FerrumError::internal(
8694 "sequence extension unexpectedly produced a Request-state deferral",
8695 ));
8696 }
8697 Err(error) => {
8698 if DecodeFailureDisposition::from_error(&error)
8699 == DecodeFailureDisposition::AbortSequence
8700 {
8701 self.abort_decode_candidates(std::slice::from_ref(candidate));
8702 }
8703 return Err(error);
8704 }
8705 }
8706 let span = TokenSpanWork::from_token_ids(&tokens, previous_len..tokens.len())
8707 .map_err(|error| FerrumError::backend(error.to_string()))?;
8708 token_batches.push(tokens);
8709 previous_lengths.push(previous_len);
8710 spans.push(span);
8711 }
8712
8713 let sequences = canonical_candidates
8714 .iter()
8715 .map(|candidate| Arc::clone(&candidate.sequence))
8716 .collect::<Vec<_>>();
8717 let logits_policies = canonical_candidates
8718 .iter()
8719 .map(|candidate| candidate.logits_policy.clone())
8720 .collect::<Vec<_>>();
8721 let logits = match self
8722 .execute_batch_step(
8723 &batch,
8724 &sequences,
8725 &token_batches,
8726 &spans,
8727 VNextExecutionWaveKind::Decode,
8728 Some(&logits_policies),
8729 )
8730 .await
8731 {
8732 Ok(VNextExecutionCapacityDecision::Ready(logits)) => logits,
8733 Ok(VNextExecutionCapacityDecision::Deferred(deferred)) => {
8734 return Ok(PlanRuntimeBatchDecodeOutcome::Deferred(deferred.into()));
8735 }
8736 Ok(VNextExecutionCapacityDecision::RequestStateDeferred(deferred)) => {
8737 return Ok(PlanRuntimeBatchDecodeOutcome::Deferred(deferred.into()));
8738 }
8739 Err(error) => {
8740 if DecodeFailureDisposition::from_error(&error)
8741 == DecodeFailureDisposition::AbortSequence
8742 {
8743 self.abort_decode_candidates(&canonical_candidates);
8744 }
8745 return Err(error);
8746 }
8747 };
8748 if logits.len() != canonical_candidates.len() {
8749 self.abort_decode_candidates(&canonical_candidates);
8750 return Err(FerrumError::internal(format!(
8751 "vNext batch decode returned {} logits rows for {} participants",
8752 logits.len(),
8753 canonical_candidates.len()
8754 )));
8755 }
8756 for (sampling_output, candidate) in logits.iter().zip(&canonical_candidates) {
8757 if let Err(error) = sampling_output
8758 .validate_for_policy(&candidate.logits_policy, self.io.output_elements)
8759 {
8760 self.abort_decode_candidates(&canonical_candidates);
8761 return Err(error);
8762 }
8763 }
8764
8765 let mut ordered_outputs = (0..inputs.len()).map(|_| None).collect::<Vec<_>>();
8766 for (((candidate, tokens), previous_len), sampling_output) in canonical_candidates
8767 .iter()
8768 .zip(token_batches)
8769 .zip(previous_lengths)
8770 .zip(logits)
8771 {
8772 if candidate.sequence.active.load(Ordering::Acquire) {
8773 *candidate.sequence.tokens.lock() = tokens;
8774 }
8775 let cache = self.cache_handle(&candidate.sequence, previous_len + 1);
8776 ordered_outputs[candidate.original_index] =
8777 Some(PlanRuntimeDecodeOutput::new(sampling_output, cache));
8778 }
8779
8780 let participant_count = u64::try_from(inputs.len()).unwrap_or(u64::MAX);
8781 self.metrics
8782 .decode_operations
8783 .fetch_add(participant_count, Ordering::Relaxed);
8784 let elapsed_us = started.elapsed().as_micros().min(u64::MAX as u128) as u64;
8785 self.metrics.total_decode_us.fetch_add(
8786 elapsed_us.saturating_mul(participant_count),
8787 Ordering::Relaxed,
8788 );
8789 let outputs = ordered_outputs
8790 .into_iter()
8791 .map(|output| {
8792 output.ok_or_else(|| {
8793 FerrumError::internal(
8794 "vNext batch decode lost the original participant ordering",
8795 )
8796 })
8797 })
8798 .collect::<Result<Vec<_>>>()?;
8799 Ok(PlanRuntimeBatchDecodeOutcome::Completed(outputs))
8800 }
8801
8802 async fn execute_legacy_decode_batch(
8803 &self,
8804 inputs: &[DecodeInput],
8805 ) -> Result<ExecutorBatchDecodeOutcome> {
8806 let mut typed_inputs = Vec::with_capacity(inputs.len());
8807 for input in inputs {
8808 if input.batch_size() != 1 {
8809 return Err(FerrumError::unsupported(
8810 "each vNext batch-decode input must contain exactly one sequence",
8811 ));
8812 }
8813 let tokens = common::tensor_to_tokens(&input.input_ids)?;
8814 let [input_token] = tokens.as_slice() else {
8815 return Err(FerrumError::request_validation(
8816 "each vNext batch-decode participant requires exactly one input token",
8817 ));
8818 };
8819 let sequence = self.sequence_for_cache(&input.kv_cache.cache_id())?;
8820 let request_id = input
8821 .request_id
8822 .clone()
8823 .unwrap_or_else(|| sequence.request_id().clone());
8824 let typed = PlanRuntimeDecodeInput::new(
8825 request_id,
8826 TokenId::new(*input_token),
8827 Arc::clone(&input.kv_cache),
8828 )
8829 .with_logits_policy(input.logits_policy.clone());
8830 typed_inputs.push(typed);
8831 }
8832
8833 match self
8834 .execute_plan_runtime_decode_batch(&typed_inputs)
8835 .await?
8836 {
8837 PlanRuntimeBatchDecodeOutcome::Completed(outputs) => {
8838 let outputs = outputs
8839 .into_iter()
8840 .map(|output| {
8841 let legacy_values = match output.sampling_output {
8842 ExecutorSamplingOutput::FullLogits(logits) => logits,
8843 ExecutorSamplingOutput::GreedyToken(token) => {
8844 vec![token.get() as f32]
8845 }
8846 };
8847 let logits = self.decode_tensor(legacy_values)?;
8848 Ok(DecodeOutput::new(logits, output.kv_cache))
8849 })
8850 .collect::<Result<Vec<_>>>()?;
8851 Ok(ExecutorBatchDecodeOutcome::Completed(outputs))
8852 }
8853 PlanRuntimeBatchDecodeOutcome::Deferred(deferred) => {
8854 Ok(ExecutorBatchDecodeOutcome::Deferred(deferred))
8855 }
8856 }
8857 }
8858
8859 async fn execute_plan_runtime_prefill_with_capacity(
8860 &self,
8861 input: &PlanRuntimePrefillInput,
8862 ) -> Result<PlanRuntimePrefillOutcome> {
8863 self.execute_plan_runtime_prefill_with_capacity_policy(
8864 input,
8865 VNextPrefillFrontierPolicy::Adaptive,
8866 )
8867 .await
8868 }
8869
8870 async fn execute_plan_runtime_prefill_with_capacity_policy(
8871 &self,
8872 input: &PlanRuntimePrefillInput,
8873 frontier_policy: VNextPrefillFrontierPolicy,
8874 ) -> Result<PlanRuntimePrefillOutcome> {
8875 let started = Instant::now();
8876 let request_id = input.request_id.clone();
8877 let tokens = input
8878 .input_tokens
8879 .iter()
8880 .map(|token| token.get())
8881 .collect::<Vec<_>>();
8882 let maximum_tokens = input.maximum_sequence_tokens;
8883 if maximum_tokens < tokens.len() || maximum_tokens > self.maximum_model_tokens {
8884 return Err(FerrumError::request_validation(format!(
8885 "request sequence ceiling {maximum_tokens} must cover prompt {} and not exceed {}",
8886 tokens.len(),
8887 self.maximum_model_tokens
8888 )));
8889 }
8890 let planned_chunk = input.chunk;
8891 if planned_chunk.total_prompt_tokens() != tokens.len() {
8892 return Err(FerrumError::request_validation(format!(
8893 "vNext prefill chunk declares {} prompt tokens for input length {}",
8894 planned_chunk.total_prompt_tokens(),
8895 tokens.len()
8896 )));
8897 }
8898
8899 let (slot, sequence) = self.sequences.lock().begin_prefill_execution(&request_id)?;
8900 let mut execution = VNextPrefillExecutionGuard::new(
8901 &self.sequences,
8902 Arc::clone(&slot),
8903 Arc::clone(&sequence),
8904 );
8905 let _operation = sequence.operation.lock().await;
8906 if sequence.maximum_tokens != maximum_tokens || *sequence.tokens.lock() != tokens {
8907 return Err(FerrumError::request_validation(format!(
8908 "vNext prefill input for `{request_id}` differs from its admitted work"
8909 )));
8910 }
8911 let processed = sequence.prefill_tokens_processed.load(Ordering::Acquire);
8912 if processed != planned_chunk.tokens_processed() {
8913 return Err(FerrumError::request_validation(format!(
8914 "vNext prefill chunk for `{request_id}` starts at {}, expected {processed}",
8915 planned_chunk.tokens_processed()
8916 )));
8917 }
8918 if slot.cancelled.load(Ordering::Acquire) {
8919 return Err(FerrumError::cancelled(format!(
8920 "vNext prefill for `{request_id}` was cancelled before submission"
8921 )));
8922 }
8923
8924 let batch = ExecutionBatchParticipants::new(vec![Arc::clone(&sequence.session)])
8925 .map_err(|error| FerrumError::backend(error.to_string()))?;
8926 let mut completed_chunk = planned_chunk;
8927 let mut capacity_probe_count = 0_u32;
8928 let mut logits = loop {
8929 let extension_tokens = &tokens[..completed_chunk.end()];
8930 let extension_span =
8931 TokenSpanWork::from_token_ids(extension_tokens, 0..extension_tokens.len())
8932 .map_err(|error| FerrumError::backend(error.to_string()))?;
8933 let extension = ResourceWorkShape::single(extension_span)
8934 .map_err(|error| FerrumError::backend(error.to_string()))?;
8935 if let VNextExecutionCapacityDecision::Deferred(deferred) =
8936 self.extend_sequence_with_capacity(&sequence, extension)?
8937 {
8938 let Some(next_tokens) =
8939 deferred.narrower_prefill_tokens(completed_chunk.tokens_to_process())
8940 else {
8941 execution.restore_ready()?;
8942 return Ok(PlanRuntimePrefillOutcome::Deferred(deferred.into()));
8943 };
8944 if frontier_policy == VNextPrefillFrontierPolicy::ExactStartup {
8945 execution.restore_ready()?;
8946 return Err(FerrumError::resource_exhausted(format!(
8947 "vNext reusable startup prefill {:?} would narrow to {next_tokens} tokens before submission; refusing to capture a shape outside the resolved matrix",
8948 completed_chunk.range()
8949 )));
8950 }
8951 capacity_probe_count = capacity_probe_count.checked_add(1).ok_or_else(|| {
8952 FerrumError::internal("vNext prefill capacity probe count overflow")
8953 })?;
8954 self.metrics
8955 .prefill_frontier_narrowings
8956 .fetch_add(1, Ordering::Relaxed);
8957 completed_chunk = PrefillChunk::new(
8958 completed_chunk.tokens_processed(),
8959 next_tokens,
8960 completed_chunk.total_prompt_tokens(),
8961 )?;
8962 continue;
8963 }
8964
8965 let span = TokenSpanWork::from_token_ids_with_fit(
8966 &tokens,
8967 completed_chunk.range(),
8968 maximum_tokens,
8969 )
8970 .map_err(|error| FerrumError::backend(error.to_string()))?;
8971 match self
8972 .execute_batch_step(
8973 &batch,
8974 std::slice::from_ref(&sequence),
8975 std::slice::from_ref(&tokens),
8976 std::slice::from_ref(&span),
8977 VNextExecutionWaveKind::Prefill,
8978 None,
8979 )
8980 .await?
8981 {
8982 VNextExecutionCapacityDecision::Ready(logits) => break logits,
8983 VNextExecutionCapacityDecision::Deferred(deferred) => {
8984 let Some(next_tokens) =
8985 deferred.narrower_prefill_tokens(completed_chunk.tokens_to_process())
8986 else {
8987 execution.restore_ready()?;
8988 return Ok(PlanRuntimePrefillOutcome::Deferred(deferred.into()));
8989 };
8990 if frontier_policy == VNextPrefillFrontierPolicy::ExactStartup {
8991 execution.restore_ready()?;
8992 return Err(FerrumError::resource_exhausted(format!(
8993 "vNext reusable startup prefill {:?} would narrow to {next_tokens} tokens before submission; refusing to capture a shape outside the resolved matrix",
8994 completed_chunk.range()
8995 )));
8996 }
8997 capacity_probe_count =
8998 capacity_probe_count.checked_add(1).ok_or_else(|| {
8999 FerrumError::internal("vNext prefill capacity probe count overflow")
9000 })?;
9001 self.metrics
9002 .prefill_frontier_narrowings
9003 .fetch_add(1, Ordering::Relaxed);
9004 completed_chunk = PrefillChunk::new(
9005 completed_chunk.tokens_processed(),
9006 next_tokens,
9007 completed_chunk.total_prompt_tokens(),
9008 )?;
9009 }
9010 VNextExecutionCapacityDecision::RequestStateDeferred(deferred) => {
9011 execution.restore_ready()?;
9012 return Ok(PlanRuntimePrefillOutcome::Deferred(deferred.into()));
9013 }
9014 }
9015 };
9016 let logits = logits.pop().ok_or_else(|| {
9017 FerrumError::internal("vNext single prefill execution returned no logits")
9018 })?;
9019 let logits = logits.into_full_logits()?;
9020 self.retain_prefill_boundary(&sequence, &tokens, completed_chunk)
9021 .await?;
9022 let cache = self.cache_handle(&sequence, completed_chunk.end());
9023 let output = if completed_chunk.is_final() {
9024 PlanRuntimePrefillOutput::final_logits(
9025 request_id.clone(),
9026 completed_chunk.end(),
9027 logits,
9028 cache,
9029 )?
9030 } else {
9031 PlanRuntimePrefillOutput::intermediate(request_id.clone(), completed_chunk.end(), cache)
9032 };
9033 output.validate_for_completion(&request_id, completed_chunk, self.io.output_elements)?;
9034 sequence
9035 .prefill_tokens_processed
9036 .store(completed_chunk.end(), Ordering::Release);
9037 if completed_chunk.is_final() {
9038 self.sequences.lock().activate(&slot, &sequence)?;
9039 execution.disarm();
9040 } else {
9041 execution.restore_ready()?;
9042 }
9043 self.metrics
9044 .prefill_operations
9045 .fetch_add(1, Ordering::Relaxed);
9046 self.metrics.total_prefill_us.fetch_add(
9047 started.elapsed().as_micros().min(u64::MAX as u128) as u64,
9048 Ordering::Relaxed,
9049 );
9050 Ok(PlanRuntimePrefillOutcome::Completed(
9051 PlanRuntimePrefillCompletion::new(
9052 output,
9053 planned_chunk,
9054 completed_chunk,
9055 capacity_probe_count,
9056 )?,
9057 ))
9058 }
9059
9060 async fn execute_plan_runtime_prefill_batch_with_capacity(
9061 &self,
9062 inputs: &[PlanRuntimePrefillInput],
9063 ) -> Result<PlanRuntimeBatchPrefillOutcome> {
9064 if inputs.is_empty() {
9065 return Ok(PlanRuntimeBatchPrefillOutcome::Completed(Vec::new()));
9066 }
9067 let started = Instant::now();
9068 let mut parsed = Vec::with_capacity(inputs.len());
9069 for (original_index, input) in inputs.iter().enumerate() {
9070 let request_id = input.request_id.clone();
9071 let tokens = input
9072 .input_tokens
9073 .iter()
9074 .map(|token| token.get())
9075 .collect::<Vec<_>>();
9076 let maximum_tokens = input.maximum_sequence_tokens;
9077 if maximum_tokens < tokens.len() || maximum_tokens > self.maximum_model_tokens {
9078 return Err(FerrumError::request_validation(format!(
9079 "request sequence ceiling {maximum_tokens} must cover prompt {} and not exceed {}",
9080 tokens.len(),
9081 self.maximum_model_tokens
9082 )));
9083 }
9084 let planned_chunk = input.chunk;
9085 if planned_chunk.total_prompt_tokens() != tokens.len() {
9086 return Err(FerrumError::request_validation(format!(
9087 "vNext batch prefill chunk declares {} prompt tokens for input length {}",
9088 planned_chunk.total_prompt_tokens(),
9089 tokens.len()
9090 )));
9091 }
9092 parsed.push((
9093 original_index,
9094 request_id,
9095 tokens,
9096 maximum_tokens,
9097 planned_chunk,
9098 ));
9099 }
9100
9101 let request_ids = parsed
9102 .iter()
9103 .map(|(_, request_id, _, _, _)| request_id.clone())
9104 .collect::<Vec<_>>();
9105 let executions = self
9106 .sequences
9107 .lock()
9108 .begin_prefill_batch_execution(&request_ids)?;
9109 let candidates = parsed
9110 .into_iter()
9111 .zip(executions)
9112 .map(
9113 |(
9114 (original_index, _request_id, tokens, maximum_tokens, planned_chunk),
9115 (slot, sequence),
9116 )| {
9117 VNextPrefillCandidate {
9118 original_index,
9119 slot,
9120 sequence,
9121 tokens,
9122 maximum_tokens,
9123 planned_chunk,
9124 }
9125 },
9126 )
9127 .collect::<Vec<_>>();
9128 let mut execution_guards = candidates
9129 .iter()
9130 .map(|candidate| {
9131 VNextPrefillExecutionGuard::new(
9132 &self.sequences,
9133 Arc::clone(&candidate.slot),
9134 Arc::clone(&candidate.sequence),
9135 )
9136 })
9137 .collect::<Vec<_>>();
9138
9139 let batch = ExecutionBatchParticipants::new(
9140 candidates
9141 .iter()
9142 .map(|candidate| Arc::clone(&candidate.sequence.session))
9143 .collect(),
9144 )
9145 .map_err(|error| FerrumError::request_validation(error.to_string()))?;
9146 let mut candidates_by_authority = BTreeMap::new();
9147 for candidate in candidates {
9148 let authority = candidate.sequence.session.sequence_authority();
9149 if candidates_by_authority
9150 .insert(authority, candidate)
9151 .is_some()
9152 {
9153 return Err(FerrumError::request_validation(
9154 "vNext batch-prefill inputs contain a duplicate sequence",
9155 ));
9156 }
9157 }
9158 let candidates = batch
9159 .sessions()
9160 .iter()
9161 .map(|session| {
9162 candidates_by_authority
9163 .remove(&session.sequence_authority())
9164 .ok_or_else(|| {
9165 FerrumError::internal(
9166 "vNext canonical prefill participant is absent from its input batch",
9167 )
9168 })
9169 })
9170 .collect::<Result<Vec<_>>>()?;
9171 if !candidates_by_authority.is_empty() {
9172 return Err(FerrumError::internal(
9173 "vNext prefill input is absent from its canonical participant batch",
9174 ));
9175 }
9176
9177 let mut operation_guards = Vec::with_capacity(candidates.len());
9178 for candidate in &candidates {
9179 operation_guards.push(candidate.sequence.operation.lock().await);
9180 if candidate.sequence.request_id() != &candidate.slot.request_id
9181 || candidate.sequence.maximum_tokens != candidate.maximum_tokens
9182 || *candidate.sequence.tokens.lock() != candidate.tokens
9183 {
9184 return Err(FerrumError::request_validation(format!(
9185 "vNext batch prefill input for `{}` differs from its admitted work",
9186 candidate.slot.request_id
9187 )));
9188 }
9189 let processed = candidate
9190 .sequence
9191 .prefill_tokens_processed
9192 .load(Ordering::Acquire);
9193 if processed != candidate.planned_chunk.tokens_processed() {
9194 return Err(FerrumError::request_validation(format!(
9195 "vNext batch prefill chunk for `{}` starts at {}, expected {processed}",
9196 candidate.slot.request_id,
9197 candidate.planned_chunk.tokens_processed()
9198 )));
9199 }
9200 }
9201
9202 let token_batches = candidates
9203 .iter()
9204 .map(|candidate| candidate.tokens.clone())
9205 .collect::<Vec<_>>();
9206 let sequences = candidates
9207 .iter()
9208 .map(|candidate| Arc::clone(&candidate.sequence))
9209 .collect::<Vec<_>>();
9210 let mut completed_chunks = candidates
9211 .iter()
9212 .map(|candidate| candidate.planned_chunk)
9213 .collect::<Vec<_>>();
9214 let mut capacity_probe_counts = vec![0_u32; candidates.len()];
9215
9216 let logits = 'capacity: loop {
9217 for (index, candidate) in candidates.iter().enumerate() {
9218 let completed_chunk = completed_chunks[index];
9219 let extension_tokens = &candidate.tokens[..completed_chunk.end()];
9220 let extension_span =
9221 TokenSpanWork::from_token_ids(extension_tokens, 0..extension_tokens.len())
9222 .map_err(|error| FerrumError::backend(error.to_string()))?;
9223 let extension = ResourceWorkShape::single(extension_span)
9224 .map_err(|error| FerrumError::backend(error.to_string()))?;
9225 if let VNextExecutionCapacityDecision::Deferred(deferred) =
9226 self.extend_sequence_with_capacity(&candidate.sequence, extension)?
9227 {
9228 if let Some(next_tokens) =
9229 deferred.narrower_prefill_tokens(completed_chunk.tokens_to_process())
9230 {
9231 capacity_probe_counts[index] =
9232 capacity_probe_counts[index].checked_add(1).ok_or_else(|| {
9233 FerrumError::internal(
9234 "vNext batch prefill capacity probe count overflow",
9235 )
9236 })?;
9237 self.metrics
9238 .prefill_frontier_narrowings
9239 .fetch_add(1, Ordering::Relaxed);
9240 completed_chunks[index] = PrefillChunk::new(
9241 completed_chunk.tokens_processed(),
9242 next_tokens,
9243 completed_chunk.total_prompt_tokens(),
9244 )?;
9245 continue 'capacity;
9246 }
9247 let authority = candidates
9248 .iter()
9249 .map(|candidate| (&candidate.slot, &candidate.sequence))
9250 .collect::<Vec<_>>();
9251 self.sequences
9252 .lock()
9253 .restore_prefill_batch_ready(&authority)?;
9254 for guard in &mut execution_guards {
9255 guard.disarm();
9256 }
9257 return Ok(PlanRuntimeBatchPrefillOutcome::NotSubmitted(
9258 deferred.into(),
9259 ));
9260 }
9261 }
9262
9263 let spans = candidates
9264 .iter()
9265 .zip(&completed_chunks)
9266 .map(|(candidate, completed_chunk)| {
9267 TokenSpanWork::from_token_ids(&candidate.tokens, completed_chunk.range())
9268 .map_err(|error| FerrumError::backend(error.to_string()))
9269 })
9270 .collect::<Result<Vec<_>>>()?;
9271 match self
9272 .execute_batch_step(
9273 &batch,
9274 &sequences,
9275 &token_batches,
9276 &spans,
9277 VNextExecutionWaveKind::Prefill,
9278 None,
9279 )
9280 .await?
9281 {
9282 VNextExecutionCapacityDecision::Ready(logits) => break logits,
9283 VNextExecutionCapacityDecision::Deferred(deferred) => {
9284 let mut narrowed = false;
9285 for (index, completed_chunk) in completed_chunks.iter_mut().enumerate() {
9286 let Some(next_tokens) =
9287 deferred.narrower_prefill_tokens(completed_chunk.tokens_to_process())
9288 else {
9289 continue;
9290 };
9291 capacity_probe_counts[index] =
9292 capacity_probe_counts[index].checked_add(1).ok_or_else(|| {
9293 FerrumError::internal(
9294 "vNext batch prefill capacity probe count overflow",
9295 )
9296 })?;
9297 self.metrics
9298 .prefill_frontier_narrowings
9299 .fetch_add(1, Ordering::Relaxed);
9300 *completed_chunk = PrefillChunk::new(
9301 completed_chunk.tokens_processed(),
9302 next_tokens,
9303 completed_chunk.total_prompt_tokens(),
9304 )?;
9305 narrowed = true;
9306 }
9307 if narrowed {
9308 continue 'capacity;
9309 }
9310 let authority = candidates
9311 .iter()
9312 .map(|candidate| (&candidate.slot, &candidate.sequence))
9313 .collect::<Vec<_>>();
9314 self.sequences
9315 .lock()
9316 .restore_prefill_batch_ready(&authority)?;
9317 for guard in &mut execution_guards {
9318 guard.disarm();
9319 }
9320 return Ok(PlanRuntimeBatchPrefillOutcome::NotSubmitted(
9321 deferred.into(),
9322 ));
9323 }
9324 VNextExecutionCapacityDecision::RequestStateDeferred(deferred) => {
9325 let authority = candidates
9326 .iter()
9327 .map(|candidate| (&candidate.slot, &candidate.sequence))
9328 .collect::<Vec<_>>();
9329 self.sequences
9330 .lock()
9331 .restore_prefill_batch_ready(&authority)?;
9332 for guard in &mut execution_guards {
9333 guard.disarm();
9334 }
9335 return Ok(PlanRuntimeBatchPrefillOutcome::NotSubmitted(
9336 deferred.into(),
9337 ));
9338 }
9339 }
9340 };
9341 if logits.len() != candidates.len() {
9342 return Err(FerrumError::internal(format!(
9343 "vNext batch prefill returned {} logits rows for {} participants",
9344 logits.len(),
9345 candidates.len()
9346 )));
9347 }
9348
9349 for (candidate, chunk) in candidates.iter().zip(&completed_chunks) {
9352 self.retain_prefill_boundary(&candidate.sequence, &candidate.tokens, *chunk)
9353 .await?;
9354 }
9355
9356 let mut ordered = (0..inputs.len()).map(|_| None).collect::<Vec<_>>();
9357 for (((candidate, completed_chunk), capacity_probe_count), logits) in candidates
9358 .iter()
9359 .zip(&completed_chunks)
9360 .zip(&capacity_probe_counts)
9361 .zip(logits)
9362 {
9363 let logits = logits.into_full_logits()?;
9364 let cache = self.cache_handle(&candidate.sequence, completed_chunk.end());
9365 let output = if completed_chunk.is_final() {
9366 PlanRuntimePrefillOutput::final_logits(
9367 candidate.slot.request_id.clone(),
9368 completed_chunk.end(),
9369 logits,
9370 cache,
9371 )?
9372 } else {
9373 PlanRuntimePrefillOutput::intermediate(
9374 candidate.slot.request_id.clone(),
9375 completed_chunk.end(),
9376 cache,
9377 )
9378 };
9379 output.validate_for_completion(
9380 &candidate.slot.request_id,
9381 *completed_chunk,
9382 self.io.output_elements,
9383 )?;
9384 ordered[candidate.original_index] = Some(PlanRuntimePrefillCompletion::new(
9385 output,
9386 candidate.planned_chunk,
9387 *completed_chunk,
9388 *capacity_probe_count,
9389 )?);
9390 }
9391 let outputs = ordered
9392 .into_iter()
9393 .map(|output| {
9394 output.ok_or_else(|| {
9395 FerrumError::internal(
9396 "vNext batch prefill lost the original participant ordering",
9397 )
9398 })
9399 })
9400 .collect::<Result<Vec<_>>>()?;
9401
9402 let authority = candidates
9403 .iter()
9404 .zip(&completed_chunks)
9405 .map(|(candidate, completed_chunk)| {
9406 (
9407 &candidate.slot,
9408 &candidate.sequence,
9409 completed_chunk.is_final(),
9410 )
9411 })
9412 .collect::<Vec<_>>();
9413 for (candidate, completed_chunk) in candidates.iter().zip(&completed_chunks) {
9414 candidate
9415 .sequence
9416 .prefill_tokens_processed
9417 .store(completed_chunk.end(), Ordering::Release);
9418 }
9419 self.sequences
9420 .lock()
9421 .commit_prefill_batch_execution(&authority)?;
9422 for guard in &mut execution_guards {
9423 guard.disarm();
9424 }
9425
9426 let participant_count = u64::try_from(inputs.len()).unwrap_or(u64::MAX);
9427 self.metrics
9428 .prefill_operations
9429 .fetch_add(participant_count, Ordering::Relaxed);
9430 let elapsed_us = started.elapsed().as_micros().min(u64::MAX as u128) as u64;
9431 self.metrics.total_prefill_us.fetch_add(
9432 elapsed_us.saturating_mul(participant_count),
9433 Ordering::Relaxed,
9434 );
9435 drop(operation_guards);
9436 Ok(PlanRuntimeBatchPrefillOutcome::Completed(outputs))
9437 }
9438
9439 fn plan_runtime_prefill_input_from_legacy(
9440 &self,
9441 input: &PrefillInput,
9442 ) -> Result<PlanRuntimePrefillInput> {
9443 if input.batch_size() != 1 {
9444 return Err(FerrumError::unsupported(
9445 "each vNext legacy prefill input must contain exactly one sequence",
9446 ));
9447 }
9448 let request_id = input.request_id.clone().ok_or_else(|| {
9449 FerrumError::request_validation("vNext legacy prefill requires a request_id")
9450 })?;
9451 let tokens = common::tensor_to_tokens(&input.input_ids)?
9452 .into_iter()
9453 .map(TokenId::new)
9454 .collect::<Vec<_>>();
9455 let maximum_tokens = input.maximum_sequence_tokens.ok_or_else(|| {
9456 FerrumError::request_validation("vNext legacy prefill requires maximum_sequence_tokens")
9457 })?;
9458 let chunk = match input.chunk {
9459 Some(chunk) => chunk,
9460 None => PrefillChunk::new(0, tokens.len(), tokens.len())?,
9461 };
9462 PlanRuntimePrefillInput::new(request_id, tokens, maximum_tokens, chunk)
9463 }
9464
9465 fn legacy_prefill_completion(
9466 &self,
9467 completion: PlanRuntimePrefillCompletion,
9468 ) -> Result<ExecutorPrefillCompletion> {
9469 let (output, planned_chunk, completed_chunk, capacity_probe_count) =
9470 completion.into_parts();
9471 let (authority, product) = output.into_parts();
9472 let PlanRuntimePrefillProduct::FinalLogits(logits) = product else {
9473 self.discard_plan_runtime_prefill(authority)?;
9474 return Err(FerrumError::unsupported(
9475 "legacy vNext prefill cannot represent an intermediate typed product",
9476 ));
9477 };
9478 let logits = match self.prefill_tensor(logits) {
9479 Ok(logits) => logits,
9480 Err(error) => {
9481 if let Err(cleanup_error) = self.discard_plan_runtime_prefill(authority) {
9482 return Err(FerrumError::internal(format!(
9483 "{error}; exact prefill authority cleanup also failed: {cleanup_error}"
9484 )));
9485 }
9486 return Err(error);
9487 }
9488 };
9489 ExecutorPrefillCompletion::new(
9490 PrefillOutput::new(logits, authority.into_cache()),
9491 planned_chunk,
9492 completed_chunk,
9493 capacity_probe_count,
9494 )
9495 }
9496
9497 fn discard_plan_runtime_prefill_completions(
9498 &self,
9499 completions: impl IntoIterator<Item = PlanRuntimePrefillCompletion>,
9500 ) -> Result<()> {
9501 let mut failures = Vec::new();
9502 for completion in completions {
9503 let (output, _, _, _) = completion.into_parts();
9504 let (authority, _) = output.into_parts();
9505 if let Err(error) = self.discard_plan_runtime_prefill(authority) {
9506 failures.push(error.to_string());
9507 }
9508 }
9509 if failures.is_empty() {
9510 Ok(())
9511 } else {
9512 Err(FerrumError::internal(format!(
9513 "failed to discard {} legacy-adapter prefill authorities: {}",
9514 failures.len(),
9515 failures.join("; ")
9516 )))
9517 }
9518 }
9519
9520 async fn execute_legacy_prefill_with_capacity(
9521 &self,
9522 input: &PrefillInput,
9523 ) -> Result<ExecutorPrefillOutcome> {
9524 let input = self.plan_runtime_prefill_input_from_legacy(input)?;
9525 match self
9526 .execute_plan_runtime_prefill_with_capacity(&input)
9527 .await?
9528 {
9529 PlanRuntimePrefillOutcome::Completed(completion) => self
9530 .legacy_prefill_completion(completion)
9531 .map(ExecutorPrefillOutcome::Completed),
9532 PlanRuntimePrefillOutcome::Deferred(deferred) => {
9533 Ok(ExecutorPrefillOutcome::Deferred(deferred))
9534 }
9535 }
9536 }
9537
9538 async fn execute_legacy_prefill_batch_with_capacity(
9539 &self,
9540 inputs: &[PrefillInput],
9541 ) -> Result<ExecutorBatchPrefillOutcome> {
9542 let inputs = inputs
9543 .iter()
9544 .map(|input| self.plan_runtime_prefill_input_from_legacy(input))
9545 .collect::<Result<Vec<_>>>()?;
9546 match self
9547 .execute_plan_runtime_prefill_batch_with_capacity(&inputs)
9548 .await?
9549 {
9550 PlanRuntimeBatchPrefillOutcome::Completed(completions) => {
9551 if completions.iter().any(|completion| {
9552 !matches!(
9553 completion.output().product(),
9554 PlanRuntimePrefillProduct::FinalLogits(_)
9555 )
9556 }) {
9557 self.discard_plan_runtime_prefill_completions(completions)?;
9558 return Err(FerrumError::unsupported(
9559 "legacy vNext batch prefill cannot represent an intermediate typed product",
9560 ));
9561 }
9562 completions
9563 .into_iter()
9564 .map(|completion| self.legacy_prefill_completion(completion))
9565 .collect::<Result<Vec<_>>>()
9566 .map(ExecutorBatchPrefillOutcome::Completed)
9567 }
9568 PlanRuntimeBatchPrefillOutcome::NotSubmitted(deferred) => {
9569 Ok(ExecutorBatchPrefillOutcome::NotSubmitted(deferred))
9570 }
9571 PlanRuntimeBatchPrefillOutcome::Unsupported => {
9572 Ok(ExecutorBatchPrefillOutcome::Unsupported)
9573 }
9574 }
9575 }
9576
9577 fn metrics_snapshot(&self) -> serde_json::Value {
9578 let pool_status = self
9579 .plan_resources
9580 .dynamic_pool_status()
9581 .ok()
9582 .and_then(|status| serde_json::to_value(status).ok());
9583 let cleanup = serde_json::to_value(self.plan_resources.deferred_cleanup_status()).ok();
9584 let (pending_sequences, active_sequences, pending_prefill_maintenance, executing_prefills) = {
9585 let sequences = self.sequences.lock();
9586 let mut ready = 0;
9587 let mut maintenance = 0;
9588 let mut executing = 0;
9589 for slot in sequences.prefills.values() {
9590 match &*slot.state.lock() {
9591 VNextPrefillSlotState::Deferred {
9592 maintenance: pending,
9593 maintaining,
9594 } => {
9595 if pending.is_some() || *maintaining {
9596 maintenance += 1;
9597 }
9598 }
9599 VNextPrefillSlotState::Ready(_) => ready += 1,
9600 VNextPrefillSlotState::Executing(_) => executing += 1,
9601 VNextPrefillSlotState::Probing | VNextPrefillSlotState::Terminal => {}
9602 }
9603 }
9604 (ready, sequences.active.len(), maintenance, executing)
9605 };
9606 let product_readback = serde_json::json!({
9607 "full_logits_waves": self.metrics.full_logits_readback_waves.load(Ordering::Relaxed),
9608 "greedy_token_waves": self.metrics.greedy_token_readback_waves.load(Ordering::Relaxed),
9609 "greedy_policy_fallback_waves": self.metrics.greedy_policy_fallback_waves.load(Ordering::Relaxed),
9610 "token_mask_residency_eligible": self.io.token_mask_residency_eligible,
9611 "token_mask_upload_participants": self.metrics.token_mask_upload_participants.load(Ordering::Relaxed),
9612 "token_mask_cache_hit_participants": self.metrics.token_mask_cache_hit_participants.load(Ordering::Relaxed),
9613 "sparse_repetition_waves": self.metrics.sparse_repetition_waves.load(Ordering::Relaxed),
9614 "sparse_repetition_participants": self.metrics.sparse_repetition_participants.load(Ordering::Relaxed),
9615 "sparse_repetition_token_ids_uploaded": self.metrics.sparse_repetition_token_ids_uploaded.load(Ordering::Relaxed),
9616 });
9617 let mut snapshot = serde_json::json!({
9618 "schema": "ferrum.runtime-vnext.executor-trace.v1",
9619 "model_id": self.info.model_id.to_string(),
9620 "family_fingerprint": self.family_fingerprint,
9621 "program_fingerprint": self.program_fingerprint,
9622 "resolved_plan_fingerprint": self.resolved_plan.fingerprint(),
9623 "plan_id": self.resolved_plan.execution_plan().payload().plan_id().to_string(),
9624 "plan_hash": self.resolved_plan.execution_plan().plan_hash().to_string(),
9625 "policy_id": self.policy.policy_id(),
9626 "policy_fingerprint": self.policy.fingerprint_str(),
9627 "device_id": self.runtime.descriptor().id.to_string(),
9628 "runtime_fingerprint": self.runtime.descriptor().runtime_implementation_fingerprint,
9629 "maximum_model_tokens": self.maximum_model_tokens,
9630 "runtime_memory_policy": self.policy.memory(),
9631 "runtime_admission_policy": self.policy.admission(),
9632 "pending_sequences": pending_sequences,
9633 "active_sequences": active_sequences,
9634 "staged_prefill_requests": 0,
9635 "staged_prefill_sequences": 0,
9636 "pending_prefill_maintenance": pending_prefill_maintenance,
9637 "executing_prefills": executing_prefills,
9638 "static_bytes": self.static_bytes,
9639 "counters": {
9640 "prefill_operations": self.metrics.prefill_operations.load(Ordering::Relaxed),
9641 "prefill_frontier_narrowings": self.metrics.prefill_frontier_narrowings.load(Ordering::Relaxed),
9642 "decode_operations": self.metrics.decode_operations.load(Ordering::Relaxed),
9643 "prepared_wave_topology": self.metrics.prepared_wave_topology.snapshot(),
9644 "submitted_waves": self.metrics.submitted_waves.load(Ordering::Relaxed),
9645 "completed_waves": self.metrics.completed_waves.load(Ordering::Relaxed),
9646 "failed_waves": self.metrics.failed_waves.load(Ordering::Relaxed),
9647 "reusable_execution": {
9648 "direct_waves": self.metrics.direct_reusable_waves.load(Ordering::Relaxed),
9649 "catalog": self.reusable_execution_catalog_snapshot(),
9650 "direct_segments": self.metrics.direct_reusable_segments.load(Ordering::Relaxed),
9651 "direct_logical_nodes": self.metrics.direct_reusable_logical_nodes.load(Ordering::Relaxed),
9652 "direct_binding_nodes": self.metrics.direct_reusable_binding_nodes.load(Ordering::Relaxed),
9653 "direct_fallbacks": self.metrics.direct_reusable_fallbacks.load(Ordering::Relaxed),
9654 "catalog_misses": self.metrics.reusable_catalog_misses.load(Ordering::Relaxed),
9655 "catalog_epoch_misses": self.metrics.reusable_catalog_epoch_misses.load(Ordering::Relaxed),
9656 "catalog_miss_ledger": self.metrics.reusable_catalog_miss_ledger.lock().snapshot(),
9657 },
9658 "identity_materialization": {
9659 "waves": self.metrics.identity_waves.load(Ordering::Relaxed),
9660 "logical_nodes": self.metrics.identity_logical_nodes.load(Ordering::Relaxed),
9661 "nodes_materialized_before_submit": self.metrics.identity_nodes_materialized_before_submit.load(Ordering::Relaxed),
9662 "full_participant_materializations_before_submit": self.metrics.identity_full_participant_materializations_before_submit.load(Ordering::Relaxed),
9663 },
9664 "definitely_not_submitted_retries": self.metrics.definitely_not_submitted_retries.load(Ordering::Relaxed),
9665 "request_deferrals": self.metrics.request_deferrals.load(Ordering::Relaxed),
9666 "sequence_deferrals": self.metrics.sequence_deferrals.load(Ordering::Relaxed),
9667 "extension_deferrals": self.metrics.extension_deferrals.load(Ordering::Relaxed),
9668 "step_deferrals": self.metrics.step_deferrals.load(Ordering::Relaxed),
9669 "wave_deferrals": self.metrics.wave_deferrals.load(Ordering::Relaxed),
9670 "backing_deferrals": self.metrics.backing_deferrals.load(Ordering::Relaxed),
9671 "uploaded_bytes": self.metrics.uploaded_bytes.load(Ordering::Relaxed),
9672 "readback_bytes": self.metrics.readback_bytes.load(Ordering::Relaxed),
9673 "product_readback": product_readback,
9674 },
9675 "wave_timing": self.metrics.wave_timing.snapshot(),
9676 "wave_timing_by_phase": {
9677 VNextExecutionWaveKind::Prefill.as_str(): self.metrics.prefill_wave_timing.snapshot(),
9678 VNextExecutionWaveKind::Decode.as_str(): self.metrics.decode_wave_timing.snapshot(),
9679 },
9680 "device_timing": self.metrics.device_timing.snapshot(),
9681 "device_timing_by_phase": {
9682 VNextExecutionWaveKind::Prefill.as_str(): self.metrics.prefill_device_timing.snapshot(),
9683 VNextExecutionWaveKind::Decode.as_str(): self.metrics.decode_device_timing.snapshot(),
9684 },
9685 "completion_worker": self.completion_worker.metrics_snapshot(),
9686 "checkpoint_timings": self.reaper.checkpoint_timing_snapshot(),
9687 "dynamic_pools": pool_status,
9688 "deferred_cleanup": cleanup,
9689 "startup_preparation": serde_json::to_value(&*self.startup_preparation.lock())
9690 .unwrap_or_else(|error| serde_json::json!({"state": "serialization_failed", "message": error.to_string()})),
9691 "last_failure": self.metrics.last_failure.lock().clone(),
9692 });
9693 let fields = snapshot
9694 .as_object_mut()
9695 .expect("vNext executor snapshot is an object");
9696 fields.extend(self.prefix_cache_metrics_snapshot());
9700 fields.insert(
9701 "attention_execution_policy".to_owned(),
9702 serde_json::json!(self.policy.attention_execution()),
9703 );
9704 fields.insert(
9705 "numerical_execution".to_owned(),
9706 serde_json::json!(self.resolved_plan.parts().numerical_execution),
9707 );
9708 let numerical = &self.resolved_plan.parts().numerical_execution;
9709 fields.insert(
9710 "kv_storage".to_owned(),
9711 serde_json::json!({
9712 "source": "resolved_model_plan",
9713 "requested": numerical.requested_kv_storage(),
9714 "selected": numerical.selected_kv_storage(),
9715 "numerical_profile": numerical.selected_profile(),
9716 "logical_sequence_state": self.sequence_state_memory,
9717 "logical_bytes_scope": "complete_model_excluding_alignment_workspace_and_checkpoint_copies",
9718 "resident_usage_source": "dynamic_pools",
9719 }),
9720 );
9721 snapshot
9722 }
9723}
9724
9725#[async_trait::async_trait]
9726impl<R: DeviceRuntime> ModelExecutor for VNextModelExecutor<R> {
9727 fn plan_prompt_tail_capture_boundary(&self, chunk: PrefillChunk) -> Option<PrefixCapturePlan> {
9728 self.prompt_tail_boundary(chunk)
9729 }
9730
9731 fn plan_prefix_capture_boundary(
9732 &self,
9733 input: PrefixCaptureBoundary<'_>,
9734 ) -> Option<PrefixCapturePlan> {
9735 self.rendezvous_boundary(input)
9736 }
9737
9738 fn retain_prefix_capture_interest(
9739 &self,
9740 input: PrefixCaptureRequest<'_>,
9741 ) -> Result<Option<Arc<dyn PrefixCaptureLease>>> {
9742 self.arm_prefix_capture(input)
9743 }
9744
9745 fn supports_plan_runtime_prefix_restore(&self) -> bool {
9746 self.prefix_restore_enabled()
9747 }
9748
9749 async fn try_restore_plan_runtime_prefix(
9750 &self,
9751 input: PlanRuntimePrefixRestoreInput<'_>,
9752 ) -> Result<PlanRuntimePrefixRestoreOutcome> {
9753 self.restore_prefix(input).await
9754 }
9755
9756 fn info(&self) -> &ModelInfo {
9757 &self.info
9758 }
9759
9760 async fn prepare_startup(&self) -> Result<()> {
9761 {
9762 let mut state = self.startup_preparation.lock();
9763 match &*state {
9764 VNextStartupPreparationState::Pending => {
9765 *state = VNextStartupPreparationState::Preparing;
9766 }
9767 VNextStartupPreparationState::Ready { .. } => return Ok(()),
9768 VNextStartupPreparationState::Preparing => {
9769 return Err(FerrumError::internal(
9770 "vNext startup preparation is already running",
9771 ));
9772 }
9773 VNextStartupPreparationState::Failed { message } => {
9774 return Err(FerrumError::device(format!(
9775 "vNext startup preparation previously failed: {message}"
9776 )));
9777 }
9778 }
9779 }
9780
9781 let preparation = self
9782 .prepare_reusable_execution_startup()
9783 .await
9784 .and_then(|report| {
9785 self.reset_request_metrics_after_startup()?;
9786 Ok(report)
9787 });
9788 match preparation {
9789 Ok(report) => {
9790 if let Some(capture) = &self.checkpoint_capture {
9791 capture.arm();
9792 }
9793 *self.startup_preparation.lock() = VNextStartupPreparationState::Ready { report };
9794 Ok(())
9795 }
9796 Err(error) => {
9797 *self.startup_preparation.lock() = VNextStartupPreparationState::Failed {
9798 message: error.to_string(),
9799 };
9800 Err(error)
9801 }
9802 }
9803 }
9804
9805 fn execution_resource_authority(&self) -> ExecutionResourceAuthority {
9806 ExecutionResourceAuthority::PlanRuntime
9807 }
9808
9809 fn admission_limits(&self) -> Result<Option<ExecutorAdmissionLimits>> {
9810 ExecutorAdmissionLimits::new(
9811 self.policy.memory().maximum_active_sequences,
9812 self.policy.admission().maximum_scheduled_tokens,
9813 )
9814 .map(Some)
9815 .map_err(|reason| {
9816 FerrumError::internal(format!(
9817 "resolved vNext admission limits violated their typed contract: {reason}"
9818 ))
9819 })
9820 }
9821
9822 fn resolved_model_plan(&self) -> Option<&ResolvedModelPlan> {
9823 Some(&self.resolved_plan)
9824 }
9825
9826 fn plan_runtime_resource_snapshot(&self) -> Result<Option<PlanRuntimeResourceSnapshot>> {
9827 let status = self
9828 .plan_resources
9829 .dynamic_pool_status()
9830 .map_err(|error| FerrumError::internal(error.to_string()))?;
9831 let mut resident_bytes = 0_u64;
9832 let mut free_bytes = 0_u64;
9833 let mut pending_growth_bytes = 0_u64;
9834 let mut quarantined_bytes = 0_u64;
9835 for pool in status.pools() {
9836 resident_bytes = resident_bytes
9837 .checked_add(pool.resident_bytes())
9838 .ok_or_else(|| FerrumError::internal("dynamic resident bytes overflow u64"))?;
9839 free_bytes = free_bytes
9840 .checked_add(pool.free_bytes())
9841 .ok_or_else(|| FerrumError::internal("dynamic free bytes overflow u64"))?;
9842 pending_growth_bytes = pending_growth_bytes
9843 .checked_add(pool.pending_growth_bytes())
9844 .ok_or_else(|| FerrumError::internal("pending growth bytes overflow u64"))?;
9845 quarantined_bytes = quarantined_bytes
9846 .checked_add(pool.quarantined_bytes())
9847 .ok_or_else(|| FerrumError::internal("quarantined bytes overflow u64"))?;
9848 }
9849 PlanRuntimeResourceSnapshot::new(
9850 status.device_capacity_bytes(),
9851 status.effective_device_usable_ceiling_bytes(),
9852 status.process_claimed_bytes(),
9853 status.budget_claimed_bytes(),
9854 self.static_bytes,
9855 resident_bytes,
9856 free_bytes,
9857 pending_growth_bytes,
9858 quarantined_bytes,
9859 )
9860 .map(Some)
9861 }
9862
9863 fn kv_capacity(&self) -> Option<usize> {
9864 Some(self.maximum_model_tokens)
9865 }
9866
9867 fn startup_memory_plan(&self) -> Option<&ferrum_types::StartupMemoryPlan> {
9868 self.startup_memory_plan.as_ref()
9869 }
9870
9871 fn attach_execution_event_sink(&self, sink: Arc<dyn ExecutionEventSink>) {
9872 self.device_timing_mode
9873 .store(sink.device_timing_mode() as u8, Ordering::Release);
9874 *self.event_sink.write() = Some(sink);
9875 }
9876
9877 fn execution_capacity_epochs(&self) -> Result<Option<ExecutorAdmissionEpochs>> {
9878 self.plan_resources
9879 .dynamic_pool_status()
9880 .map(|status| Some(ExecutorAdmissionEpochs::from_capacity(status.epochs())))
9881 .map_err(|error| FerrumError::backend(error.to_string()))
9882 }
9883
9884 fn write_execution_capacity_snapshot(
9885 &self,
9886 availability: &mut Vec<ferrum_interfaces::vnext::CapacityAvailabilityEpoch>,
9887 ) -> Result<Option<ExecutorAdmissionEpochs>> {
9888 self.plan_resources
9889 .write_dynamic_capacity_availability(availability)
9890 .map(|epochs| Some(ExecutorAdmissionEpochs::from_capacity(epochs)))
9891 .map_err(|error| FerrumError::backend(error.to_string()))
9892 }
9893
9894 fn register_execution_capacity_waiter(
9895 &self,
9896 observed: &CapacityWaitCondition,
9897 ) -> Result<Option<ExecutorCapacityWaitRegistration>> {
9898 let registration = self
9899 .plan_resources
9900 .register_capacity_waiter(observed)
9901 .map_err(|error| FerrumError::backend(error.to_string()))?;
9902 Ok(Some(ExecutorCapacityWaitRegistration::new(async move {
9903 registration
9904 .wait_for_change()
9905 .await
9906 .map(ExecutorAdmissionEpochs::from_capacity)
9907 .map_err(|error| FerrumError::backend(error.to_string()))
9908 })))
9909 }
9910
9911 fn try_admit_prefill(
9912 &self,
9913 input: ExecutorPrefillAdmission<'_>,
9914 ) -> Result<ExecutorPrefillAdmissionDecision> {
9915 input.validate()?;
9916 if input.maximum_sequence_tokens < input.input_tokens.len()
9917 || input.maximum_sequence_tokens > self.maximum_model_tokens
9918 {
9919 return Err(FerrumError::request_validation(format!(
9920 "request sequence ceiling {} must cover prompt {} and not exceed {}",
9921 input.maximum_sequence_tokens,
9922 input.input_tokens.len(),
9923 self.maximum_model_tokens
9924 )));
9925 }
9926 let tokens = input
9927 .input_tokens
9928 .iter()
9929 .map(|token| token.get())
9930 .collect::<Vec<_>>();
9931 let span =
9932 TokenSpanWork::from_token_ids_with_fit(&tokens, 0..1, input.maximum_sequence_tokens)
9933 .map_err(|error| FerrumError::backend(error.to_string()))?;
9934 let work = ResourceWorkShape::single(span.clone())
9935 .map_err(|error| FerrumError::backend(error.to_string()))?;
9936 let slot = self
9937 .sequences
9938 .lock()
9939 .begin_prefill_probe(input.request_id, &work)?;
9940 let resolution = match self.resolve_prefill_probe(
9941 input.request_id,
9942 input.request_origin,
9943 input.maximum_sequence_tokens,
9944 tokens,
9945 input.product_prompt_tokens,
9946 input.replayed_output_tokens,
9947 work,
9948 ) {
9949 Ok(resolution) => resolution,
9950 Err(error) => {
9951 self.fail_prefill_probe(&slot);
9952 return Err(error);
9953 }
9954 };
9955 self.publish_prefill_probe(&slot, resolution)
9956 }
9957
9958 fn cancel_prefill_admission(&self, request_id: &RequestId) -> bool {
9959 self.sequences.lock().cancel_prefill(request_id)
9960 }
9961
9962 fn write_execution_capacity_release_sources(
9963 &self,
9964 preemption: &ExecutorExecutionCapacityPreemption,
9965 sources: &mut Vec<CapacityAvailabilitySource>,
9966 ) -> Result<bool> {
9967 self.sequences
9968 .lock()
9969 .write_execution_capacity_release_sources(preemption, sources)
9970 }
9971
9972 async fn preempt_execution_capacity(
9973 &self,
9974 preemption: ExecutorExecutionCapacityPreemption,
9975 ) -> Result<ExecutorExecutionCapacityPreemptionReceipt> {
9976 let authority = self
9977 .sequences
9978 .lock()
9979 .preempt_execution_capacity(&preemption)?;
9980 Ok(ExecutorExecutionCapacityPreemptionReceipt::new(
9981 preemption.request_id().clone(),
9982 preemption.cache_id().to_string(),
9983 authority,
9984 ))
9985 }
9986
9987 fn maintain_prefill_backing(
9988 &self,
9989 request_id: &RequestId,
9990 ) -> Result<ExecutorPrefillMaintenanceOutcome> {
9991 let (slot, pending) = {
9992 let sequences = self.sequences.lock();
9993 let Some(slot) = sequences.prefills.get(request_id).cloned() else {
9994 return Ok(ExecutorPrefillMaintenanceOutcome::NoLongerPending);
9995 };
9996 let mut state = slot.state.lock();
9997 let pending = match &mut *state {
9998 VNextPrefillSlotState::Deferred {
9999 maintenance,
10000 maintaining,
10001 ..
10002 } if !*maintaining => {
10003 let Some(pending) = maintenance.take() else {
10004 return Ok(ExecutorPrefillMaintenanceOutcome::NoLongerPending);
10005 };
10006 *maintaining = true;
10007 pending
10008 }
10009 _ => return Ok(ExecutorPrefillMaintenanceOutcome::NoLongerPending),
10010 };
10011 drop(state);
10012 (slot, pending)
10013 };
10014
10015 let outcome = match &pending {
10016 PendingPrefillMaintenance::Logical(deferred) => self
10017 .plan_resources
10018 .maintain_for_admission_deferred(deferred),
10019 PendingPrefillMaintenance::Backing(deferred) => deferred.maintain(),
10020 };
10021 drop(pending);
10022
10023 let mut sequences = self.sequences.lock();
10024 let current = sequences
10025 .prefills
10026 .get(request_id)
10027 .is_some_and(|current| Arc::ptr_eq(current, &slot));
10028 if !current {
10029 return Ok(ExecutorPrefillMaintenanceOutcome::NoLongerPending);
10030 }
10031 let mut state = slot.state.lock();
10032 let cancelled = slot.cancelled.load(Ordering::Acquire);
10033 let owns_maintenance = matches!(
10034 &*state,
10035 VNextPrefillSlotState::Deferred {
10036 maintenance: None,
10037 maintaining: true,
10038 ..
10039 }
10040 );
10041 if cancelled || outcome.is_err() || !owns_maintenance {
10042 let prior = std::mem::replace(&mut *state, VNextPrefillSlotState::Terminal);
10043 drop(state);
10044 sequences.prefills.remove(request_id);
10045 drop(sequences);
10046 prior.abort();
10047 if cancelled {
10048 return Ok(ExecutorPrefillMaintenanceOutcome::NoLongerPending);
10049 }
10050 return match outcome {
10051 Err(error) => Err(FerrumError::backend(error.to_string())),
10052 Ok(_) => Err(FerrumError::internal(format!(
10053 "vNext prefill maintenance for `{request_id}` lost its slot state"
10054 ))),
10055 };
10056 }
10057 let VNextPrefillSlotState::Deferred { maintaining, .. } = &mut *state else {
10058 unreachable!("maintenance ownership was checked")
10059 };
10060 *maintaining = false;
10061 drop(state);
10062 drop(sequences);
10063 let outcome = outcome.expect("maintenance error was handled above");
10064 match outcome {
10065 DynamicDeferredMaintenanceOutcome::RetryAdmission { current_epochs } => {
10066 Ok(ExecutorPrefillMaintenanceOutcome::RetryAdmission {
10067 current: ExecutorAdmissionEpochs::from_capacity(current_epochs),
10068 })
10069 }
10070 DynamicDeferredMaintenanceOutcome::WaitForRelease {
10071 current_epochs,
10072 wait_condition,
10073 pressure,
10074 maintenance_boundary: _,
10075 } => {
10076 if self.evict_prefix_for_wait(&wait_condition) {
10077 return Ok(ExecutorPrefillMaintenanceOutcome::RetryAdmission {
10078 current: self.current_execution_capacity_epochs()?,
10079 });
10080 }
10081 Ok(ExecutorPrefillMaintenanceOutcome::WaitForRelease {
10082 current: ExecutorAdmissionEpochs::from_capacity(current_epochs),
10083 wait_condition,
10084 pressure,
10085 })
10086 }
10087 DynamicDeferredMaintenanceOutcome::Maintained(receipt) => {
10088 let allocated_bytes = receipt
10089 .growths()
10090 .iter()
10091 .try_fold(0_u64, |total, growth| {
10092 total.checked_add(growth.chunk_bytes())
10093 })
10094 .ok_or_else(|| {
10095 FerrumError::internal("vNext prefill maintenance byte count overflow")
10096 })?;
10097 let rebalance = receipt.rebalance().cloned();
10098 let (pools_reclaimed, chunks_reclaimed, reclaimed_bytes) =
10099 rebalance.as_ref().map_or((0, 0, 0), |rebalance| {
10100 (
10101 rebalance.pools().len(),
10102 rebalance.reclaimed_chunks(),
10103 rebalance.reclaimed_bytes(),
10104 )
10105 });
10106 Ok(ExecutorPrefillMaintenanceOutcome::Maintained {
10107 current: self.current_execution_capacity_epochs()?,
10108 pools_grown: receipt.growths().len(),
10109 allocated_bytes,
10110 pools_reclaimed,
10111 chunks_reclaimed,
10112 reclaimed_bytes,
10113 rebalance,
10114 })
10115 }
10116 }
10117 }
10118
10119 async fn prefill(&self, input: &PrefillInput) -> Result<PrefillOutput> {
10120 match self.execute_legacy_prefill_with_capacity(input).await? {
10121 ExecutorPrefillOutcome::Completed(completion) => {
10122 let (output, _, _, _) = completion.into_parts();
10123 Ok(output)
10124 }
10125 ExecutorPrefillOutcome::Deferred(deferred) => {
10126 Err(Self::execution_deferral_error(&deferred))
10127 }
10128 }
10129 }
10130
10131 async fn prefill_with_capacity(&self, input: &PrefillInput) -> Result<ExecutorPrefillOutcome> {
10132 self.execute_legacy_prefill_with_capacity(input).await
10133 }
10134
10135 async fn batch_prefill(&self, inputs: &[PrefillInput]) -> Result<Vec<PrefillOutput>> {
10136 match self
10137 .execute_legacy_prefill_batch_with_capacity(inputs)
10138 .await?
10139 {
10140 ExecutorBatchPrefillOutcome::Completed(completions) => completions
10141 .into_iter()
10142 .map(|completion| {
10143 let (output, _, _, _) = completion.into_parts();
10144 Ok(output)
10145 })
10146 .collect(),
10147 ExecutorBatchPrefillOutcome::NotSubmitted(deferred) => {
10148 Err(Self::execution_deferral_error(&deferred))
10149 }
10150 ExecutorBatchPrefillOutcome::Unsupported => Err(FerrumError::internal(
10151 "vNext batch prefill returned its own unsupported marker",
10152 )),
10153 }
10154 }
10155
10156 async fn batch_prefill_with_capacity(
10157 &self,
10158 inputs: &[PrefillInput],
10159 ) -> Result<ExecutorBatchPrefillOutcome> {
10160 self.execute_legacy_prefill_batch_with_capacity(inputs)
10161 .await
10162 }
10163
10164 async fn plan_runtime_prefill_with_capacity(
10165 &self,
10166 input: &PlanRuntimePrefillInput,
10167 ) -> Result<PlanRuntimePrefillOutcome> {
10168 self.execute_plan_runtime_prefill_with_capacity(input).await
10169 }
10170
10171 async fn plan_runtime_batch_prefill_with_capacity(
10172 &self,
10173 inputs: &[PlanRuntimePrefillInput],
10174 ) -> Result<PlanRuntimeBatchPrefillOutcome> {
10175 self.execute_plan_runtime_prefill_batch_with_capacity(inputs)
10176 .await
10177 }
10178
10179 async fn decode(&self, input: &DecodeInput) -> Result<DecodeOutput> {
10180 let started = Instant::now();
10181 if input.batch_size() != 1 {
10182 return Err(FerrumError::unsupported(
10183 "vNext decode currently requires one sequence per typed submission wave",
10184 ));
10185 }
10186 let cache_id = input.kv_cache.cache_id();
10187 let sequence = self.sequence_for_cache(&cache_id)?;
10188 if input
10189 .request_id
10190 .as_ref()
10191 .is_some_and(|request_id| request_id != sequence.request_id())
10192 {
10193 return Err(FerrumError::request_validation(
10194 "vNext decode request identity differs from its cache owner",
10195 ));
10196 }
10197 let _operation = sequence.operation.lock().await;
10198 if !sequence.active.load(Ordering::Acquire) {
10199 return Err(FerrumError::cancelled(format!(
10200 "vNext cache `{cache_id}` is no longer active"
10201 )));
10202 }
10203 let next = common::tensor_to_tokens(&input.input_ids)?;
10204 let [next_token] = next.as_slice() else {
10205 return Err(FerrumError::request_validation(
10206 "vNext decode requires exactly one input token",
10207 ));
10208 };
10209 let (tokens, previous_len) = {
10210 let current = sequence.tokens.lock();
10211 let previous_len = current.len();
10212 if previous_len >= sequence.maximum_tokens {
10213 return Err(FerrumError::request_validation(format!(
10214 "vNext sequence reached its {} token ceiling",
10215 sequence.maximum_tokens
10216 )));
10217 }
10218 let mut tokens = current.clone();
10219 tokens.push(*next_token);
10220 (tokens, previous_len)
10221 };
10222 let extension_span = TokenSpanWork::from_token_ids(&tokens, 0..tokens.len())
10223 .map_err(|error| FerrumError::backend(error.to_string()))?;
10224 let extension = ResourceWorkShape::single(extension_span)
10225 .map_err(|error| FerrumError::backend(error.to_string()))?;
10226 if let Err(error) = self.extend_sequence(&sequence, extension) {
10227 if DecodeFailureDisposition::from_error(&error)
10228 == DecodeFailureDisposition::AbortSequence
10229 {
10230 self.sequences.lock().active.remove(&cache_id);
10231 sequence.abort();
10232 }
10233 return Err(error);
10234 }
10235 let step_span = TokenSpanWork::from_token_ids(&tokens, previous_len..tokens.len())
10236 .map_err(|error| FerrumError::backend(error.to_string()))?;
10237 let sampling_output = match self
10238 .execute_step(&sequence, &tokens, step_span, &input.logits_policy)
10239 .await
10240 {
10241 Ok(sampling_output) => sampling_output,
10242 Err(error) => {
10243 if DecodeFailureDisposition::from_error(&error)
10244 == DecodeFailureDisposition::AbortSequence
10245 {
10246 self.sequences.lock().active.remove(&cache_id);
10247 sequence.abort();
10248 }
10249 return Err(error);
10250 }
10251 };
10252 sampling_output.validate_for_policy(&input.logits_policy, self.io.output_elements)?;
10253 *sequence.tokens.lock() = tokens;
10254 self.metrics
10255 .decode_operations
10256 .fetch_add(1, Ordering::Relaxed);
10257 self.metrics.total_decode_us.fetch_add(
10258 started.elapsed().as_micros().min(u64::MAX as u128) as u64,
10259 Ordering::Relaxed,
10260 );
10261 let legacy_values = match sampling_output {
10262 ExecutorSamplingOutput::FullLogits(logits) => logits,
10263 ExecutorSamplingOutput::GreedyToken(token) => vec![token.get() as f32],
10264 };
10265 let logits = self.decode_tensor(legacy_values)?;
10266 let cache = self.cache_handle(&sequence, previous_len + 1);
10267 Ok(DecodeOutput::new(logits, cache))
10268 }
10269
10270 async fn batch_decode(&self, inputs: &[DecodeInput]) -> Result<Vec<DecodeOutput>> {
10271 match self.execute_legacy_decode_batch(inputs).await? {
10272 ExecutorBatchDecodeOutcome::Completed(outputs) => Ok(outputs),
10273 ExecutorBatchDecodeOutcome::Deferred(deferred) => {
10274 Err(Self::execution_deferral_error(&deferred))
10275 }
10276 }
10277 }
10278
10279 async fn batch_decode_with_capacity(
10280 &self,
10281 inputs: &[DecodeInput],
10282 ) -> Result<ExecutorBatchDecodeOutcome> {
10283 self.execute_legacy_decode_batch(inputs).await
10284 }
10285
10286 async fn plan_runtime_batch_decode_with_capacity(
10287 &self,
10288 inputs: &[PlanRuntimeDecodeInput],
10289 ) -> Result<PlanRuntimeBatchDecodeOutcome> {
10290 self.execute_plan_runtime_decode_batch(inputs).await
10291 }
10292
10293 fn discard_plan_runtime_prefill(&self, authority: PlanRuntimePrefillAuthority) -> Result<()> {
10294 let handle = authority
10295 .kv_cache()
10296 .as_any()
10297 .downcast_ref::<VNextKvCacheHandle<R>>()
10298 .ok_or_else(|| {
10299 FerrumError::request_validation(
10300 "vNext prefill discard received a foreign cache handle",
10301 )
10302 })?;
10303 let sequence = handle.sequence.upgrade().ok_or_else(|| {
10304 FerrumError::not_found("vNext prefill discard sequence is no longer retained")
10305 })?;
10306 if authority.request_id() != sequence.request_id()
10307 || authority.committed_tokens() != handle.num_tokens()
10308 || handle.cache_id != sequence.cache_id
10309 {
10310 return Err(FerrumError::request_validation(
10311 "vNext prefill discard authority does not match its exact sequence",
10312 ));
10313 }
10314 if !self.sequences.lock().discard_exact_sequence(&sequence) {
10315 return Err(FerrumError::not_found(format!(
10316 "vNext prefill discard authority `{}` is no longer current",
10317 sequence.cache_id
10318 )));
10319 }
10320 Ok(())
10321 }
10322
10323 fn release_cache(&self, cache_id: &str) {
10324 if let Some(sequence) = self.sequences.lock().active.remove(cache_id) {
10325 sequence.abort();
10326 }
10327 }
10328
10329 async fn complete_cache(&self, completion: ExecutorSequenceCompletion) -> Result<()> {
10330 let sequence = self
10331 .sequences
10332 .lock()
10333 .active
10334 .remove(completion.cache_id())
10335 .ok_or_else(|| {
10336 FerrumError::not_found(format!(
10337 "vNext completion cache `{}` is not active",
10338 completion.cache_id()
10339 ))
10340 })?;
10341 let mut pending = PendingSequenceCompletion {
10345 sequence: &sequence,
10346 operation: None,
10347 completed: false,
10348 };
10349 pending.operation = Some(sequence.operation.lock().await);
10350 validate_sequence_completion_accounting(
10351 sequence.request_id(),
10352 sequence.product_prompt_tokens,
10353 sequence.replayed_output_tokens,
10354 &completion,
10355 )?;
10356 self.retain_completed_sequence_boundary(&sequence).await?;
10357 sequence.complete(&completion)?;
10358 pending.completed = true;
10359 Ok(())
10360 }
10361
10362 fn capabilities(&self) -> ExecutorCapabilities {
10363 ExecutorCapabilities {
10364 max_batch_size: self.policy.memory().maximum_active_sequences as usize,
10365 max_sequence_length: self.maximum_model_tokens,
10366 attention_mechanisms: vec![AttentionType::GroupedQuery, AttentionType::Paged],
10367 supports_dynamic_batching: true,
10368 supports_continuous_batching: true,
10369 supports_speculative_decoding: false,
10370 supports_tensor_parallelism: false,
10371 supports_pipeline_parallelism: false,
10372 supported_dtypes: vec![self.info.dtype],
10373 supported_devices: vec![self.info.device.clone()],
10374 memory_requirements: MemoryRequirements {
10375 parameter_memory: self.static_bytes,
10376 activation_memory_per_token: self.info.hidden_size * self.info.dtype.size_bytes(),
10377 kv_cache_memory_per_token: 0,
10378 typed_sequence_state: Some(self.sequence_state_memory),
10379 overhead_memory: self.policy.memory().reserve_bytes,
10380 },
10381 }
10382 }
10383
10384 fn status(&self) -> ExecutorStatus {
10385 let prefill_operations = self.metrics.prefill_operations.load(Ordering::Relaxed);
10386 let decode_operations = self.metrics.decode_operations.load(Ordering::Relaxed);
10387 let pool_status = self.plan_resources.dynamic_pool_status().ok();
10388 let allocated_bytes = reported_allocated_bytes(
10389 pool_status
10390 .as_ref()
10391 .map(|status| status.budget_claimed_bytes()),
10392 self.static_bytes,
10393 );
10394 let used_dynamic = pool_status
10395 .as_ref()
10396 .map(|status| {
10397 status.pools().iter().fold(0_u64, |total, pool| {
10398 total.saturating_add(pool.resident_bytes().saturating_sub(pool.free_bytes()))
10399 })
10400 })
10401 .unwrap_or(0);
10402 let used_bytes = self.static_bytes.saturating_add(used_dynamic);
10403 let capacity = self.policy.memory().capacity_bytes;
10404 let startup = self.startup_preparation.lock();
10405 let startup_ready = startup.is_ready();
10406 let startup_failed = matches!(&*startup, VNextStartupPreparationState::Failed { .. });
10407 drop(startup);
10408 ExecutorStatus {
10409 state: if startup_failed {
10410 ExecutorState::Error
10411 } else if !startup_ready {
10412 ExecutorState::Initializing
10413 } else if self.sequences.lock().total_len() == 0 {
10414 ExecutorState::Ready
10415 } else {
10416 ExecutorState::Busy
10417 },
10418 is_ready: startup_ready && !self.plan_resources.is_closing(),
10419 current_batch_size: self.sequences.lock().active.len(),
10420 prefill_operations,
10421 decode_operations,
10422 avg_prefill_time_ms: VNextExecutorMetrics::average_ms(
10423 self.metrics.total_prefill_us.load(Ordering::Relaxed),
10424 prefill_operations,
10425 ),
10426 avg_decode_time_ms: VNextExecutorMetrics::average_ms(
10427 self.metrics.total_decode_us.load(Ordering::Relaxed),
10428 decode_operations,
10429 ),
10430 memory_usage: ExecutorMemoryUsage {
10431 allocated_bytes: usize::try_from(allocated_bytes).unwrap_or(usize::MAX),
10432 used_bytes: usize::try_from(used_bytes).unwrap_or(usize::MAX),
10433 peak_bytes: usize::try_from(allocated_bytes).unwrap_or(usize::MAX),
10434 utilization_percent: if capacity == 0 {
10435 0.0
10436 } else {
10437 used_bytes as f32 / capacity as f32 * 100.0
10438 },
10439 },
10440 last_operation: Some(Instant::now()),
10441 }
10442 }
10443
10444 fn cache_metrics_snapshot(&self) -> Option<serde_json::Value> {
10445 Some(self.metrics_snapshot())
10446 }
10447
10448 fn execution_attribution_snapshot(&self) -> Option<serde_json::Value> {
10449 self.static_provider_attribution
10450 .as_ref()
10451 .and_then(|witness| serde_json::to_value(witness).ok())
10452 }
10453}
10454
10455#[cfg(test)]
10456mod tests {
10457 use std::collections::{BTreeMap, BTreeSet};
10458 use std::time::Duration;
10459
10460 use super::{
10461 apply_teacher_forced_decision, bounded_wall_anchor, budget_reusable_decode_seed_prefill,
10462 decode_output_width, decode_selected_token, is_language_masked_argmax_operation,
10463 is_language_token_embedding_operation, journal_clock_anchor_required,
10464 nonterminal_completion_message, normalized_product_token_mask,
10465 padded_repetition_token_id_bytes, product_output_mode_for_policies,
10466 product_repetition_input, reported_allocated_bytes, resolve_reusable_execution_policy,
10467 resolve_runtime_attention_authority, resolved_sequence_fit_policy,
10468 reusable_catalog_lookup_allowed, reusable_executable_inventory_matches,
10469 reusable_execution_program_catalog_is_usable, reusable_execution_requires_eager_fallback,
10470 reusable_program_identity_required, reusable_startup_case_budget_violation,
10471 submission_execution_policy_for_timing, validate_sequence_completion_accounting,
10472 AdmissionFitPolicy, DecodeFailureDisposition, FerrumError, SequenceFitPolicy,
10473 VNextDeviceTimingMetrics, VNextExecutionWaveKind, VNextPhysicalSpanTimingMetrics,
10474 VNextPreparedWaveTopologyMetrics, VNextProductOutputMode, VNextProductTokenMaskContent,
10475 VNextProductTokenMaskResidency, VNextProductTokenMaskResidencyTransaction,
10476 VNextProductTokenMaskSlotIdentity, VNextProductTokenMaskSlotTarget,
10477 VNextReusableExecutionCatalogMissKey, VNextReusableExecutionCatalogMissLedger,
10478 VNextReusableExecutionCatalogMissReason, VNextReusableExecutionDescriptor,
10479 VNextReusableExecutionMetrics, VNextReusableExecutionStartupPlan,
10480 VNextTeacherForcedDecision, VNextWaveTimingMetrics, VNextWaveTimingSink,
10481 MAX_PRODUCT_TOKEN_MASK_SLOT_CACHE_ENTRIES, MAX_REUSABLE_EXECUTION_CATALOG_MISS_KEYS,
10482 };
10483 use ferrum_interfaces::model_executor::{
10484 ExecutorSamplingOutput, ExecutorSequenceCompletion, GreedyRepetitionPenalty,
10485 LogitsReturnPolicy, PrefillChunk, TokenSelectionMask,
10486 };
10487 use ferrum_interfaces::vnext::{
10488 CompletionReadbackBatchObservation, DeviceComputePathRequirement, DeviceExecutionInterval,
10489 DeviceExecutionIntervalKind, DeviceExecutionSpanKind, DeviceReusableExecutionObservation,
10490 DeviceReusableExecutionPlan, DeviceReusableExecutionPreparation,
10491 DeviceReusableExecutionTopologyFingerprint, DeviceSubmissionExecutionSpan,
10492 DeviceSubmissionExecutionTiming, DeviceSubmissionTimingSink, DeviceTimingMeasurement,
10493 DeviceTimingMode, StepResourceAdmissionProfilePhase, DENSE_SWIGLU_OPERATION_ID,
10494 LAST_TOKEN_MASKED_ARGMAX_F32_OPERATION_ID, LAST_TOKEN_MASKED_ARGMAX_OPERATION_ID,
10495 TOKEN_EMBEDDING_F32_MASTER_OPERATION_ID, TOKEN_EMBEDDING_OPERATION_ID,
10496 };
10497 use ferrum_types::{
10498 AttentionExecutionPolicy, RequestId, ReusableExecutionCaptureConfig, TokenId,
10499 };
10500
10501 fn resolve_test_reusable_startup_plan(
10502 maximum_active_sequences: u32,
10503 maximum_scheduled_tokens: u64,
10504 maximum_model_tokens: usize,
10505 prefill_chunks: &[PrefillChunk],
10506 execution_node_count: usize,
10507 capture_config: &ReusableExecutionCaptureConfig,
10508 ) -> ferrum_types::Result<VNextReusableExecutionStartupPlan> {
10509 let resolution = resolve_reusable_execution_policy(
10510 maximum_active_sequences,
10511 maximum_scheduled_tokens,
10512 maximum_model_tokens,
10513 prefill_chunks,
10514 capture_config,
10515 true,
10516 )?;
10517 let program_policy = resolution
10518 .policy
10519 .program_policy()
10520 .cloned()
10521 .ok_or_else(|| FerrumError::internal("test capture policy is missing"))?;
10522 let maximum_device_executables = u64::try_from(
10523 execution_node_count
10524 .checked_mul(program_policy.programs().len())
10525 .ok_or_else(|| FerrumError::internal("test executable capacity overflowed"))?,
10526 )
10527 .map_err(|_| FerrumError::internal("test executable capacity exceeds u64"))?;
10528 VNextReusableExecutionStartupPlan::resolve(
10529 &program_policy,
10530 resolution
10531 .capture
10532 .ok_or_else(|| FerrumError::internal("test capture resolution is missing"))?,
10533 maximum_model_tokens,
10534 prefill_chunks,
10535 maximum_device_executables,
10536 )
10537 }
10538
10539 #[test]
10540 fn language_io_resolution_accepts_legacy_and_fp32_master_contracts_only() {
10541 for operation_id in [
10542 TOKEN_EMBEDDING_OPERATION_ID,
10543 TOKEN_EMBEDDING_F32_MASTER_OPERATION_ID,
10544 ] {
10545 assert!(is_language_token_embedding_operation(operation_id));
10546 }
10547 for operation_id in [
10548 LAST_TOKEN_MASKED_ARGMAX_OPERATION_ID,
10549 LAST_TOKEN_MASKED_ARGMAX_F32_OPERATION_ID,
10550 ] {
10551 assert!(is_language_masked_argmax_operation(operation_id));
10552 }
10553 assert!(!is_language_token_embedding_operation(
10554 DENSE_SWIGLU_OPERATION_ID
10555 ));
10556 assert!(!is_language_masked_argmax_operation(
10557 DENSE_SWIGLU_OPERATION_ID
10558 ));
10559 }
10560
10561 #[test]
10562 fn runtime_attention_authority_rejects_plan_provider_drift() {
10563 assert_eq!(
10564 resolve_runtime_attention_authority(
10565 AttentionExecutionPolicy::Auto,
10566 true,
10567 AttentionExecutionPolicy::NativeAdaptive,
10568 )
10569 .unwrap(),
10570 AttentionExecutionPolicy::NativeAdaptive
10571 );
10572 assert!(resolve_runtime_attention_authority(
10573 AttentionExecutionPolicy::Auto,
10574 true,
10575 AttentionExecutionPolicy::Portable,
10576 )
10577 .is_err());
10578 assert!(resolve_runtime_attention_authority(
10579 AttentionExecutionPolicy::Portable,
10580 false,
10581 AttentionExecutionPolicy::Auto,
10582 )
10583 .is_err());
10584 }
10585
10586 #[test]
10587 fn verification_timing_selects_typed_eager_submission_without_owning_other_paths() {
10588 assert_eq!(
10589 submission_execution_policy_for_timing(DeviceTimingMode::Verification).compute_path(),
10590 DeviceComputePathRequirement::EagerOnly
10591 );
10592 for timing_mode in [
10593 DeviceTimingMode::Off,
10594 DeviceTimingMode::Completion,
10595 DeviceTimingMode::Replay,
10596 DeviceTimingMode::Kernel,
10597 ] {
10598 assert_eq!(
10599 submission_execution_policy_for_timing(timing_mode).compute_path(),
10600 DeviceComputePathRequirement::Adaptive
10601 );
10602 }
10603 }
10604
10605 #[test]
10606 fn reusable_catalog_lookup_requires_a_startup_plan_and_one_direct_attempt() {
10607 assert!(reusable_catalog_lookup_allowed(true, true, false));
10608 assert!(!reusable_catalog_lookup_allowed(false, true, false));
10609 assert!(!reusable_catalog_lookup_allowed(true, false, false));
10610 assert!(!reusable_catalog_lookup_allowed(true, true, true));
10611 }
10612
10613 #[test]
10614 fn reusable_program_identity_is_recorded_before_catalog_installation() {
10615 for timing_mode in [DeviceTimingMode::Kernel, DeviceTimingMode::Verification] {
10616 assert!(reusable_program_identity_required(
10617 true,
10618 false,
10619 timing_mode.direct_reusable_execution_allowed(),
10620 false,
10621 ));
10622 assert!(!reusable_program_identity_required(
10623 true,
10624 true,
10625 timing_mode.direct_reusable_execution_allowed(),
10626 false,
10627 ));
10628 }
10629 for timing_mode in [
10630 DeviceTimingMode::Off,
10631 DeviceTimingMode::Completion,
10632 DeviceTimingMode::Replay,
10633 ] {
10634 assert_eq!(
10635 reusable_program_identity_required(
10636 true,
10637 true,
10638 timing_mode.direct_reusable_execution_allowed(),
10639 false,
10640 ),
10641 reusable_catalog_lookup_allowed(
10642 true,
10643 timing_mode.direct_reusable_execution_allowed(),
10644 false,
10645 ),
10646 );
10647 }
10648 assert!(reusable_program_identity_required(true, false, false, true));
10649 assert!(!reusable_program_identity_required(
10650 false, false, true, false
10651 ));
10652
10653 assert!(reusable_program_identity_required(true, true, true, false));
10654 assert!(!reusable_program_identity_required(true, true, true, true));
10655 }
10656
10657 #[test]
10658 fn reusable_startup_case_budget_allows_observed_many_to_one_program_identity() {
10659 let requested_cases = BTreeSet::from(["prefill-a", "prefill-b"]);
10660 let shared_program = BTreeSet::from(["physical-program"]);
10661 let observations = BTreeMap::from([
10662 ("prefill-a", shared_program.clone()),
10663 ("prefill-b", shared_program),
10664 ]);
10665 assert!(
10666 reusable_startup_case_budget_violation(&requested_cases, &observations).is_none(),
10667 "each logical case actually observed the same stable physical program"
10668 );
10669
10670 let invalid = BTreeMap::from([(
10671 "prefill-a",
10672 BTreeSet::from(["physical-program-a", "physical-program-b"]),
10673 )]);
10674 assert!(reusable_startup_case_budget_violation(&requested_cases, &invalid).is_some());
10675 }
10676
10677 #[test]
10678 fn nonterminal_completion_message_preserves_typed_failure_class() {
10679 assert_eq!(
10680 nonterminal_completion_message(
10681 &CompletionReadbackBatchObservation::ObservationPanicked
10682 ),
10683 "vNext completion did not reach a quiescent terminal: ObservationPanicked"
10684 );
10685 }
10686
10687 #[test]
10688 fn reusable_execution_metrics_aggregate_typed_preparation_outcomes() {
10689 let mut observation = DeviceReusableExecutionObservation::default();
10690 observation.observe_candidate_segment();
10691 observation.observe_captured_segment();
10692 observation.observe_uploaded_segment();
10693 observation.observe_cache_hit_segment();
10694 observation.observe_cached_rejected_segment();
10695 observation.observe_capture_rejection();
10696 observation.observe_quiescence_deferred_segment();
10697 observation.observe_capacity_deferred_segment();
10698 observation.observe_outside_preparation_segment();
10699 observation.observe_evicted_segment();
10700 observation.observe_replayed_segment(4);
10701 observation.observe_eager_command();
10702
10703 let metrics = VNextReusableExecutionMetrics::default();
10704 metrics.record(observation);
10705 metrics.record(observation);
10706 let snapshot = metrics.snapshot();
10707
10708 for field in [
10709 "candidate_segments",
10710 "captured_segments",
10711 "uploaded_segments",
10712 "cache_hit_segments",
10713 "cached_rejected_segments",
10714 "capture_rejected_segments",
10715 "quiescence_deferred_segments",
10716 "capacity_deferred_segments",
10717 "outside_preparation_segments",
10718 "evicted_segments",
10719 "replayed_segments",
10720 "eager_commands",
10721 ] {
10722 assert_eq!(snapshot[field], 2, "counter {field} must aggregate");
10723 }
10724 assert_eq!(snapshot["replayed_commands"], 8);
10725 }
10726
10727 #[test]
10728 fn reusable_catalog_miss_ledger_is_bounded_and_preserves_overflow_reason() {
10729 let mut ledger = VNextReusableExecutionCatalogMissLedger::default();
10730 for width in 1..=(MAX_REUSABLE_EXECUTION_CATALOG_MISS_KEYS + 1) {
10731 ledger.record(VNextReusableExecutionCatalogMissKey {
10732 immediate_sequences: u32::try_from(width).unwrap(),
10733 immediate_tokens: u64::try_from(width).unwrap(),
10734 immediate_pages: 1,
10735 topology_fingerprint: DeviceReusableExecutionTopologyFingerprint::static_program(),
10736 reason: VNextReusableExecutionCatalogMissReason::ProgramAbsent,
10737 });
10738 }
10739 ledger.record(VNextReusableExecutionCatalogMissKey {
10740 immediate_sequences: 1,
10741 immediate_tokens: 1,
10742 immediate_pages: 1,
10743 topology_fingerprint: DeviceReusableExecutionTopologyFingerprint::static_program(),
10744 reason: VNextReusableExecutionCatalogMissReason::ProgramAbsent,
10745 });
10746
10747 let snapshot = ledger.snapshot();
10748 assert_eq!(snapshot.maximum_distinct_keys, 64);
10749 assert_eq!(snapshot.distinct_keys, 64);
10750 assert_eq!(snapshot.rows[0].count, 2);
10751 assert_eq!(snapshot.overflow.len(), 1);
10752 assert_eq!(
10753 snapshot.overflow[0].reason,
10754 VNextReusableExecutionCatalogMissReason::ProgramAbsent
10755 );
10756 assert_eq!(snapshot.overflow[0].count, 1);
10757
10758 ledger.reset();
10759 let empty = ledger.snapshot();
10760 assert_eq!(empty.distinct_keys, 0);
10761 assert!(empty.rows.is_empty());
10762 assert!(empty.overflow.is_empty());
10763 }
10764
10765 #[test]
10766 fn wave_timing_sink_attributes_replay_to_aggregate_and_exact_phase() {
10767 let aggregate = VNextWaveTimingMetrics::default();
10768 let decode = VNextWaveTimingMetrics::default();
10769 let sink = VNextWaveTimingSink {
10770 aggregate: &aggregate,
10771 phase: &decode,
10772 };
10773 let mut observation = DeviceReusableExecutionObservation::default();
10774 observation.observe_candidate_segment();
10775 observation.observe_replayed_segment(3);
10776
10777 sink.record_reusable_execution(observation);
10778
10779 assert_eq!(
10780 aggregate.reusable_execution.snapshot()["candidate_segments"],
10781 1
10782 );
10783 assert_eq!(
10784 decode.reusable_execution.snapshot()["candidate_segments"],
10785 1
10786 );
10787 assert_eq!(VNextExecutionWaveKind::Prefill.as_str(), "prefill");
10788 assert_eq!(VNextExecutionWaveKind::Decode.as_str(), "decode");
10789 }
10790
10791 #[test]
10792 fn prepared_wave_topology_metrics_separate_owners_from_node_projections() {
10793 let metrics = VNextPreparedWaveTopologyMetrics::default();
10794
10795 metrics.record_counts(131, 1, 131, 1);
10796 metrics.record_counts(131, 4, 524, 1);
10797 let snapshot = metrics.snapshot();
10798
10799 assert_eq!(snapshot["wave_authorities"], 2);
10800 assert_eq!(snapshot["covered_nodes"], 262);
10801 assert_eq!(snapshot["participant_flights"], 5);
10802 assert_eq!(snapshot["node_participant_projections"], 655);
10803 assert_eq!(snapshot["physical_ledger_entries"], 2);
10804
10805 metrics.reset();
10806 assert_eq!(metrics.snapshot()["wave_authorities"], 0);
10807 }
10808
10809 #[test]
10810 fn reusable_execution_startup_plan_is_policy_derived_largest_first_and_bounded() {
10811 let chunk_64 = PrefillChunk::new(0, 64, 64).unwrap();
10812 let plan = resolve_test_reusable_startup_plan(
10813 32,
10814 2_048,
10815 128,
10816 &[chunk_64, chunk_64],
10817 23,
10818 &ReusableExecutionCaptureConfig::default(),
10819 )
10820 .unwrap();
10821
10822 assert_eq!(plan.decode_widths(), (1..=32).rev().collect::<Vec<_>>());
10823 assert_eq!(plan.prefill_token_counts(), [64]);
10824 assert_eq!(plan.prefill_chunks(), [chunk_64]);
10825 assert_eq!(plan.maximum_decode_sequence_tokens, 97);
10826 assert_eq!(plan.device_plan.maximum_executables(), 759);
10827 assert!(!plan.decode_catalog_omits_admitted_widths());
10828 assert_eq!(
10829 plan.descriptors.last(),
10830 Some(&VNextReusableExecutionDescriptor::prefill(chunk_64))
10831 );
10832
10833 let chunk_7 = PrefillChunk::new(0, 7, 7).unwrap();
10834 let chunk_4 = PrefillChunk::new(0, 4, 4).unwrap();
10835 let non_power_of_two = resolve_test_reusable_startup_plan(
10836 7,
10837 7,
10838 64,
10839 &[chunk_7, chunk_4],
10840 2,
10841 &ReusableExecutionCaptureConfig::default(),
10842 )
10843 .unwrap();
10844 assert_eq!(
10845 non_power_of_two.decode_widths(),
10846 (1..=7).rev().collect::<Vec<_>>()
10847 );
10848 assert_eq!(non_power_of_two.prefill_token_counts(), [7, 4]);
10849 assert_eq!(non_power_of_two.maximum_decode_sequence_tokens, 22);
10850 assert_eq!(non_power_of_two.device_plan.maximum_executables(), 18);
10851
10852 let wider_admission = resolve_test_reusable_startup_plan(
10853 65,
10854 2_048,
10855 128,
10856 &[],
10857 1,
10858 &ReusableExecutionCaptureConfig::default(),
10859 )
10860 .unwrap();
10861 assert_eq!(
10862 wider_admission.decode_widths(),
10863 (1..=32).rev().collect::<Vec<_>>()
10864 );
10865 assert!(wider_admission.decode_catalog_omits_admitted_widths());
10866 assert!(wider_admission
10867 .capture_resolution
10868 .reduction_reason
10869 .as_deref()
10870 .is_some_and(|reason| reason.contains("bounded at width 32")));
10871
10872 let explicit_wide = ReusableExecutionCaptureConfig {
10873 exact_decode_widths: Some(vec![1, 2, 4, 8, 16, 32, 64, 65]),
10874 ..ReusableExecutionCaptureConfig::default()
10875 };
10876 assert!(
10877 resolve_test_reusable_startup_plan(65, 2_048, 128, &[], 1, &explicit_wide).is_err()
10878 );
10879
10880 let unbounded_explicit = ReusableExecutionCaptureConfig {
10881 exact_decode_widths: Some(vec![u32::MAX as usize]),
10882 ..ReusableExecutionCaptureConfig::default()
10883 };
10884 assert!(resolve_reusable_execution_policy(
10885 u32::MAX,
10886 u64::MAX,
10887 usize::MAX,
10888 &[],
10889 &unbounded_explicit,
10890 true,
10891 )
10892 .is_err());
10893
10894 let short_model = resolve_test_reusable_startup_plan(
10895 32,
10896 2_048,
10897 19,
10898 &[],
10899 1,
10900 &ReusableExecutionCaptureConfig::default(),
10901 )
10902 .unwrap();
10903 assert_eq!(short_model.decode_widths(), [32, 16, 8, 4, 2, 1]);
10904 assert_eq!(short_model.maximum_decode_sequence_tokens, 19);
10905 assert_eq!(short_model.device_plan.maximum_executables(), 6);
10906 assert!(short_model.capture_resolution.reduction_reason.is_some());
10907 assert!(short_model.decode_catalog_omits_admitted_widths());
10908 assert!(resolve_test_reusable_startup_plan(
10909 32,
10910 2_048,
10911 18,
10912 &[],
10913 1,
10914 &ReusableExecutionCaptureConfig::default(),
10915 )
10916 .is_err());
10917 let explicit_all = ReusableExecutionCaptureConfig {
10918 exact_decode_widths: Some((1..=32).collect()),
10919 ..ReusableExecutionCaptureConfig::default()
10920 };
10921 assert!(resolve_test_reusable_startup_plan(32, 2_048, 19, &[], 1, &explicit_all).is_err());
10922 }
10923
10924 #[test]
10925 fn reusable_execution_on_demand_keeps_capacity_without_synthetic_token_budget() {
10926 let config = ReusableExecutionCaptureConfig {
10927 preparation: ferrum_types::ReusableExecutionPreparationMode::OnDemand,
10928 ..Default::default()
10929 };
10930 let plan = resolve_test_reusable_startup_plan(32, 32, 2, &[], 7, &config).unwrap();
10931 assert_eq!(plan.maximum_decode_sequence_tokens, 0);
10932 assert_eq!(plan.decode_widths(), (1..=32).rev().collect::<Vec<_>>());
10933 assert_eq!(plan.device_plan.maximum_executables(), 32 * 7);
10934 assert_eq!(
10935 plan.device_plan.catalog_lifetime(),
10936 ferrum_interfaces::vnext::ReusableExecutionCatalogLifetime::OnDemandBounded
10937 );
10938 assert_eq!(plan.program_policy.replay_validation_passes(), 0);
10939 assert!(resolve_test_reusable_startup_plan(
10940 32,
10941 32,
10942 2,
10943 &[],
10944 7,
10945 &ReusableExecutionCaptureConfig::default()
10946 )
10947 .is_err());
10948 }
10949
10950 #[test]
10951 fn reusable_execution_startup_plan_preserves_exact_chunk_boundaries() {
10952 let chunk_single = PrefillChunk::new(0, 1, 1).unwrap();
10953 let chunk_multi = PrefillChunk::new(0, 4, 4).unwrap();
10954 let chunk_boundary = PrefillChunk::new(4, 4, 8).unwrap();
10955 let plan = resolve_test_reusable_startup_plan(
10956 32,
10957 2_048,
10958 128,
10959 &[chunk_single, chunk_multi, chunk_boundary],
10960 23,
10961 &ReusableExecutionCaptureConfig::default(),
10962 )
10963 .unwrap();
10964
10965 assert_eq!(
10966 plan.prefill_chunks(),
10967 [chunk_multi, chunk_boundary, chunk_single]
10968 );
10969 assert_eq!(plan.prefill_token_counts(), [4, 1]);
10970 assert_eq!(plan.prefill_wave_shapes(), 4);
10971 assert_eq!(plan.device_plan.maximum_executables(), 828);
10972 }
10973
10974 #[test]
10975 fn reusable_execution_product_policy_budgets_decode_seed_prefill() {
10976 let seed = PrefillChunk::new(0, 1, 1).unwrap();
10977 let mut enabled_chunks = Vec::new();
10978 budget_reusable_decode_seed_prefill(&mut enabled_chunks, true).unwrap();
10979 assert_eq!(enabled_chunks, [seed]);
10980
10981 let plan = resolve_test_reusable_startup_plan(
10982 32,
10983 2_048,
10984 128,
10985 &enabled_chunks,
10986 23,
10987 &ReusableExecutionCaptureConfig::default(),
10988 )
10989 .unwrap();
10990 assert!(plan
10991 .descriptors
10992 .contains(&VNextReusableExecutionDescriptor::prefill(seed)));
10993
10994 let mut disabled_chunks = Vec::new();
10995 budget_reusable_decode_seed_prefill(&mut disabled_chunks, false).unwrap();
10996 assert!(disabled_chunks.is_empty());
10997 }
10998
10999 #[test]
11000 fn reusable_execution_safe_misses_use_eager_fallback_without_inventory_drift() {
11001 let plan = DeviceReusableExecutionPlan::new(4).unwrap();
11002 let captured =
11003 DeviceReusableExecutionPreparation::preparing_with_progress(plan, 2, 1, 2, 2, 0)
11004 .unwrap();
11005 let replayed =
11006 DeviceReusableExecutionPreparation::preparing_with_progress(plan, 2, 1, 2, 2, 3)
11007 .unwrap();
11008 let sealed = DeviceReusableExecutionPreparation::ready(plan, 2, 1, 2, 2, 3).unwrap();
11009
11010 assert!(reusable_executable_inventory_matches(captured, replayed));
11011 assert!(reusable_executable_inventory_matches(replayed, sealed));
11012 assert!(reusable_execution_requires_eager_fallback(sealed));
11013 }
11014
11015 #[test]
11016 fn reusable_execution_complete_preparation_needs_no_eager_fallback() {
11017 let plan = DeviceReusableExecutionPlan::new(4).unwrap();
11018 let prepared = DeviceReusableExecutionPreparation::ready(plan, 2, 0, 2, 2, 0).unwrap();
11019 let drifted = DeviceReusableExecutionPreparation::ready(plan, 1, 0, 2, 2, 0).unwrap();
11020
11021 assert!(!reusable_execution_requires_eager_fallback(prepared));
11022 assert!(!reusable_executable_inventory_matches(prepared, drifted));
11023 }
11024
11025 #[test]
11026 fn resident_reusable_segments_require_a_typed_program_catalog() {
11027 let plan = DeviceReusableExecutionPlan::new(4).unwrap();
11028 let resident = DeviceReusableExecutionPreparation::ready(plan, 2, 0, 2, 2, 0).unwrap();
11029 let eager_only = DeviceReusableExecutionPreparation::ready(plan, 0, 0, 0, 0, 0).unwrap();
11030
11031 assert!(!reusable_execution_program_catalog_is_usable(resident, 0));
11032 assert!(reusable_execution_program_catalog_is_usable(resident, 1));
11033 assert!(reusable_execution_program_catalog_is_usable(eager_only, 0));
11034 }
11035
11036 #[test]
11037 fn reusable_workspace_policy_is_backend_neutral_and_bucketed() {
11038 let chunks = [
11039 PrefillChunk::new(0, 128, 128).unwrap(),
11040 PrefillChunk::new(0, 64, 64).unwrap(),
11041 ];
11042 let resolution = resolve_reusable_execution_policy(
11043 16,
11044 2_048,
11045 4_096,
11046 &chunks,
11047 &ReusableExecutionCaptureConfig::default(),
11048 true,
11049 )
11050 .unwrap();
11051 let policy = resolution.policy;
11052 let decode_widths = policy
11053 .buckets()
11054 .iter()
11055 .filter(|bucket| bucket.class_id().as_str() == super::UNIFORM_QUERY_REUSABLE_CLASS)
11056 .map(|bucket| bucket.capacity().maximum_sequences())
11057 .collect::<Vec<_>>();
11058 let prefill_tokens = policy
11059 .buckets()
11060 .iter()
11061 .filter(|bucket| bucket.class_id().as_str() == super::PACKED_TOKEN_REUSABLE_CLASS)
11062 .map(|bucket| bucket.capacity().maximum_tokens())
11063 .collect::<Vec<_>>();
11064
11065 assert_eq!(policy.maximum_reusable_lanes(), 1);
11066 assert_eq!(decode_widths, [1, 2, 4, 8, 16]);
11067 assert_eq!(prefill_tokens, [64, 128]);
11068 }
11069
11070 #[test]
11071 fn wave_timing_snapshot_exposes_honest_host_boundaries() {
11072 let snapshot = VNextWaveTimingMetrics::default().snapshot();
11073
11074 assert_eq!(snapshot["clock"], "host_monotonic");
11075 assert_eq!(snapshot["resource_prepare_attempt"]["samples"], 0);
11076 assert_eq!(
11077 snapshot["resource_prepare_breakdown"]["collection"],
11078 "profile_attached_only"
11079 );
11080 assert_eq!(
11081 snapshot["resource_prepare_breakdown"]["step_request_prepare"]["samples"],
11082 0
11083 );
11084 assert_eq!(
11085 snapshot["resource_prepare_breakdown"]["step_admission"]["samples"],
11086 0
11087 );
11088 assert_eq!(
11089 snapshot["resource_prepare_breakdown"]["step_admission_breakdown"]["demand_evaluate"]
11090 ["samples"],
11091 0
11092 );
11093 assert_eq!(
11094 snapshot["resource_prepare_breakdown"]["submission_wave_prepare"]["samples"],
11095 0
11096 );
11097 assert_eq!(snapshot["submitted_wave_total"]["samples"], 0);
11098 assert_eq!(
11099 snapshot["host_encode_submit_breakdown"]["collection"],
11100 "profile_attached_only"
11101 );
11102 assert_eq!(
11103 snapshot["host_encode_submit_breakdown"]["wave_identity_bind"]["samples"],
11104 0
11105 );
11106 assert_eq!(
11107 snapshot["host_encode_submit_breakdown"]["provider_encode_submit_breakdown"]
11108 ["provider_node_encode"]["samples"],
11109 0
11110 );
11111 assert_eq!(
11112 snapshot["host_encode_submit_breakdown"]["provider_encode_submit_breakdown"]
11113 ["lane_reserve_submit_arm_breakdown"]["device_runtime_submit"]["samples"],
11114 0
11115 );
11116 assert_eq!(
11117 snapshot["host_encode_submit_breakdown"]["provider_encode_submit_breakdown"]
11118 ["lane_reserve_submit_arm_breakdown"]["device_runtime_submit_breakdown"]
11119 ["enqueue_commands"]["samples"],
11120 0
11121 );
11122 assert_eq!(
11123 snapshot["host_encode_submit_breakdown"]["provider_encode_submit_breakdown"]
11124 ["lane_reserve_submit_arm_breakdown"]["device_runtime_submit_breakdown"]
11125 ["reusable_execution"]["replayed_commands"],
11126 0
11127 );
11128 assert!(snapshot["limitations"]
11129 .as_array()
11130 .unwrap()
11131 .iter()
11132 .any(|entry| entry.as_str().unwrap().contains("not kernel")));
11133 }
11134
11135 #[test]
11136 fn wave_timing_reset_clears_resource_prepare_breakdown() {
11137 let metrics = VNextWaveTimingMetrics::default();
11138 metrics
11139 .resource_step_request_prepare
11140 .record(Duration::from_micros(11));
11141 metrics
11142 .resource_step_admission
11143 .record(Duration::from_micros(13));
11144 metrics.resource_step_admission_breakdown.record(
11145 StepResourceAdmissionProfilePhase::DemandEvaluate,
11146 Duration::from_micros(7),
11147 );
11148 metrics
11149 .resource_submission_wave_prepare
11150 .record(Duration::from_micros(17));
11151
11152 let snapshot = metrics.snapshot();
11153 assert_eq!(
11154 snapshot["resource_prepare_breakdown"]["step_request_prepare"]["samples"],
11155 1
11156 );
11157 assert_eq!(
11158 snapshot["resource_prepare_breakdown"]["step_admission"]["average_us"],
11159 13.0
11160 );
11161 assert_eq!(
11162 snapshot["resource_prepare_breakdown"]["step_admission_breakdown"]["demand_evaluate"]
11163 ["average_us"],
11164 7.0
11165 );
11166 assert_eq!(
11167 snapshot["resource_prepare_breakdown"]["submission_wave_prepare"]["average_us"],
11168 17.0
11169 );
11170
11171 metrics.reset();
11172 let snapshot = metrics.snapshot();
11173 assert_eq!(
11174 snapshot["resource_prepare_breakdown"]["step_request_prepare"]["samples"],
11175 0
11176 );
11177 assert_eq!(
11178 snapshot["resource_prepare_breakdown"]["step_admission"]["samples"],
11179 0
11180 );
11181 assert_eq!(
11182 snapshot["resource_prepare_breakdown"]["step_admission_breakdown"]["demand_evaluate"]
11183 ["samples"],
11184 0
11185 );
11186 assert_eq!(
11187 snapshot["resource_prepare_breakdown"]["submission_wave_prepare"]["samples"],
11188 0
11189 );
11190 }
11191
11192 #[test]
11193 fn device_timing_snapshot_distinguishes_device_and_host_clocks() {
11194 let snapshot = VNextDeviceTimingMetrics::default().snapshot();
11195
11196 assert_eq!(snapshot["device_execution"]["samples"], 0);
11197 assert_eq!(snapshot["fence_wait_host"]["samples"], 0);
11198 assert_eq!(snapshot["readback_host"]["samples"], 0);
11199 assert_eq!(
11200 snapshot["clocks"]["device_execution"],
11201 "backend_device_event_elapsed"
11202 );
11203 assert!(snapshot["limitations"]
11204 .as_array()
11205 .unwrap()
11206 .iter()
11207 .any(|entry| entry.as_str().unwrap().contains("must not be added")));
11208 }
11209
11210 #[test]
11211 fn physical_span_metrics_group_replay_time_by_executable_fingerprint() {
11212 let eager = DeviceSubmissionExecutionSpan::measured(
11213 0,
11214 1,
11215 DeviceExecutionSpanKind::EagerCommand,
11216 vec![
11217 DeviceExecutionInterval::new(DeviceExecutionIntervalKind::Transfer, 0, 10).unwrap(),
11218 ],
11219 )
11220 .unwrap();
11221 let replay = DeviceSubmissionExecutionSpan::measured(
11222 1,
11223 2,
11224 DeviceExecutionSpanKind::ReusableExecutable,
11225 vec![
11226 DeviceExecutionInterval::new(DeviceExecutionIntervalKind::Compute, 10, 50).unwrap(),
11227 ],
11228 )
11229 .unwrap()
11230 .with_reusable_executable_fingerprint("a".repeat(64))
11231 .unwrap();
11232 let timing = DeviceSubmissionExecutionTiming::from_spans(2, vec![eager, replay]).unwrap();
11233 let metrics = VNextPhysicalSpanTimingMetrics::default();
11234
11235 metrics.record(&DeviceTimingMeasurement::Measured(timing));
11236 let snapshot = metrics.snapshot();
11237
11238 assert_eq!(snapshot["measured_submissions"], 1);
11239 assert_eq!(snapshot["eager_commands"]["total_ns"], 10);
11240 assert_eq!(snapshot["reusable_executables"]["total_ns"], 40);
11241 assert_eq!(
11242 snapshot["reusable_by_fingerprint"][0]["reusable_executable_fingerprint"],
11243 "a".repeat(64)
11244 );
11245 assert_eq!(
11246 snapshot["reusable_by_fingerprint"][0]["timing"]["total_ns"],
11247 40
11248 );
11249
11250 metrics.reset();
11251 assert_eq!(metrics.snapshot()["measured_submissions"], 0);
11252 }
11253
11254 #[test]
11255 fn product_sequence_fit_policy_maps_exhaustively_to_runtime_contract() {
11256 assert_eq!(
11257 resolved_sequence_fit_policy(SequenceFitPolicy::ImmediateOnly),
11258 AdmissionFitPolicy::ImmediateOnly
11259 );
11260 assert_eq!(
11261 resolved_sequence_fit_policy(SequenceFitPolicy::FullInputMustFit),
11262 AdmissionFitPolicy::FullInputMustFit
11263 );
11264 }
11265
11266 #[test]
11267 fn allocated_memory_does_not_count_static_claim_twice() {
11268 assert_eq!(reported_allocated_bytes(Some(64), 64), 64);
11269 assert_eq!(reported_allocated_bytes(None, 64), 64);
11270 }
11271
11272 #[test]
11273 fn monotonic_wall_anchor_uses_sample_midpoint_and_bounds_full_capture_span() {
11274 assert_eq!(bounded_wall_anchor(1_000, 1_100), (1_050, 100));
11275 assert_eq!(bounded_wall_anchor(1_101, 1_000), (1_050, 101));
11276 assert_eq!(bounded_wall_anchor(7, 7), (7, 0));
11277 }
11278
11279 #[test]
11280 fn journal_clock_anchor_is_kernel_profile_only() {
11281 assert!(journal_clock_anchor_required(DeviceTimingMode::Kernel));
11282 for timing_mode in [
11283 DeviceTimingMode::Off,
11284 DeviceTimingMode::Completion,
11285 DeviceTimingMode::Replay,
11286 DeviceTimingMode::Verification,
11287 ] {
11288 assert!(!journal_clock_anchor_required(timing_mode));
11289 }
11290 }
11291
11292 #[test]
11293 fn decode_capacity_deferral_preserves_plan_runtime_sequence() {
11294 let error = FerrumError::resource_exhausted("dynamic pool is waiting for release");
11295
11296 assert_eq!(
11297 DecodeFailureDisposition::from_error(&error),
11298 DecodeFailureDisposition::PreserveForCapacityRetry
11299 );
11300 }
11301
11302 #[test]
11303 fn decode_permanent_failure_aborts_plan_runtime_sequence() {
11304 let error = FerrumError::request_validation("sequence exceeds its configured ceiling");
11305
11306 assert_eq!(
11307 DecodeFailureDisposition::from_error(&error),
11308 DecodeFailureDisposition::AbortSequence
11309 );
11310 }
11311
11312 #[test]
11313 fn product_output_mode_requires_a_uniform_exact_greedy_decode_wave() {
11314 let greedy = LogitsReturnPolicy::GreedyArgmax {
11315 token_mask: None,
11316 repetition_penalty: None,
11317 };
11318 let full = LogitsReturnPolicy::FullLogits;
11319 let repetition = LogitsReturnPolicy::GreedyArgmax {
11320 token_mask: None,
11321 repetition_penalty: Some(GreedyRepetitionPenalty::new(1.1, vec![7, 11])),
11322 };
11323
11324 assert_eq!(
11325 product_output_mode_for_policies(
11326 VNextExecutionWaveKind::Decode,
11327 [Some(&greedy), Some(&greedy)],
11328 ),
11329 VNextProductOutputMode::GreedyToken
11330 );
11331 assert_eq!(
11332 product_output_mode_for_policies(
11333 VNextExecutionWaveKind::Decode,
11334 [Some(&greedy), Some(&repetition)],
11335 ),
11336 VNextProductOutputMode::GreedyToken
11337 );
11338 assert_eq!(
11339 product_output_mode_for_policies(
11340 VNextExecutionWaveKind::Decode,
11341 [Some(&greedy), Some(&full)],
11342 ),
11343 VNextProductOutputMode::FullLogits
11344 );
11345 assert_eq!(
11346 product_output_mode_for_policies(VNextExecutionWaveKind::Prefill, [Some(&greedy)],),
11347 VNextProductOutputMode::FullLogits
11348 );
11349 assert_eq!(
11350 product_output_mode_for_policies(VNextExecutionWaveKind::Decode, std::iter::empty(),),
11351 VNextProductOutputMode::FullLogits
11352 );
11353 }
11354
11355 #[test]
11356 fn teacher_forcing_masks_only_the_engine_facing_logits_copy() {
11357 let mut outputs = vec![ExecutorSamplingOutput::full_logits(vec![1.0, 2.0, 3.0]).unwrap()];
11358 apply_teacher_forced_decision(
11359 &mut outputs,
11360 VNextTeacherForcedDecision::new(7, TokenId::new(1)),
11361 )
11362 .unwrap();
11363
11364 assert_eq!(
11365 outputs,
11366 [ExecutorSamplingOutput::FullLogits(vec![
11367 f32::NEG_INFINITY,
11368 0.0,
11369 f32::NEG_INFINITY,
11370 ])]
11371 );
11372 assert!(apply_teacher_forced_decision(
11373 &mut [ExecutorSamplingOutput::greedy_token(TokenId::new(1))],
11374 VNextTeacherForcedDecision::new(0, TokenId::new(1)),
11375 )
11376 .is_err());
11377 assert!(apply_teacher_forced_decision(
11378 &mut [ExecutorSamplingOutput::full_logits(vec![0.0]).unwrap()],
11379 VNextTeacherForcedDecision::new(0, TokenId::new(2)),
11380 )
11381 .is_err());
11382 }
11383
11384 #[test]
11385 fn product_repetition_input_is_typed_and_neutral_outside_greedy_decode() {
11386 let policy = LogitsReturnPolicy::GreedyArgmax {
11387 token_mask: None,
11388 repetition_penalty: Some(GreedyRepetitionPenalty::new(1.25, vec![3, 9])),
11389 };
11390 let active = product_repetition_input(Some(&policy), VNextProductOutputMode::GreedyToken);
11391 assert_eq!(active.token_ids, [3, 9]);
11392 assert_eq!(active.penalty, 1.25);
11393 assert!(active.is_active());
11394
11395 let neutral = product_repetition_input(Some(&policy), VNextProductOutputMode::FullLogits);
11396 assert!(neutral.token_ids.is_empty());
11397 assert_eq!(neutral.penalty, 1.0);
11398 assert!(!neutral.is_active());
11399 }
11400
11401 #[test]
11402 fn repetition_token_ids_are_zero_padded_to_the_typed_participant_capacity() {
11403 let bytes = padded_repetition_token_id_bytes(&[3, 9], 4).unwrap();
11404 assert_eq!(
11405 bytes,
11406 [
11407 3_u32.to_le_bytes(),
11408 9_u32.to_le_bytes(),
11409 0_u32.to_le_bytes(),
11410 0_u32.to_le_bytes(),
11411 ]
11412 .concat()
11413 );
11414 assert!(padded_repetition_token_id_bytes(&[3, 9], 1).is_err());
11415 }
11416
11417 #[test]
11418 fn product_token_mask_preserves_short_mask_semantics_without_hidden_defaults() {
11419 let policy = LogitsReturnPolicy::GreedyArgmax {
11420 token_mask: Some(TokenSelectionMask::new(vec![1, -7, 0])),
11421 repetition_penalty: None,
11422 };
11423 assert_eq!(
11424 normalized_product_token_mask(Some(&policy), VNextProductOutputMode::GreedyToken, 5,),
11425 [1, 1, 0, 0, 0]
11426 );
11427 assert_eq!(
11428 normalized_product_token_mask(Some(&policy), VNextProductOutputMode::FullLogits, 5,),
11429 [1, 1, 1, 1, 1]
11430 );
11431 assert_eq!(
11432 normalized_product_token_mask(None, VNextProductOutputMode::GreedyToken, 3),
11433 [1, 1, 1]
11434 );
11435 }
11436
11437 fn test_token_mask_target(
11438 slot_id: u64,
11439 participant_index: usize,
11440 ) -> VNextProductTokenMaskSlotTarget {
11441 VNextProductTokenMaskSlotTarget {
11442 identity: VNextProductTokenMaskSlotIdentity::Test(slot_id),
11443 participant_index,
11444 }
11445 }
11446
11447 fn test_selection_content(mask: &TokenSelectionMask) -> VNextProductTokenMaskContent {
11448 let policy = LogitsReturnPolicy::GreedyArgmax {
11449 token_mask: Some(mask.clone()),
11450 repetition_penalty: None,
11451 };
11452 VNextProductTokenMaskContent::from_policy(
11453 Some(&policy),
11454 VNextProductOutputMode::GreedyToken,
11455 5,
11456 )
11457 }
11458
11459 #[test]
11460 fn product_token_mask_residency_requires_exact_slot_participant_and_content() {
11461 let first_mask = TokenSelectionMask::new(vec![1, 0, 1]);
11462 let first_content = test_selection_content(&first_mask);
11463 let target = test_token_mask_target(7, 0);
11464 let mut residency = VNextProductTokenMaskResidency::default();
11465
11466 let first = residency.prepare(Some(target.clone()), first_content.clone());
11467 assert!(first.upload_required);
11468 residency.publish(std::slice::from_ref(&first));
11469
11470 assert!(
11471 !residency
11472 .prepare(Some(target.clone()), first_content.clone())
11473 .upload_required
11474 );
11475 assert!(
11476 residency
11477 .prepare(Some(test_token_mask_target(7, 1)), first_content.clone())
11478 .upload_required
11479 );
11480 assert!(
11481 residency
11482 .prepare(Some(test_token_mask_target(8, 0)), first_content.clone())
11483 .upload_required
11484 );
11485
11486 let identical = TokenSelectionMask::new(vec![1, 0, 1]);
11488 assert!(
11489 !residency
11490 .prepare(Some(target.clone()), test_selection_content(&identical))
11491 .upload_required
11492 );
11493
11494 let mut forged_collision = TokenSelectionMask::new(vec![0, 1, 0]);
11497 forged_collision.fingerprint = first_mask.fingerprint;
11498 assert!(
11499 residency
11500 .prepare(Some(target), test_selection_content(&forged_collision))
11501 .upload_required
11502 );
11503 }
11504
11505 #[test]
11506 fn product_token_mask_residency_overwrite_and_failed_transactions_are_fail_closed() {
11507 let residency = parking_lot::Mutex::new(VNextProductTokenMaskResidency::default());
11508 let first = test_selection_content(&TokenSelectionMask::new(vec![1, 0, 1]));
11509 let changed = test_selection_content(&TokenSelectionMask::new(vec![1, 1, 0]));
11510
11511 {
11512 let mut transaction = VNextProductTokenMaskResidencyTransaction::prepare_for_test(
11513 &residency,
11514 Some(7),
11515 [first.clone()],
11516 );
11517 assert!(transaction.plans()[0].upload_required);
11518 transaction.publish();
11519 transaction.settle_success();
11520 }
11521 {
11522 let mut transaction = VNextProductTokenMaskResidencyTransaction::prepare_for_test(
11523 &residency,
11524 Some(7),
11525 [changed.clone()],
11526 );
11527 assert!(transaction.plans()[0].upload_required);
11528 transaction.invalidate_before_slot_release();
11530 assert!(residency.lock().entries.is_empty());
11531 drop(transaction);
11532 }
11533 {
11534 let transaction = VNextProductTokenMaskResidencyTransaction::prepare_for_test(
11535 &residency,
11536 Some(7),
11537 [first.clone()],
11538 );
11539 assert!(transaction.plans()[0].upload_required);
11540 }
11541 {
11542 let mut transaction = VNextProductTokenMaskResidencyTransaction::prepare_for_test(
11543 &residency,
11544 Some(7),
11545 [first.clone()],
11546 );
11547 transaction.publish();
11548 }
11552 let transaction = VNextProductTokenMaskResidencyTransaction::prepare_for_test(
11553 &residency,
11554 Some(7),
11555 [first],
11556 );
11557 assert!(transaction.plans()[0].upload_required);
11558 }
11559
11560 #[test]
11561 fn product_token_mask_residency_tracks_participant_reordering_by_physical_range() {
11562 let mask_a = TokenSelectionMask::new(vec![1, 0, 1]);
11563 let mask_b = TokenSelectionMask::new(vec![0, 1, 1]);
11564 let content_a = test_selection_content(&mask_a);
11565 let content_b = test_selection_content(&mask_b);
11566 let target_zero = test_token_mask_target(7, 0);
11567 let target_one = test_token_mask_target(7, 1);
11568 let mut residency = VNextProductTokenMaskResidency::default();
11569
11570 let original = [
11571 residency.prepare(Some(target_zero.clone()), content_a.clone()),
11572 residency.prepare(Some(target_one.clone()), content_b.clone()),
11573 ];
11574 assert!(original.iter().all(|plan| plan.upload_required));
11575 residency.publish(&original);
11576
11577 let reordered = [
11578 residency.prepare(Some(target_zero.clone()), content_b.clone()),
11579 residency.prepare(Some(target_one.clone()), content_a.clone()),
11580 ];
11581 assert!(reordered.iter().all(|plan| plan.upload_required));
11582 residency.publish(&reordered);
11583
11584 assert!(
11585 !residency
11586 .prepare(Some(target_zero), content_b)
11587 .upload_required
11588 );
11589 assert!(
11590 !residency
11591 .prepare(Some(target_one), content_a)
11592 .upload_required
11593 );
11594 }
11595
11596 #[test]
11597 fn product_token_mask_residency_rejects_copy_on_write_mutation() {
11598 let mut mask = TokenSelectionMask::new(vec![1, 0, 1]);
11599 let target = test_token_mask_target(7, 0);
11600 let mut residency = VNextProductTokenMaskResidency::default();
11601 let original = residency.prepare(Some(target.clone()), test_selection_content(&mask));
11602 residency.publish(std::slice::from_ref(&original));
11603
11604 assert!(mask.set_tokens_validity(&[1], true));
11605 assert!(
11606 residency
11607 .prepare(Some(target), test_selection_content(&mask))
11608 .upload_required,
11609 "copy-on-write mutation must not inherit the prior device residency proof"
11610 );
11611 }
11612
11613 #[test]
11614 fn product_token_mask_residency_without_stable_slot_always_uploads() {
11615 let residency = parking_lot::Mutex::new(VNextProductTokenMaskResidency::default());
11616 let content = VNextProductTokenMaskContent::AllValid { vocabulary_size: 5 };
11617 for _ in 0..2 {
11618 let mut transaction = VNextProductTokenMaskResidencyTransaction::prepare_for_test(
11619 &residency,
11620 None,
11621 [content.clone()],
11622 );
11623 assert!(transaction.plans()[0].upload_required);
11624 transaction.publish();
11625 transaction.settle_success();
11626 }
11627 assert!(residency.lock().entries.is_empty());
11628 }
11629
11630 #[test]
11631 fn product_token_mask_residency_is_entry_bounded_and_does_not_retain_masks() {
11632 let mut residency = VNextProductTokenMaskResidency::default();
11633 for slot_id in 0..=MAX_PRODUCT_TOKEN_MASK_SLOT_CACHE_ENTRIES as u64 {
11634 let plan = residency.prepare(
11635 Some(test_token_mask_target(slot_id, 0)),
11636 VNextProductTokenMaskContent::AllValid { vocabulary_size: 5 },
11637 );
11638 residency.publish(std::slice::from_ref(&plan));
11639 assert!(residency.entries.len() <= MAX_PRODUCT_TOKEN_MASK_SLOT_CACHE_ENTRIES);
11640 }
11641
11642 residency.clear();
11643 let target = test_token_mask_target(17, 0);
11644 {
11645 let content = test_selection_content(&TokenSelectionMask::new(vec![1, 0, 1]));
11646 let plan = residency.prepare(Some(target.clone()), content);
11647 residency.publish(std::slice::from_ref(&plan));
11648 }
11649 assert!(
11650 residency
11651 .prepare(
11652 Some(target),
11653 test_selection_content(&TokenSelectionMask::new(vec![1, 0, 1])),
11654 )
11655 .upload_required,
11656 "a dead request mask must not be retained solely by the residency ledger"
11657 );
11658 }
11659
11660 #[test]
11661 fn selected_token_readback_rejects_out_of_vocabulary_values() {
11662 assert_eq!(
11663 decode_selected_token(&3_u32.to_le_bytes(), 8).unwrap(),
11664 TokenId::new(3)
11665 );
11666 assert!(decode_selected_token(&8_u32.to_le_bytes(), 8).is_err());
11667 assert!(decode_selected_token(&u32::MAX.to_le_bytes(), 8).is_err());
11668 assert!(decode_selected_token(&[0, 1, 2], 8).is_err());
11669 }
11670
11671 #[test]
11672 fn decode_output_width_accepts_only_full_logits_or_one_token_sentinel() {
11673 assert_eq!(decode_output_width(1, 248_320).unwrap(), 1);
11674 assert_eq!(decode_output_width(248_320, 248_320).unwrap(), 248_320);
11675 assert!(decode_output_width(2, 248_320).is_err());
11676 assert!(decode_output_width(0, 248_320).is_err());
11677 }
11678
11679 #[test]
11680 fn recompute_completion_preserves_product_usage_and_replay_baseline() {
11681 let request_id = RequestId::new();
11682 let valid = ExecutorSequenceCompletion::new(request_id.clone(), "cache-valid".into(), 2, 3)
11683 .unwrap();
11684 validate_sequence_completion_accounting(&request_id, 2, 1, &valid).unwrap();
11685
11686 let wrong_prompt =
11687 ExecutorSequenceCompletion::new(request_id.clone(), "cache-prompt".into(), 3, 3)
11688 .unwrap();
11689 assert!(validate_sequence_completion_accounting(&request_id, 2, 1, &wrong_prompt).is_err());
11690
11691 let before_replay =
11692 ExecutorSequenceCompletion::new(request_id.clone(), "cache-output".into(), 2, 0)
11693 .unwrap();
11694 assert!(
11695 validate_sequence_completion_accounting(&request_id, 2, 1, &before_replay).is_err()
11696 );
11697
11698 let other_request =
11699 ExecutorSequenceCompletion::new(RequestId::new(), "cache-other".into(), 2, 3).unwrap();
11700 assert!(
11701 validate_sequence_completion_accounting(&request_id, 2, 1, &other_request).is_err()
11702 );
11703 }
11704}