response_time_analysis/
fixed_point.rs1use std::cmp::Ordering;
9
10use thiserror::Error;
11
12use crate::supply::SupplyBound;
13use crate::time::{Duration, Offset, Service};
14
15#[cfg(debug_assertions)]
16use crate::time::Time;
17
18#[derive(Debug, Error, Copy, Clone, Eq, PartialEq, PartialOrd)]
20pub enum SearchFailure {
21 #[error("no fixed point less than {limit} found for offset {offset}")]
23 DivergenceLimitExceeded { offset: Offset, limit: Duration },
24
25 #[error("analysis assumption violated")]
27 AssumptionViolated,
28}
29
30pub type SearchResult = Result<Duration, SearchFailure>;
33
34pub fn search_with_offset<SBF, RHS>(
38 supply: &SBF,
39 offset: Offset,
40 divergence_limit: Duration,
41 workload: &RHS,
42) -> SearchResult
43where
44 SBF: SupplyBound + ?Sized,
45 RHS: Fn(Duration) -> Service,
46{
47 let mut assumed_response_time = Duration::from(1);
48 while assumed_response_time <= divergence_limit {
49 let demand = workload(assumed_response_time);
50 let demand_met = Offset::from_time_zero(supply.service_time(demand));
51 let response_time_bound = offset.distance_to(demand_met);
52 if response_time_bound <= assumed_response_time {
53 return Ok(response_time_bound);
55 } else {
56 assumed_response_time = response_time_bound
58 }
59 }
60 Err(SearchFailure::DivergenceLimitExceeded {
62 offset,
63 limit: divergence_limit,
64 })
65}
66
67#[cfg(debug_assertions)]
71fn brute_force_search_with_offset<SBF, RHS>(
72 supply: &SBF,
73 offset: Offset,
74 divergence_limit: Duration,
75 workload: &RHS,
76) -> SearchResult
77where
78 SBF: SupplyBound + ?Sized,
79 RHS: Fn(Duration) -> Service,
80{
81 for r in 1..=Time::from(divergence_limit) {
82 let assumed_response_time = Duration::from(r);
83 let lhs = supply.provided_service(offset.since_time_zero() + assumed_response_time);
84 let rhs = workload(assumed_response_time);
85 if rhs.is_none() {
87 return Ok(Duration::zero());
88 } else if lhs == rhs {
89 return Ok(assumed_response_time);
90 }
91 }
92 Err(SearchFailure::DivergenceLimitExceeded {
93 offset,
94 limit: divergence_limit,
95 })
96}
97
98pub fn search<SBF, RHS>(
102 supply: &SBF,
103 divergence_limit: Duration,
104 workload_bound: RHS,
105) -> SearchResult
106where
107 SBF: SupplyBound + ?Sized,
108 RHS: Fn(Duration) -> Service,
109{
110 let bw = search_with_offset(supply, Offset::from(0), divergence_limit, &workload_bound);
111 #[cfg(debug_assertions)]
113 if divergence_limit <= Duration::from(100_000) {
114 debug_assert_eq!(
115 brute_force_search_with_offset(
116 supply,
117 Offset::from(0),
118 divergence_limit,
119 &workload_bound
120 ),
121 bw
122 );
123 }
124 bw
125}
126
127pub fn max_response_time(rta_per_offset: impl Iterator<Item = SearchResult>) -> SearchResult {
134 rta_per_offset
135 .max_by(|a, b| {
136 if a.is_err() {
138 Ordering::Greater
140 } else if b.is_err() {
141 Ordering::Less
143 } else {
144 a.unwrap().cmp(&b.unwrap())
146 }
147 })
148 .unwrap_or(Ok(Duration::zero()))
151}