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