dynamo_mocker/common/
utils.rs1use std::time::{Duration, Instant};
5
6use crate::common::handoff::HandoffTransferTiming;
7use crate::common::protocols::{KvTransferTimingMode, MockEngineArgs, WorkerType};
8
9pub fn prefill_handoff_transfer_timing(
10 num_input_tokens: usize,
11 kv_transfer_bandwidth: Option<f64>,
12 kv_bytes_per_token: Option<usize>,
13 mode: KvTransferTimingMode,
14) -> HandoffTransferTiming {
15 HandoffTransferTiming {
16 mode,
17 full_prompt_tokens: num_input_tokens,
18 kv_bytes_per_token,
19 bandwidth_gb_s: kv_transfer_bandwidth,
20 }
21}
22
23pub fn compute_prefill_handoff_delay_ms(
30 worker_type: WorkerType,
31 completed: bool,
32 num_input_tokens: usize,
33 kv_transfer_bandwidth: Option<f64>,
34 kv_bytes_per_token: Option<usize>,
35) -> Option<f64> {
36 if worker_type != WorkerType::Prefill || !completed {
37 return None;
38 }
39 let timing = prefill_handoff_transfer_timing(
40 num_input_tokens,
41 kv_transfer_bandwidth,
42 kv_bytes_per_token,
43 KvTransferTimingMode::FullPrompt,
44 );
45 match timing.full_prompt_delay_ms() {
46 Some(delay_ms) => {
47 tracing::debug!(
48 num_input_tokens,
49 bandwidth_gb_s = kv_transfer_bandwidth,
50 delay_ms = format!("{delay_ms:.2}"),
51 "KV handoff delay for prefill completion"
52 );
53 Some(delay_ms)
54 }
55 None => None,
56 }
57}
58
59pub fn compute_kv_transfer_delay(
63 args: &MockEngineArgs,
64 num_input_tokens: usize,
65) -> Option<Duration> {
66 compute_prefill_handoff_delay_ms(
67 args.worker_type,
68 true,
69 num_input_tokens,
70 args.kv_transfer_bandwidth,
71 args.kv_bytes_per_token,
72 )
73 .map(|delay_ms| Duration::from_secs_f64(delay_ms / 1000.0))
74}
75
76pub async fn sleep_precise(duration: Duration) {
78 sleep_until_precise(Instant::now() + duration).await;
79}
80
81pub async fn sleep_until_precise(deadline: Instant) {
87 if deadline <= Instant::now() {
91 tokio::task::yield_now().await;
92 return;
93 }
94
95 #[cfg(target_os = "linux")]
96 {
97 if let Ok(delay) = tokio_timerfd::Delay::new(deadline) {
98 let _ = delay.await;
99 } else {
100 tokio::time::sleep_until(tokio::time::Instant::from_std(deadline)).await;
101 }
102 }
103 #[cfg(not(target_os = "linux"))]
104 {
105 tokio::time::sleep_until(tokio::time::Instant::from_std(deadline)).await;
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112 use std::task::Poll;
113
114 #[tokio::test(flavor = "current_thread")]
115 async fn test_expired_precise_sleep_yields_to_runtime() {
116 let sleep = sleep_until_precise(Instant::now());
117 tokio::pin!(sleep);
118
119 let first_poll = futures::poll!(sleep.as_mut());
120
121 assert!(matches!(first_poll, Poll::Pending));
122 sleep.await;
123 }
124
125 #[test]
126 fn test_prefill_handoff_delay_only_applies_to_completed_prefill() {
127 let delay_ms = compute_prefill_handoff_delay_ms(
128 WorkerType::Prefill,
129 true,
130 128,
131 Some(1.0),
132 Some(1_000_000),
133 )
134 .expect("prefill completion should produce a handoff delay");
135 assert!((delay_ms - 128.0).abs() < 1e-9);
136
137 assert!(
138 compute_prefill_handoff_delay_ms(
139 WorkerType::Prefill,
140 false,
141 128,
142 Some(1.0),
143 Some(1_000_000),
144 )
145 .is_none()
146 );
147 assert!(
148 compute_prefill_handoff_delay_ms(
149 WorkerType::Decode,
150 true,
151 128,
152 Some(1.0),
153 Some(1_000_000),
154 )
155 .is_none()
156 );
157 }
158}