xref: /aosp_15_r20/external/crosvm/devices/src/tsc/grouping.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2022 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker use std::cmp::Ordering;
6*bb4ee6a4SAndroid Build Coastguard Worker use std::cmp::Reverse;
7*bb4ee6a4SAndroid Build Coastguard Worker use std::ops::Sub;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::time::Duration;
9*bb4ee6a4SAndroid Build Coastguard Worker 
10*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::anyhow;
11*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::bail;
12*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Result;
13*bb4ee6a4SAndroid Build Coastguard Worker use base::warn;
14*bb4ee6a4SAndroid Build Coastguard Worker 
abs_diff<T: Sub<Output = T> + Ord>(x: T, y: T) -> T15*bb4ee6a4SAndroid Build Coastguard Worker fn abs_diff<T: Sub<Output = T> + Ord>(x: T, y: T) -> T {
16*bb4ee6a4SAndroid Build Coastguard Worker     if x < y {
17*bb4ee6a4SAndroid Build Coastguard Worker         y - x
18*bb4ee6a4SAndroid Build Coastguard Worker     } else {
19*bb4ee6a4SAndroid Build Coastguard Worker         x - y
20*bb4ee6a4SAndroid Build Coastguard Worker     }
21*bb4ee6a4SAndroid Build Coastguard Worker }
22*bb4ee6a4SAndroid Build Coastguard Worker 
23*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
24*bb4ee6a4SAndroid Build Coastguard Worker pub struct CoreOffset {
25*bb4ee6a4SAndroid Build Coastguard Worker     pub core: usize,
26*bb4ee6a4SAndroid Build Coastguard Worker     pub offset: i128,
27*bb4ee6a4SAndroid Build Coastguard Worker }
28*bb4ee6a4SAndroid Build Coastguard Worker 
29*bb4ee6a4SAndroid Build Coastguard Worker impl Ord for CoreOffset {
30*bb4ee6a4SAndroid Build Coastguard Worker     // CoreOffsets are ordered by offset ascending, then by their core number ascending
cmp(&self, other: &Self) -> Ordering31*bb4ee6a4SAndroid Build Coastguard Worker     fn cmp(&self, other: &Self) -> Ordering {
32*bb4ee6a4SAndroid Build Coastguard Worker         (self.offset, self.core).cmp(&(other.offset, other.core))
33*bb4ee6a4SAndroid Build Coastguard Worker     }
34*bb4ee6a4SAndroid Build Coastguard Worker }
35*bb4ee6a4SAndroid Build Coastguard Worker 
36*bb4ee6a4SAndroid Build Coastguard Worker impl PartialOrd for CoreOffset {
partial_cmp(&self, other: &Self) -> Option<Ordering>37*bb4ee6a4SAndroid Build Coastguard Worker     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
38*bb4ee6a4SAndroid Build Coastguard Worker         Some(self.cmp(other))
39*bb4ee6a4SAndroid Build Coastguard Worker     }
40*bb4ee6a4SAndroid Build Coastguard Worker }
41*bb4ee6a4SAndroid Build Coastguard Worker 
42*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Default, Debug, Clone, Eq, PartialEq)]
43*bb4ee6a4SAndroid Build Coastguard Worker pub struct CoreGroup {
44*bb4ee6a4SAndroid Build Coastguard Worker     pub cores: Vec<CoreOffset>,
45*bb4ee6a4SAndroid Build Coastguard Worker }
46*bb4ee6a4SAndroid Build Coastguard Worker 
47*bb4ee6a4SAndroid Build Coastguard Worker impl CoreGroup {
size(&self) -> usize48*bb4ee6a4SAndroid Build Coastguard Worker     fn size(&self) -> usize {
49*bb4ee6a4SAndroid Build Coastguard Worker         self.cores.len()
50*bb4ee6a4SAndroid Build Coastguard Worker     }
51*bb4ee6a4SAndroid Build Coastguard Worker 
add(&self, core: CoreOffset, limit: u128) -> Result<Self>52*bb4ee6a4SAndroid Build Coastguard Worker     fn add(&self, core: CoreOffset, limit: u128) -> Result<Self> {
53*bb4ee6a4SAndroid Build Coastguard Worker         let diff_from_min = abs_diff(self.cores.iter().min().unwrap().offset, core.offset) as u128;
54*bb4ee6a4SAndroid Build Coastguard Worker         let diff_from_max = abs_diff(self.cores.iter().max().unwrap().offset, core.offset) as u128;
55*bb4ee6a4SAndroid Build Coastguard Worker         let can_add = diff_from_min < limit && diff_from_max < limit;
56*bb4ee6a4SAndroid Build Coastguard Worker 
57*bb4ee6a4SAndroid Build Coastguard Worker         if can_add {
58*bb4ee6a4SAndroid Build Coastguard Worker             let mut new = self.clone();
59*bb4ee6a4SAndroid Build Coastguard Worker             new.cores.push(core);
60*bb4ee6a4SAndroid Build Coastguard Worker             Ok(new)
61*bb4ee6a4SAndroid Build Coastguard Worker         } else {
62*bb4ee6a4SAndroid Build Coastguard Worker             Err(anyhow!(
63*bb4ee6a4SAndroid Build Coastguard Worker                 "offset {} not within {} of all members of core group",
64*bb4ee6a4SAndroid Build Coastguard Worker                 core.offset,
65*bb4ee6a4SAndroid Build Coastguard Worker                 limit
66*bb4ee6a4SAndroid Build Coastguard Worker             ))
67*bb4ee6a4SAndroid Build Coastguard Worker         }
68*bb4ee6a4SAndroid Build Coastguard Worker     }
69*bb4ee6a4SAndroid Build Coastguard Worker }
70*bb4ee6a4SAndroid Build Coastguard Worker 
71*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Default, Debug, Clone, Eq, PartialEq)]
72*bb4ee6a4SAndroid Build Coastguard Worker pub struct CoreGrouping {
73*bb4ee6a4SAndroid Build Coastguard Worker     groups: Vec<CoreGroup>,
74*bb4ee6a4SAndroid Build Coastguard Worker }
75*bb4ee6a4SAndroid Build Coastguard Worker 
76*bb4ee6a4SAndroid Build Coastguard Worker impl Ord for CoreGrouping {
cmp(&self, other: &Self) -> Ordering77*bb4ee6a4SAndroid Build Coastguard Worker     fn cmp(&self, other: &Self) -> Ordering {
78*bb4ee6a4SAndroid Build Coastguard Worker         // Ordered by largest group size descending, then by number of groups ascending
79*bb4ee6a4SAndroid Build Coastguard Worker         (Reverse(self.largest_group().size()), self.groups.len())
80*bb4ee6a4SAndroid Build Coastguard Worker             .cmp(&(Reverse(other.largest_group().size()), other.groups.len()))
81*bb4ee6a4SAndroid Build Coastguard Worker     }
82*bb4ee6a4SAndroid Build Coastguard Worker }
83*bb4ee6a4SAndroid Build Coastguard Worker 
84*bb4ee6a4SAndroid Build Coastguard Worker impl PartialOrd for CoreGrouping {
partial_cmp(&self, other: &Self) -> Option<Ordering>85*bb4ee6a4SAndroid Build Coastguard Worker     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
86*bb4ee6a4SAndroid Build Coastguard Worker         Some(self.cmp(other))
87*bb4ee6a4SAndroid Build Coastguard Worker     }
88*bb4ee6a4SAndroid Build Coastguard Worker }
89*bb4ee6a4SAndroid Build Coastguard Worker 
90*bb4ee6a4SAndroid Build Coastguard Worker impl CoreGrouping {
new(groups: Vec<CoreGroup>) -> Result<Self>91*bb4ee6a4SAndroid Build Coastguard Worker     pub(super) fn new(groups: Vec<CoreGroup>) -> Result<Self> {
92*bb4ee6a4SAndroid Build Coastguard Worker         // Other functions in this type rely on the fact that there is at least one CoreGroup.
93*bb4ee6a4SAndroid Build Coastguard Worker         if groups.is_empty() {
94*bb4ee6a4SAndroid Build Coastguard Worker             return Err(anyhow!("Cannot create an empty CoreGrouping."));
95*bb4ee6a4SAndroid Build Coastguard Worker         }
96*bb4ee6a4SAndroid Build Coastguard Worker         Ok(CoreGrouping { groups })
97*bb4ee6a4SAndroid Build Coastguard Worker     }
98*bb4ee6a4SAndroid Build Coastguard Worker 
size(&self) -> usize99*bb4ee6a4SAndroid Build Coastguard Worker     pub fn size(&self) -> usize {
100*bb4ee6a4SAndroid Build Coastguard Worker         self.groups.len()
101*bb4ee6a4SAndroid Build Coastguard Worker     }
102*bb4ee6a4SAndroid Build Coastguard Worker 
largest_group_index(&self) -> usize103*bb4ee6a4SAndroid Build Coastguard Worker     pub fn largest_group_index(&self) -> usize {
104*bb4ee6a4SAndroid Build Coastguard Worker         // Sort by Reverse(size) then group index, and take the min.
105*bb4ee6a4SAndroid Build Coastguard Worker         // We could sort by size then Reverse(index), but then getting the index out is hard.
106*bb4ee6a4SAndroid Build Coastguard Worker         self.groups
107*bb4ee6a4SAndroid Build Coastguard Worker             .iter()
108*bb4ee6a4SAndroid Build Coastguard Worker             .enumerate()
109*bb4ee6a4SAndroid Build Coastguard Worker             .map(|(i, g)| (Reverse(g.size()), i))
110*bb4ee6a4SAndroid Build Coastguard Worker             .min()
111*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap_or((Reverse(0), 0))
112*bb4ee6a4SAndroid Build Coastguard Worker             .1
113*bb4ee6a4SAndroid Build Coastguard Worker     }
114*bb4ee6a4SAndroid Build Coastguard Worker 
largest_group(&self) -> &CoreGroup115*bb4ee6a4SAndroid Build Coastguard Worker     pub fn largest_group(&self) -> &CoreGroup {
116*bb4ee6a4SAndroid Build Coastguard Worker         &self.groups[self.largest_group_index()]
117*bb4ee6a4SAndroid Build Coastguard Worker     }
118*bb4ee6a4SAndroid Build Coastguard Worker 
core_grouping_bitmask(&self) -> u64119*bb4ee6a4SAndroid Build Coastguard Worker     pub fn core_grouping_bitmask(&self) -> u64 {
120*bb4ee6a4SAndroid Build Coastguard Worker         // If there's only one group, then all cores are in sync
121*bb4ee6a4SAndroid Build Coastguard Worker         if self.size() == 0 {
122*bb4ee6a4SAndroid Build Coastguard Worker             return 0;
123*bb4ee6a4SAndroid Build Coastguard Worker         }
124*bb4ee6a4SAndroid Build Coastguard Worker 
125*bb4ee6a4SAndroid Build Coastguard Worker         let mut bitmask = 0u64;
126*bb4ee6a4SAndroid Build Coastguard Worker         let largest_group_index = self.largest_group_index();
127*bb4ee6a4SAndroid Build Coastguard Worker         for (i, group) in self.groups.iter().enumerate() {
128*bb4ee6a4SAndroid Build Coastguard Worker             // The largest group is considered the in-sync group
129*bb4ee6a4SAndroid Build Coastguard Worker             if i == largest_group_index {
130*bb4ee6a4SAndroid Build Coastguard Worker                 continue;
131*bb4ee6a4SAndroid Build Coastguard Worker             }
132*bb4ee6a4SAndroid Build Coastguard Worker 
133*bb4ee6a4SAndroid Build Coastguard Worker             // Set the bitmask to 1 for all cores in all other groups
134*bb4ee6a4SAndroid Build Coastguard Worker             for core in &group.cores {
135*bb4ee6a4SAndroid Build Coastguard Worker                 if core.core > 63 {
136*bb4ee6a4SAndroid Build Coastguard Worker                     warn!("Core grouping bitmask cannot contain core {}", core.core);
137*bb4ee6a4SAndroid Build Coastguard Worker                     continue;
138*bb4ee6a4SAndroid Build Coastguard Worker                 }
139*bb4ee6a4SAndroid Build Coastguard Worker                 bitmask |= 1 << core.core;
140*bb4ee6a4SAndroid Build Coastguard Worker             }
141*bb4ee6a4SAndroid Build Coastguard Worker         }
142*bb4ee6a4SAndroid Build Coastguard Worker 
143*bb4ee6a4SAndroid Build Coastguard Worker         bitmask
144*bb4ee6a4SAndroid Build Coastguard Worker     }
145*bb4ee6a4SAndroid Build Coastguard Worker 
add_group(&mut self, group: CoreGroup)146*bb4ee6a4SAndroid Build Coastguard Worker     fn add_group(&mut self, group: CoreGroup) {
147*bb4ee6a4SAndroid Build Coastguard Worker         self.groups.push(group)
148*bb4ee6a4SAndroid Build Coastguard Worker     }
149*bb4ee6a4SAndroid Build Coastguard Worker 
add_core_to_last_group(&self, core: CoreOffset, in_sync_threshold: u128) -> Option<Self>150*bb4ee6a4SAndroid Build Coastguard Worker     fn add_core_to_last_group(&self, core: CoreOffset, in_sync_threshold: u128) -> Option<Self> {
151*bb4ee6a4SAndroid Build Coastguard Worker         let last_group = self.groups.len() - 1;
152*bb4ee6a4SAndroid Build Coastguard Worker         self.groups[last_group]
153*bb4ee6a4SAndroid Build Coastguard Worker             .add(core, in_sync_threshold)
154*bb4ee6a4SAndroid Build Coastguard Worker             .map(|new_group| {
155*bb4ee6a4SAndroid Build Coastguard Worker                 let mut new_grouping = self.clone();
156*bb4ee6a4SAndroid Build Coastguard Worker                 new_grouping.groups[last_group] = new_group;
157*bb4ee6a4SAndroid Build Coastguard Worker                 new_grouping
158*bb4ee6a4SAndroid Build Coastguard Worker             })
159*bb4ee6a4SAndroid Build Coastguard Worker             .ok()
160*bb4ee6a4SAndroid Build Coastguard Worker     }
161*bb4ee6a4SAndroid Build Coastguard Worker }
162*bb4ee6a4SAndroid Build Coastguard Worker 
163*bb4ee6a4SAndroid Build Coastguard Worker /// Group cores by their offsets that are within `in_sync_threshold` of each other.
164*bb4ee6a4SAndroid Build Coastguard Worker ///
165*bb4ee6a4SAndroid Build Coastguard Worker /// This uses a generic integer grouping algorithm. Because we're grouping within a threshold,
166*bb4ee6a4SAndroid Build Coastguard Worker /// there are potentially multiple ways we can group the integers, and we want to find the "best"
167*bb4ee6a4SAndroid Build Coastguard Worker /// grouping. Our definition of best grouping is:
168*bb4ee6a4SAndroid Build Coastguard Worker ///   1. The grouping who's largest group is the largest.
169*bb4ee6a4SAndroid Build Coastguard Worker ///   2. If there are multiple groupings with the same size largest group, take the one with the
170*bb4ee6a4SAndroid Build Coastguard Worker ///      fewest groups.
171*bb4ee6a4SAndroid Build Coastguard Worker ///
172*bb4ee6a4SAndroid Build Coastguard Worker /// We could naively generate all possible groupings by iterating through the sorted integers and,
173*bb4ee6a4SAndroid Build Coastguard Worker /// for each grouping, either adding that integer as it's own group or adding it to the last group
174*bb4ee6a4SAndroid Build Coastguard Worker /// of that grouping. This could generate 2^N groupings, where N is the number of integers. Instead,
175*bb4ee6a4SAndroid Build Coastguard Worker /// we still iterate through the sorted integers and, for each grouping, we add it to the last
176*bb4ee6a4SAndroid Build Coastguard Worker /// group of that grouping. But we only add the integer as it's own group to the current best
177*bb4ee6a4SAndroid Build Coastguard Worker /// grouping. This optimization avoids creating groupings that we know will not be the "best"
178*bb4ee6a4SAndroid Build Coastguard Worker /// grouping, because adding the integer as it's own group means that all subsequent integers
179*bb4ee6a4SAndroid Build Coastguard Worker /// cannot be grouped with the existing groups, and thus we only care about the optimal existing
180*bb4ee6a4SAndroid Build Coastguard Worker /// groups.
group_core_offsets( offsets: &[(usize, i128)], in_sync_threshold: Duration, tsc_frequency: u64, ) -> Result<CoreGrouping>181*bb4ee6a4SAndroid Build Coastguard Worker pub(super) fn group_core_offsets(
182*bb4ee6a4SAndroid Build Coastguard Worker     offsets: &[(usize, i128)],
183*bb4ee6a4SAndroid Build Coastguard Worker     in_sync_threshold: Duration,
184*bb4ee6a4SAndroid Build Coastguard Worker     tsc_frequency: u64,
185*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<CoreGrouping> {
186*bb4ee6a4SAndroid Build Coastguard Worker     if offsets.is_empty() {
187*bb4ee6a4SAndroid Build Coastguard Worker         bail!("Per-core offsets cannot be empty");
188*bb4ee6a4SAndroid Build Coastguard Worker     }
189*bb4ee6a4SAndroid Build Coastguard Worker 
190*bb4ee6a4SAndroid Build Coastguard Worker     // Convert threshold to TSC ticks
191*bb4ee6a4SAndroid Build Coastguard Worker     let in_sync_threshold_ticks =
192*bb4ee6a4SAndroid Build Coastguard Worker         in_sync_threshold.as_nanos() * tsc_frequency as u128 / 1_000_000_000u128;
193*bb4ee6a4SAndroid Build Coastguard Worker 
194*bb4ee6a4SAndroid Build Coastguard Worker     let mut cores: Vec<CoreOffset> = offsets
195*bb4ee6a4SAndroid Build Coastguard Worker         .iter()
196*bb4ee6a4SAndroid Build Coastguard Worker         .map(|(i, offset)| CoreOffset {
197*bb4ee6a4SAndroid Build Coastguard Worker             core: *i,
198*bb4ee6a4SAndroid Build Coastguard Worker             offset: *offset,
199*bb4ee6a4SAndroid Build Coastguard Worker         })
200*bb4ee6a4SAndroid Build Coastguard Worker         .collect();
201*bb4ee6a4SAndroid Build Coastguard Worker 
202*bb4ee6a4SAndroid Build Coastguard Worker     // Cores are sorted by their ascending by their offset then ascending by their core #. See the
203*bb4ee6a4SAndroid Build Coastguard Worker     // Ord implementation for CoreOffset.
204*bb4ee6a4SAndroid Build Coastguard Worker     cores.sort();
205*bb4ee6a4SAndroid Build Coastguard Worker 
206*bb4ee6a4SAndroid Build Coastguard Worker     let mut grouping_options: Vec<CoreGrouping> = vec![CoreGrouping::new(vec![CoreGroup {
207*bb4ee6a4SAndroid Build Coastguard Worker         cores: vec![cores[0]],
208*bb4ee6a4SAndroid Build Coastguard Worker     }])?];
209*bb4ee6a4SAndroid Build Coastguard Worker     for core in &cores[1..] {
210*bb4ee6a4SAndroid Build Coastguard Worker         let mut best = grouping_options[0].clone();
211*bb4ee6a4SAndroid Build Coastguard Worker         best.add_group(CoreGroup { cores: vec![*core] });
212*bb4ee6a4SAndroid Build Coastguard Worker 
213*bb4ee6a4SAndroid Build Coastguard Worker         let mut next_grouping_options = vec![best];
214*bb4ee6a4SAndroid Build Coastguard Worker 
215*bb4ee6a4SAndroid Build Coastguard Worker         for grouping_option in &grouping_options {
216*bb4ee6a4SAndroid Build Coastguard Worker             if let Some(new_grouping) =
217*bb4ee6a4SAndroid Build Coastguard Worker                 grouping_option.add_core_to_last_group(*core, in_sync_threshold_ticks)
218*bb4ee6a4SAndroid Build Coastguard Worker             {
219*bb4ee6a4SAndroid Build Coastguard Worker                 next_grouping_options.push(new_grouping);
220*bb4ee6a4SAndroid Build Coastguard Worker             }
221*bb4ee6a4SAndroid Build Coastguard Worker         }
222*bb4ee6a4SAndroid Build Coastguard Worker 
223*bb4ee6a4SAndroid Build Coastguard Worker         next_grouping_options.sort();
224*bb4ee6a4SAndroid Build Coastguard Worker         grouping_options = next_grouping_options;
225*bb4ee6a4SAndroid Build Coastguard Worker     }
226*bb4ee6a4SAndroid Build Coastguard Worker 
227*bb4ee6a4SAndroid Build Coastguard Worker     Ok(grouping_options[0].clone())
228*bb4ee6a4SAndroid Build Coastguard Worker }
229*bb4ee6a4SAndroid Build Coastguard Worker 
230*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)]
231*bb4ee6a4SAndroid Build Coastguard Worker mod tests {
232*bb4ee6a4SAndroid Build Coastguard Worker     use super::super::TscState;
233*bb4ee6a4SAndroid Build Coastguard Worker     use super::*;
234*bb4ee6a4SAndroid Build Coastguard Worker 
235*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_simple_offset_grouping()236*bb4ee6a4SAndroid Build Coastguard Worker     fn test_simple_offset_grouping() {
237*bb4ee6a4SAndroid Build Coastguard Worker         let offsets = vec![(0, 10), (1, 10), (2, 10), (3, 1000)];
238*bb4ee6a4SAndroid Build Coastguard Worker         let state = TscState::new(1_000_000_000, offsets, Duration::from_nanos(1))
239*bb4ee6a4SAndroid Build Coastguard Worker             .expect("TscState::new should not fail for this test");
240*bb4ee6a4SAndroid Build Coastguard Worker 
241*bb4ee6a4SAndroid Build Coastguard Worker         let group0 = CoreGroup {
242*bb4ee6a4SAndroid Build Coastguard Worker             cores: vec![
243*bb4ee6a4SAndroid Build Coastguard Worker                 CoreOffset {
244*bb4ee6a4SAndroid Build Coastguard Worker                     core: 0,
245*bb4ee6a4SAndroid Build Coastguard Worker                     offset: 10,
246*bb4ee6a4SAndroid Build Coastguard Worker                 },
247*bb4ee6a4SAndroid Build Coastguard Worker                 CoreOffset {
248*bb4ee6a4SAndroid Build Coastguard Worker                     core: 1,
249*bb4ee6a4SAndroid Build Coastguard Worker                     offset: 10,
250*bb4ee6a4SAndroid Build Coastguard Worker                 },
251*bb4ee6a4SAndroid Build Coastguard Worker                 CoreOffset {
252*bb4ee6a4SAndroid Build Coastguard Worker                     core: 2,
253*bb4ee6a4SAndroid Build Coastguard Worker                     offset: 10,
254*bb4ee6a4SAndroid Build Coastguard Worker                 },
255*bb4ee6a4SAndroid Build Coastguard Worker             ],
256*bb4ee6a4SAndroid Build Coastguard Worker         };
257*bb4ee6a4SAndroid Build Coastguard Worker         let group1 = CoreGroup {
258*bb4ee6a4SAndroid Build Coastguard Worker             cores: vec![CoreOffset {
259*bb4ee6a4SAndroid Build Coastguard Worker                 core: 3,
260*bb4ee6a4SAndroid Build Coastguard Worker                 offset: 1000,
261*bb4ee6a4SAndroid Build Coastguard Worker             }],
262*bb4ee6a4SAndroid Build Coastguard Worker         };
263*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
264*bb4ee6a4SAndroid Build Coastguard Worker             state.core_grouping,
265*bb4ee6a4SAndroid Build Coastguard Worker             CoreGrouping::new(vec![group0.clone(), group1])
266*bb4ee6a4SAndroid Build Coastguard Worker                 .expect("CoreGrouping::new should not fail here")
267*bb4ee6a4SAndroid Build Coastguard Worker         );
268*bb4ee6a4SAndroid Build Coastguard Worker 
269*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(state.core_grouping.largest_group().clone(), group0);
270*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(state.core_grouping.core_grouping_bitmask(), 0b1000u64);
271*bb4ee6a4SAndroid Build Coastguard Worker     }
272*bb4ee6a4SAndroid Build Coastguard Worker 
273*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_ambiguous_offset_grouping()274*bb4ee6a4SAndroid Build Coastguard Worker     fn test_ambiguous_offset_grouping() {
275*bb4ee6a4SAndroid Build Coastguard Worker         // Could be grouped in several ways:
276*bb4ee6a4SAndroid Build Coastguard Worker         //  - [10, 20] [30, 40] [50] <--- we like to have core0 be in a larger group
277*bb4ee6a4SAndroid Build Coastguard Worker         //  - [10] [20, 30] [40, 50]
278*bb4ee6a4SAndroid Build Coastguard Worker         //  - [10, 20] [30] [40, 50]
279*bb4ee6a4SAndroid Build Coastguard Worker         let offsets = vec![(0, 10), (1, 20), (2, 30), (3, 40), (4, 50)];
280*bb4ee6a4SAndroid Build Coastguard Worker         let state = TscState::new(1_000_000_000, offsets, Duration::from_nanos(20))
281*bb4ee6a4SAndroid Build Coastguard Worker             .expect("TscState::new should not fail for this test");
282*bb4ee6a4SAndroid Build Coastguard Worker 
283*bb4ee6a4SAndroid Build Coastguard Worker         let group0 = CoreGroup {
284*bb4ee6a4SAndroid Build Coastguard Worker             cores: vec![
285*bb4ee6a4SAndroid Build Coastguard Worker                 CoreOffset {
286*bb4ee6a4SAndroid Build Coastguard Worker                     core: 0,
287*bb4ee6a4SAndroid Build Coastguard Worker                     offset: 10,
288*bb4ee6a4SAndroid Build Coastguard Worker                 },
289*bb4ee6a4SAndroid Build Coastguard Worker                 CoreOffset {
290*bb4ee6a4SAndroid Build Coastguard Worker                     core: 1,
291*bb4ee6a4SAndroid Build Coastguard Worker                     offset: 20,
292*bb4ee6a4SAndroid Build Coastguard Worker                 },
293*bb4ee6a4SAndroid Build Coastguard Worker             ],
294*bb4ee6a4SAndroid Build Coastguard Worker         };
295*bb4ee6a4SAndroid Build Coastguard Worker         let group1 = CoreGroup {
296*bb4ee6a4SAndroid Build Coastguard Worker             cores: vec![
297*bb4ee6a4SAndroid Build Coastguard Worker                 CoreOffset {
298*bb4ee6a4SAndroid Build Coastguard Worker                     core: 2,
299*bb4ee6a4SAndroid Build Coastguard Worker                     offset: 30,
300*bb4ee6a4SAndroid Build Coastguard Worker                 },
301*bb4ee6a4SAndroid Build Coastguard Worker                 CoreOffset {
302*bb4ee6a4SAndroid Build Coastguard Worker                     core: 3,
303*bb4ee6a4SAndroid Build Coastguard Worker                     offset: 40,
304*bb4ee6a4SAndroid Build Coastguard Worker                 },
305*bb4ee6a4SAndroid Build Coastguard Worker             ],
306*bb4ee6a4SAndroid Build Coastguard Worker         };
307*bb4ee6a4SAndroid Build Coastguard Worker         let group2 = CoreGroup {
308*bb4ee6a4SAndroid Build Coastguard Worker             cores: vec![CoreOffset {
309*bb4ee6a4SAndroid Build Coastguard Worker                 core: 4,
310*bb4ee6a4SAndroid Build Coastguard Worker                 offset: 50,
311*bb4ee6a4SAndroid Build Coastguard Worker             }],
312*bb4ee6a4SAndroid Build Coastguard Worker         };
313*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
314*bb4ee6a4SAndroid Build Coastguard Worker             state.core_grouping,
315*bb4ee6a4SAndroid Build Coastguard Worker             CoreGrouping::new(vec![group0.clone(), group1, group2])
316*bb4ee6a4SAndroid Build Coastguard Worker                 .expect("CoreGrouping::new should not fail here")
317*bb4ee6a4SAndroid Build Coastguard Worker         );
318*bb4ee6a4SAndroid Build Coastguard Worker 
319*bb4ee6a4SAndroid Build Coastguard Worker         // largest_group should return the first group over other equally large groups
320*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(state.core_grouping.largest_group().clone(), group0);
321*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(state.core_grouping.core_grouping_bitmask(), 0b11100u64);
322*bb4ee6a4SAndroid Build Coastguard Worker     }
323*bb4ee6a4SAndroid Build Coastguard Worker 
324*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_worst_case_grouping()325*bb4ee6a4SAndroid Build Coastguard Worker     fn test_worst_case_grouping() {
326*bb4ee6a4SAndroid Build Coastguard Worker         // Worst case for the grouping algorithm, where if your algorithm isn't smart you can
327*bb4ee6a4SAndroid Build Coastguard Worker         // generate 2^129 groupings, which would be too large for a Vec to hold. This test just
328*bb4ee6a4SAndroid Build Coastguard Worker         // verifies that we don't crash or run out of memory.
329*bb4ee6a4SAndroid Build Coastguard Worker         let offsets = (0..129).map(|i| (i, 0)).collect();
330*bb4ee6a4SAndroid Build Coastguard Worker         TscState::new(1_000_000_000, offsets, Duration::from_nanos(1))
331*bb4ee6a4SAndroid Build Coastguard Worker             .expect("TscState::new should not fail for this test");
332*bb4ee6a4SAndroid Build Coastguard Worker     }
333*bb4ee6a4SAndroid Build Coastguard Worker }
334