1use anyhow::anyhow;
10use anyhow::Result;
11use combinations::Combinations;
12use itertools::iproduct;
13use scx_utils::CoreType;
14use scx_utils::Cpumask;
15use scx_utils::EnergyModel;
16use scx_utils::PerfDomain;
17use scx_utils::PerfState;
18use scx_utils::Topology;
19use scx_utils::NR_CPU_IDS;
20use std::cell::Cell;
21use std::cell::RefCell;
22use std::collections::BTreeMap;
23use std::collections::BTreeSet;
24use std::collections::HashSet;
25use std::fmt;
26use std::hash::{Hash, Hasher};
27use tracing::debug;
28
29#[derive(Debug, Clone)]
30pub struct CpuId {
31 pub numa_adx: usize,
41 pub pd_adx: usize,
42 pub llc_adx: usize,
43 pub llc_rdx: usize,
44 pub llc_kernel_id: usize,
45 pub core_rdx: usize,
46 pub cpu_rdx: usize,
47 pub cpu_adx: usize,
48 pub smt_level: usize,
49 pub cache_size: usize,
50 pub cpu_cap: usize,
51 pub big_core: bool,
52 pub turbo_core: bool,
53 pub cpu_sibling: usize,
54}
55
56#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone)]
57pub struct ComputeDomainId {
58 pub numa_adx: usize,
59 pub llc_adx: usize,
60 pub llc_rdx: usize,
61 pub llc_kernel_id: usize,
62 pub is_big: bool,
63}
64
65#[derive(Debug, Clone)]
66pub struct ComputeDomain {
67 pub cpdom_id: usize,
68 pub cpdom_alt_id: Cell<usize>,
69 pub cpu_ids: Vec<usize>,
70 pub neighbor_map: RefCell<BTreeMap<usize, RefCell<Vec<usize>>>>,
71}
72
73#[derive(Debug, Clone)]
74#[allow(dead_code)]
75pub struct PerfCpuOrder {
76 pub perf_cap: usize, pub perf_util: f32, pub cpus_perf: RefCell<Vec<usize>>, pub cpus_ovflw: RefCell<Vec<usize>>, }
81
82#[derive(Debug)]
83#[allow(dead_code)]
84pub struct CpuOrder {
85 pub all_cpus_mask: Cpumask,
86 pub cpuids: Vec<CpuId>,
87 pub perf_cpu_order: BTreeMap<usize, PerfCpuOrder>,
88 pub cpdom_map: BTreeMap<ComputeDomainId, ComputeDomain>,
89 pub nr_cpus: usize,
90 pub nr_cores: usize,
91 pub nr_cpdoms: usize,
92 pub nr_llcs: usize,
93 pub nr_numa: usize,
94 pub smt_enabled: bool,
95 pub has_biglittle: bool,
96 pub has_energy_model: bool,
97}
98
99impl CpuOrder {
100 pub fn new(
105 topology_args: Option<&scx_utils::TopologyArgs>,
106 no_use_em: bool,
107 ) -> Result<CpuOrder> {
108 let ctx = CpuOrderCtx::new(topology_args, no_use_em)?;
109 let cpus_pf = ctx.build_topo_order(false).unwrap();
110 let cpus_ps = ctx.build_topo_order(true).unwrap();
111 let cpdom_map = CpuOrderCtx::build_cpdom(&cpus_pf).unwrap();
112 let perf_cpu_order = if ctx.em.is_ok() {
113 let em = ctx.em.unwrap();
114 EnergyModelOptimizer::get_perf_cpu_order_table(&em, &cpus_pf)
115 } else {
116 EnergyModelOptimizer::get_fake_perf_cpu_order_table(&cpus_pf, &cpus_ps)
117 };
118
119 let nr_cpdoms = cpdom_map.len();
120 Ok(CpuOrder {
121 all_cpus_mask: ctx.topo.span,
122 cpuids: cpus_pf,
123 perf_cpu_order,
124 cpdom_map,
125 nr_cpus: ctx.topo.all_cpus.len(),
126 nr_cores: ctx.topo.all_cores.len(),
127 nr_cpdoms,
128 nr_llcs: ctx.topo.all_llcs.len(),
129 nr_numa: ctx.topo.nodes.len(),
130 smt_enabled: ctx.smt_enabled,
131 has_biglittle: ctx.has_biglittle,
132 has_energy_model: ctx.has_energy_model,
133 })
134 }
135}
136
137struct CpuOrderCtx {
139 topo: Topology,
140 em: Result<EnergyModel>,
141 smt_enabled: bool,
142 has_biglittle: bool,
143 has_energy_model: bool,
144}
145
146impl CpuOrderCtx {
147 fn new(topology_args: Option<&scx_utils::TopologyArgs>, no_use_em: bool) -> Result<Self> {
148 let topo = match topology_args {
149 Some(args) => Topology::with_args(args)?,
150 None => Topology::new()?,
151 };
152
153 let em = if no_use_em {
154 Err(anyhow!("energy model disabled (--no-use-em)"))
155 } else {
156 EnergyModel::new()
157 };
158 let smt_enabled = topo.smt_enabled;
159 let has_biglittle = topo.has_little_cores();
160 let has_energy_model = em.is_ok();
161
162 debug!("{:#?}", topo);
163 debug!("{:#?}", em);
164
165 Ok(CpuOrderCtx {
166 topo,
167 em,
168 smt_enabled,
169 has_biglittle,
170 has_energy_model,
171 })
172 }
173
174 fn build_topo_order(&self, prefer_powersave: bool) -> Option<Vec<CpuId>> {
176 let mut cpu_ids = Vec::new();
177 let smt_siblings = self.topo.sibling_cpus();
178
179 for (&numa_adx, node) in self.topo.nodes.iter() {
181 for (llc_rdx, (&llc_adx, llc)) in node.llcs.iter().enumerate() {
182 for (core_rdx, (_core_adx, core)) in llc.cores.iter().enumerate() {
183 for (cpu_rdx, (cpu_adx, cpu)) in core.cpus.iter().enumerate() {
184 let cpu_adx = *cpu_adx;
185 let pd_adx = Self::get_pd_id(&self.em, cpu_adx, llc_adx);
186 let cpu_id = CpuId {
187 numa_adx,
188 pd_adx,
189 llc_adx,
190 llc_rdx,
191 core_rdx,
192 cpu_rdx,
193 cpu_adx,
194 smt_level: cpu.smt_level,
195 cache_size: cpu.cache_size,
196 cpu_cap: cpu.cpu_capacity,
197 big_core: cpu.core_type != CoreType::Little,
198 turbo_core: cpu.core_type == CoreType::Big { turbo: true },
199 cpu_sibling: smt_siblings[cpu_adx] as usize,
200 llc_kernel_id: llc.kernel_id,
201 };
202 cpu_ids.push(RefCell::new(cpu_id));
203 }
204 }
205 }
206 }
207
208 let mut cpu_ids2 = Vec::new();
210 for cpu_id in cpu_ids.iter() {
211 cpu_ids2.push(cpu_id.borrow().clone());
212 }
213 let mut cpu_ids = cpu_ids2;
214
215 match (prefer_powersave, self.has_biglittle) {
217 (true, false) => {
226 cpu_ids.sort_by(|a, b| {
227 a.numa_adx
228 .cmp(&b.numa_adx)
229 .then_with(|| a.llc_rdx.cmp(&b.llc_rdx))
230 .then_with(|| b.cpu_cap.cmp(&a.cpu_cap))
231 .then_with(|| b.cache_size.cmp(&a.cache_size))
232 .then_with(|| a.pd_adx.cmp(&b.pd_adx))
233 .then_with(|| a.core_rdx.cmp(&b.core_rdx))
234 .then_with(|| b.smt_level.cmp(&a.smt_level))
235 .then_with(|| a.cpu_rdx.cmp(&b.cpu_rdx))
236 .then_with(|| a.cpu_adx.cmp(&b.cpu_adx))
237 });
238 }
239 (true, true) => {
247 cpu_ids.sort_by(|a, b| {
248 a.numa_adx
249 .cmp(&b.numa_adx)
250 .then_with(|| a.llc_rdx.cmp(&b.llc_rdx))
251 .then_with(|| a.cpu_cap.cmp(&b.cpu_cap))
252 .then_with(|| b.cache_size.cmp(&a.cache_size))
253 .then_with(|| a.pd_adx.cmp(&b.pd_adx))
254 .then_with(|| a.core_rdx.cmp(&b.core_rdx))
255 .then_with(|| b.smt_level.cmp(&a.smt_level))
256 .then_with(|| a.cpu_rdx.cmp(&b.cpu_rdx))
257 .then_with(|| a.cpu_adx.cmp(&b.cpu_adx))
258 });
259 }
260 _ => {
272 cpu_ids.sort_by(|a, b| {
273 a.cpu_rdx
274 .cmp(&b.cpu_rdx)
275 .then_with(|| a.numa_adx.cmp(&b.numa_adx))
276 .then_with(|| a.llc_rdx.cmp(&b.llc_rdx))
277 .then_with(|| b.cpu_cap.cmp(&a.cpu_cap))
278 .then_with(|| b.cache_size.cmp(&a.cache_size))
279 .then_with(|| a.smt_level.cmp(&b.smt_level))
280 .then_with(|| a.pd_adx.cmp(&b.pd_adx))
281 .then_with(|| a.core_rdx.cmp(&b.core_rdx))
282 .then_with(|| a.cpu_adx.cmp(&b.cpu_adx))
283 });
284 }
285 }
286
287 Some(cpu_ids)
288 }
289
290 fn build_cpdom(cpu_ids: &Vec<CpuId>) -> Option<BTreeMap<ComputeDomainId, ComputeDomain>> {
292 let mut cpdom_id = 0;
298 let mut cpdom_map: BTreeMap<ComputeDomainId, ComputeDomain> = BTreeMap::new();
299 let mut cpdom_types: BTreeMap<usize, bool> = BTreeMap::new();
300 for cpu_id in cpu_ids.iter() {
301 let key = ComputeDomainId {
302 numa_adx: cpu_id.numa_adx,
303 llc_adx: cpu_id.llc_adx,
304 llc_rdx: cpu_id.llc_rdx,
305 llc_kernel_id: cpu_id.llc_kernel_id,
306 is_big: cpu_id.big_core,
307 };
308 let value = cpdom_map.entry(key.clone()).or_insert_with(|| {
309 let val = ComputeDomain {
310 cpdom_id,
311 cpdom_alt_id: Cell::new(cpdom_id),
312 cpu_ids: Vec::new(),
313 neighbor_map: RefCell::new(BTreeMap::new()),
314 };
315 cpdom_types.insert(cpdom_id, key.is_big);
316
317 cpdom_id += 1;
318 val
319 });
320 value.cpu_ids.push(cpu_id.cpu_adx);
321 }
322
323 for ((from_k, from_v), (to_k, to_v)) in iproduct!(cpdom_map.iter(), cpdom_map.iter()) {
326 if from_k == to_k {
327 continue;
328 }
329
330 let d = Self::dist(from_k, to_k);
331 let mut map = from_v.neighbor_map.borrow_mut();
332 match map.get(&d) {
333 Some(v) => {
334 v.borrow_mut().push(to_v.cpdom_id);
335 }
336 None => {
337 map.insert(d, RefCell::new(vec![to_v.cpdom_id]));
338 }
339 }
340 }
341
342 for (_, cpdom) in cpdom_map.iter() {
360 for (_, neighbors) in cpdom.neighbor_map.borrow_mut().iter() {
361 let mut neighbors_csorted =
362 Self::circular_sort(cpdom.cpdom_id, &neighbors.borrow_mut().to_vec());
363 neighbors.borrow_mut().clear();
364 neighbors.borrow_mut().append(&mut neighbors_csorted);
365 }
366 }
367
368 for (k, v) in cpdom_map.iter() {
370 let mut key = k.clone();
371 key.is_big = !k.is_big;
372
373 if let Some(alt_v) = cpdom_map.get(&key) {
374 v.cpdom_alt_id.set(alt_v.cpdom_id);
377 } else {
378 'outer: for (_dist, ncpdoms) in v.neighbor_map.borrow().iter() {
385 for ncpdom_id in ncpdoms.borrow().iter() {
386 if let Some(is_big) = cpdom_types.get(ncpdom_id) {
387 if *is_big == key.is_big {
388 v.cpdom_alt_id.set(*ncpdom_id);
389 break 'outer;
390 }
391 }
392 }
393 }
394 }
395 }
396
397 Some(cpdom_map)
398 }
399
400 fn circular_sort(start: usize, the_rest: &Vec<usize>) -> Vec<usize> {
402 let mut list = the_rest.clone();
404 list.push(start);
405 list.sort();
406
407 let s = list
409 .binary_search(&start)
410 .expect("start must appear exactly once");
411
412 let n = list.len();
414 let dist = |x: usize| {
415 let d = (x + n - s) % n;
416 d.min(n - d)
417 };
418 let mut order: Vec<usize> = (0..n).collect();
419 order.sort_by_key(|&x| (dist(x), x));
420
421 let list_csorted: Vec<_> = order.iter().map(|&i| list[i]).collect();
424
425 list_csorted[1..].to_vec()
427 }
428
429 fn get_pd_id(em: &Result<EnergyModel>, cpu_adx: usize, llc_adx: usize) -> usize {
432 match em {
433 Ok(em) => em.get_pd_by_cpu_id(cpu_adx).unwrap().id,
434 Err(_) => llc_adx,
435 }
436 }
437
438 fn dist(from: &ComputeDomainId, to: &ComputeDomainId) -> usize {
440 let mut d = 0;
441 if from.is_big != to.is_big {
443 d += 100;
444 }
445 if from.numa_adx != to.numa_adx {
446 d += 10;
447 } else {
448 if from.llc_rdx != to.llc_rdx {
449 d += 1;
450 }
451 if from.llc_kernel_id != to.llc_kernel_id {
452 d += 1;
453 }
454 }
455 d
456 }
457}
458
459#[derive(Debug)]
460struct EnergyModelOptimizer<'a> {
461 em: &'a EnergyModel,
463
464 cpus_topological_order: Vec<usize>,
466
467 pd_cpu_order: BTreeMap<usize, RefCell<Vec<usize>>>,
469
470 tot_perf: usize,
472
473 pdss_infos: RefCell<BTreeMap<usize, RefCell<HashSet<PDSetInfo<'a>>>>>,
476
477 perf_pdsi: RefCell<BTreeMap<usize, PDSetInfo<'a>>>,
480
481 perf_cpu_order: RefCell<BTreeMap<usize, PerfCpuOrder>>,
483}
484
485#[derive(Debug, Clone, Eq, Hash, Ord, PartialOrd)]
486struct PDS<'a> {
487 pd: &'a PerfDomain,
488 ps: &'a PerfState,
489}
490
491#[derive(Debug, Clone, Eq, Hash, Ord, PartialOrd)]
492struct PDCpu<'a> {
493 pd: &'a PerfDomain, cpu_vid: usize, }
496
497#[derive(Debug, Clone, Eq)]
498struct PDSetInfo<'a> {
499 performance: usize,
500 power: usize,
501 pdcpu_set: BTreeSet<PDCpu<'a>>,
502 pd_id_set: BTreeSet<usize>, }
504
505const PD_UNIT: usize = 100_000_000;
506const CPU_UNIT: usize = 100_000;
507const LOOKAHEAD_CNT: usize = 10;
508
509impl<'a> EnergyModelOptimizer<'a> {
510 fn new(em: &'a EnergyModel, cpus_pf: &'a Vec<CpuId>) -> EnergyModelOptimizer<'a> {
511 let tot_perf = em.perf_total();
512
513 let pdss_infos: BTreeMap<usize, RefCell<HashSet<PDSetInfo<'a>>>> = BTreeMap::new();
514 let pdss_infos = pdss_infos.into();
515
516 let perf_pdsi: BTreeMap<usize, PDSetInfo<'a>> = BTreeMap::new();
517 let perf_pdsi = perf_pdsi.into();
518
519 let mut pd_cpu_order: BTreeMap<usize, RefCell<Vec<usize>>> = BTreeMap::new();
520 let mut cpus_topological_order: Vec<usize> = vec![];
521 for cpuid in cpus_pf.iter() {
522 match pd_cpu_order.get(&cpuid.pd_adx) {
523 Some(v) => {
524 let mut v = v.borrow_mut();
525 v.push(cpuid.cpu_adx);
526 }
527 None => {
528 let v = vec![cpuid.cpu_adx];
529 pd_cpu_order.insert(cpuid.pd_adx, v.into());
530 }
531 }
532 cpus_topological_order.push(cpuid.cpu_adx);
533 }
534
535 let perf_cpu_order: BTreeMap<usize, PerfCpuOrder> = BTreeMap::new();
536 let perf_cpu_order = perf_cpu_order.into();
537
538 debug!("# pd_cpu_order");
539 debug!("{:#?}", pd_cpu_order);
540
541 EnergyModelOptimizer {
542 em,
543 cpus_topological_order,
544 pd_cpu_order,
545 tot_perf,
546 pdss_infos,
547 perf_pdsi,
548 perf_cpu_order,
549 }
550 }
551
552 fn get_perf_cpu_order_table(
553 em: &'a EnergyModel,
554 cpus_pf: &'a Vec<CpuId>,
555 ) -> BTreeMap<usize, PerfCpuOrder> {
556 let emo = EnergyModelOptimizer::new(em, &cpus_pf);
557 emo.gen_perf_cpu_order_table();
558 let perf_cpu_order = emo.perf_cpu_order.borrow().clone();
559
560 perf_cpu_order
561 }
562
563 fn get_fake_perf_cpu_order_table(
564 cpus_pf: &'a Vec<CpuId>,
565 cpus_ps: &'a Vec<CpuId>,
566 ) -> BTreeMap<usize, PerfCpuOrder> {
567 let tot_perf: usize = cpus_pf.iter().map(|cpuid| cpuid.cpu_cap).sum();
568
569 let pco_pf = Self::fake_pco(tot_perf, cpus_pf, false);
570 let pco_ps = Self::fake_pco(tot_perf, cpus_ps, true);
571
572 let mut perf_cpu_order: BTreeMap<usize, PerfCpuOrder> = BTreeMap::new();
573 perf_cpu_order.insert(pco_pf.perf_cap, pco_pf);
574 perf_cpu_order.insert(pco_ps.perf_cap, pco_ps);
575
576 perf_cpu_order
577 }
578
579 fn fake_pco(tot_perf: usize, cpuids: &'a Vec<CpuId>, powersave: bool) -> PerfCpuOrder {
580 let perf_cap;
581
582 if powersave {
583 perf_cap = cpuids[0].cpu_cap;
584 } else {
585 perf_cap = tot_perf;
586 }
587
588 let perf_util: f32 = (perf_cap as f32) / (tot_perf as f32);
589 let cpus: Vec<usize> = cpuids.iter().map(|cpuid| cpuid.cpu_adx).collect();
590 let cpus_perf: Vec<usize> = cpus[..1].iter().map(|&cpuid| cpuid).collect();
591 let cpus_ovflw: Vec<usize> = cpus[1..].iter().map(|&cpuid| cpuid).collect();
592 PerfCpuOrder {
593 perf_cap,
594 perf_util,
595 cpus_perf: cpus_perf.clone().into(),
596 cpus_ovflw: cpus_ovflw.clone().into(),
597 }
598 }
599
600 fn gen_perf_cpu_order_table(&'a self) {
613 self.gen_all_pds_combinations();
619
620 self.gen_perf_pds_table();
625
626 self.assign_cpu_vids();
629 }
630
631 fn assign_cpu_vids(&'a self) {
633 for (&perf_cap, pdsi) in self.perf_pdsi.borrow().iter() {
635 let mut cpus_perf: Vec<usize> = vec![];
636
637 for pdcpu in pdsi.pdcpu_set.iter() {
638 let pd_id = pdcpu.pd.id;
639 let cpu_vid = pdcpu.cpu_vid;
640 let cpu_order = self.pd_cpu_order.get(&pd_id).unwrap().borrow();
641 let cpu_adx = cpu_order[cpu_vid];
642 cpus_perf.push(cpu_adx);
643 }
644
645 let perf_util: f32 = (perf_cap as f32) / (self.tot_perf as f32);
646 let cpus_perf = self.sort_cpus_by_topological_order(&cpus_perf);
647 let cpus_ovflw: Vec<usize> = vec![];
648
649 let mut perf_cpu_order = self.perf_cpu_order.borrow_mut();
650 perf_cpu_order.insert(
651 perf_cap,
652 PerfCpuOrder {
653 perf_cap,
654 perf_util,
655 cpus_perf: cpus_perf.clone().into(),
656 cpus_ovflw: cpus_ovflw.clone().into(),
657 },
658 );
659 }
660
661 let perf_cpu_order = self.perf_cpu_order.borrow();
663 let perf_caps: Vec<_> = self.perf_pdsi.borrow().keys().cloned().collect();
664 for o in 1..perf_caps.len() {
665 let ovrflw_perf_caps = &perf_caps[o..];
667 let mut ovrflw_cpus_all: Vec<usize> = vec![];
668 for perf_cap in ovrflw_perf_caps.iter() {
669 let cpu_order = perf_cpu_order.get(perf_cap).unwrap();
670 let cpus_perf = cpu_order.cpus_perf.borrow();
671 ovrflw_cpus_all.extend(cpus_perf.iter().cloned());
672 }
673
674 let mut cpu_set = HashSet::<usize>::new();
677 let perf_cap = perf_caps[o - 1];
678 let cpu_order = perf_cpu_order.get(&perf_cap).unwrap();
679 let cpus_perf = cpu_order.cpus_perf.borrow();
680 for &cpu_adx in cpus_perf.iter() {
681 cpu_set.insert(cpu_adx);
682 }
683
684 let mut cpus_ovflw: Vec<usize> = vec![];
685 for &cpu_adx in ovrflw_cpus_all.iter() {
686 if cpu_set.get(&cpu_adx).is_none() {
687 cpus_ovflw.push(cpu_adx);
688 cpu_set.insert(cpu_adx);
689 }
690 }
691
692 let mut v = cpu_order.cpus_ovflw.borrow_mut();
694 v.extend(cpus_ovflw.iter().cloned());
695 }
696
697 debug!("## gen_perf_cpu_order_table");
699 debug!("{:#?}", perf_cpu_order);
700 }
701
702 fn sort_cpus_by_topological_order(&'a self, cpus: &Vec<usize>) -> Vec<usize> {
704 let mut sorted: Vec<usize> = vec![];
705 for &cpu_adx in self.cpus_topological_order.iter() {
706 if let Some(_) = cpus.iter().find(|&&x| x == cpu_adx) {
707 sorted.push(cpu_adx);
708 }
709 }
710 sorted
711 }
712
713 fn gen_perf_pds_table(&'a self) {
757 let utils = vec![0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0];
758
759 for &util in utils.iter() {
761 let mut best_pdsi: Option<PDSetInfo<'a>>;
762 let mut del_pdsi: Option<PDSetInfo<'a>> = None;
763
764 match self.perf_pdsi.borrow().last_key_value() {
765 Some((_, base)) => {
766 best_pdsi = self.find_perf_pds_for(util, Some(base));
767
768 if let Some(ref best) = best_pdsi {
774 if best.pdcpu_set.is_subset(&base.pdcpu_set) {
775 let ext_pdcpu = PDSetInfo {
776 performance: best.performance,
777 power: best.power,
778 pdcpu_set: base.pdcpu_set.clone(),
779 pd_id_set: base.pd_id_set.clone(),
780 };
781 best_pdsi = Some(ext_pdcpu);
782 del_pdsi = Some(base.clone());
783 }
784 }
785 }
786 None => {
787 best_pdsi = self.find_perf_pds_for(util, None);
788 }
789 };
790
791 if let Some(best_pdsi) = best_pdsi {
792 self.perf_pdsi
793 .borrow_mut()
794 .insert(best_pdsi.performance, best_pdsi);
795 }
796
797 if let Some(del_pdsi) = del_pdsi {
798 self.perf_pdsi.borrow_mut().remove(&del_pdsi.performance);
799 }
800 }
801
802 debug!("## gen_perf_pds_table");
804 for (perf, pdsi) in self.perf_pdsi.borrow().iter() {
805 debug!("PERF: [_, {}]", perf);
806 for pdcpu in pdsi.pdcpu_set.iter() {
807 debug!(
808 " pd:id: {:?} -- cpu_vid: {}",
809 pdcpu.pd.id, pdcpu.cpu_vid
810 );
811 }
812 }
813 }
814
815 fn find_perf_pds_for(
816 &'a self,
817 util: f32,
818 base: Option<&PDSetInfo<'a>>,
819 ) -> Option<PDSetInfo<'a>> {
820 let target_perf = (util * self.tot_perf as f32) as usize;
821 let mut lookahead = 0;
822 let mut min_dist: usize = usize::MAX;
823 let mut best_pdsi: Option<PDSetInfo<'a>> = None;
824
825 let pdss_infos = self.pdss_infos.borrow();
826 for (&pdsi_perf, pdsi_set) in pdss_infos.iter() {
827 if pdsi_perf >= target_perf {
828 let pdsi_set_ref = pdsi_set.borrow();
829 for pdsi in pdsi_set_ref.iter() {
830 let dist = pdsi.dist(base);
831 if dist < min_dist {
832 min_dist = dist;
833 best_pdsi = Some(pdsi.clone());
834 }
835 }
836 lookahead += 1;
837 if lookahead >= LOOKAHEAD_CNT {
838 break;
839 }
840 }
841 }
842
843 best_pdsi
844 }
845
846 fn gen_all_pds_combinations(&'a self) {
912 let pdsi_vec = self.gen_pds_combinations(0.0);
914 self.insert_pds_combinations(&pdsi_vec);
915
916 let pdsi_vec = self.gen_pds_combinations(100.0);
917 self.insert_pds_combinations(&pdsi_vec);
918
919 self.gen_perf_cpuset_table_range(0, 100);
921
922 debug!("## gen_all_pds_combinations");
924 for (perf, pdss_info) in self.pdss_infos.borrow().iter() {
925 debug!("PERF: [_, {}]", perf);
926 for pdsi in pdss_info.borrow().iter() {
927 debug!(" perf: {} -- power: {}", pdsi.performance, pdsi.power);
928 for pdcpu in pdsi.pdcpu_set.iter() {
929 debug!(
930 " pd:id: {:?} -- cpu_vid: {}",
931 pdcpu.pd.id, pdcpu.cpu_vid
932 );
933 }
934 }
935 }
936 }
937
938 fn gen_perf_cpuset_table_range(&'a self, low: isize, high: isize) {
939 if low > high {
940 return;
941 }
942
943 let mid: isize = low + (high - low) / 2;
946 let pdsi_vec = self.gen_pds_combinations(mid as f32);
947 let found_new = self.insert_pds_combinations(&pdsi_vec);
948 if found_new {
949 self.gen_perf_cpuset_table_range(mid + 1, high);
950 self.gen_perf_cpuset_table_range(low, mid - 1);
951 }
952 }
953
954 fn gen_pds_combinations(&'a self, util: f32) -> Vec<PDSetInfo<'a>> {
955 let mut pdsi_vec = Vec::new();
956
957 let pds_set = self.gen_pds_set(util);
958 let n = pds_set.len();
959 for k in 1..n {
960 let pdss = pds_set.clone();
961 let pds_cmbs: Vec<_> = Combinations::new(pdss, k)
962 .map(|cmb| PDSetInfo::new(cmb.clone()))
963 .collect();
964 pdsi_vec.extend(pds_cmbs);
965 }
966
967 let pdsi = PDSetInfo::new(pds_set.clone());
968 pdsi_vec.push(pdsi);
969
970 pdsi_vec
971 }
972
973 fn insert_pds_combinations(&self, new_pdsi_vec: &Vec<PDSetInfo<'a>>) -> bool {
974 let mut found_new = false;
979
980 for new_pdsi in new_pdsi_vec.iter() {
981 let mut pdss_infos = self.pdss_infos.borrow_mut();
982 let v = pdss_infos.get(&new_pdsi.performance);
983 match v {
984 Some(v) => {
986 let mut v = v.borrow_mut();
987 let pdsi = &v.iter().next().unwrap();
988 if pdsi.power == new_pdsi.power {
989 if v.insert(new_pdsi.clone()) {
991 found_new = true;
992 }
993 } else if pdsi.power > new_pdsi.power {
994 v.clear();
996 v.insert(new_pdsi.clone());
997 found_new = true;
998 }
999 }
1000 None => {
1002 let mut v: HashSet<PDSetInfo<'a>> = HashSet::new();
1004 v.insert(new_pdsi.clone());
1005 pdss_infos.insert(new_pdsi.performance, v.into());
1006 found_new = true;
1007 }
1008 }
1009 }
1010 found_new
1011 }
1012
1013 fn gen_pds_set(&self, util: f32) -> Vec<PDS<'_>> {
1016 let mut pds_set = vec![];
1017 for (_, pd) in self.em.perf_doms.iter() {
1018 let ps = pd.select_perf_state(util).unwrap();
1019 let pds = PDS::new(pd, ps);
1020 pds_set.push(pds);
1021 }
1022 self.expand_pds_set(&mut pds_set);
1023 pds_set
1024 }
1025
1026 fn expand_pds_set(&self, pds_set: &mut Vec<PDS<'_>>) {
1030 let mut xset = vec![];
1031 for pds in pds_set.iter() {
1034 let nr_cpus = pds.pd.span.weight();
1035 for _ in 1..nr_cpus {
1036 xset.push(pds.clone());
1037 }
1038 }
1039 pds_set.append(&mut xset);
1040
1041 pds_set.sort();
1043 }
1044}
1045
1046impl<'a> PDS<'_> {
1047 fn new(pd: &'a PerfDomain, ps: &'a PerfState) -> PDS<'a> {
1048 PDS { pd, ps }
1049 }
1050}
1051
1052impl PartialEq for PDS<'_> {
1053 fn eq(&self, other: &Self) -> bool {
1054 self.pd == other.pd && self.ps == other.ps
1055 }
1056}
1057
1058impl<'a> PDCpu<'_> {
1059 fn new(pd: &'a PerfDomain, cpu_vid: usize) -> PDCpu<'a> {
1060 PDCpu { pd, cpu_vid }
1061 }
1062}
1063
1064impl PartialEq for PDCpu<'_> {
1065 fn eq(&self, other: &Self) -> bool {
1066 self.pd == other.pd && self.cpu_vid == other.cpu_vid
1067 }
1068}
1069
1070impl fmt::Display for PDS<'_> {
1071 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1072 write!(
1073 f,
1074 "pd:id:{}/pd:weight:{}/ps:cap:{}/ps:power:{}",
1075 self.pd.id,
1076 self.pd.span.weight(),
1077 self.ps.performance,
1078 self.ps.power,
1079 )?;
1080 Ok(())
1081 }
1082}
1083
1084impl<'a> PDSetInfo<'_> {
1085 fn new(pds_set: Vec<PDS<'a>>) -> PDSetInfo<'a> {
1086 let mut performance = 0;
1088 let mut power = 0;
1089 let mut pd_id_set: BTreeSet<usize> = BTreeSet::new();
1090
1091 for pds in pds_set.iter() {
1092 performance += pds.ps.performance;
1093 power += pds.ps.power;
1094 pd_id_set.insert(pds.pd.id);
1095 }
1096
1097 let mut pds_map: BTreeMap<PDS<'a>, RefCell<Vec<PDS<'a>>>> = BTreeMap::new();
1099
1100 for pds in pds_set.iter() {
1101 let v = pds_map.get(&pds);
1102 match v {
1103 Some(v) => {
1104 let mut v = v.borrow_mut();
1105 v.push(pds.clone());
1106 }
1107 None => {
1108 let mut v: Vec<PDS<'a>> = Vec::new();
1109 v.push(pds.clone());
1110 pds_map.insert(pds.clone(), v.into());
1111 }
1112 }
1113 }
1114 let mut pdcpu_set: BTreeSet<PDCpu<'a>> = BTreeSet::new();
1116 let pds_map = pds_map;
1117
1118 for (_, v) in pds_map.iter() {
1119 for (cpu_vid, pds) in v.borrow().iter().enumerate() {
1120 let pdcpu = PDCpu::new(pds.pd, cpu_vid);
1121 pdcpu_set.insert(pdcpu);
1122 }
1123 }
1124
1125 PDSetInfo {
1126 performance,
1127 power,
1128 pdcpu_set,
1129 pd_id_set,
1130 }
1131 }
1132
1133 fn dist(&self, base: Option<&PDSetInfo<'a>>) -> usize {
1141 let nr_pds = self.pd_id_set.len();
1142 let nr_pds_overlap = match base {
1143 Some(base) => self.pd_id_set.intersection(&base.pd_id_set).count(),
1144 None => 0,
1145 };
1146 let nr_cpus = self.pdcpu_set.len();
1147
1148 ((nr_pds - nr_pds_overlap) * PD_UNIT) + ((*NR_CPU_IDS - nr_cpus) * CPU_UNIT) + (*NR_CPU_IDS - self.pd_id_set.first().unwrap()) }
1152}
1153
1154impl PartialEq for PDSetInfo<'_> {
1155 fn eq(&self, other: &Self) -> bool {
1156 self.performance == other.performance
1157 && self.power == other.power
1158 && self.pdcpu_set == other.pdcpu_set
1159 }
1160}
1161
1162impl Hash for PDSetInfo<'_> {
1163 fn hash<H: Hasher>(&self, state: &mut H) {
1164 self.pdcpu_set.hash(state);
1167 }
1168}
1169
1170impl PartialEq for PerfCpuOrder {
1171 fn eq(&self, other: &Self) -> bool {
1172 self.perf_cap == other.perf_cap
1173 }
1174}
1175
1176impl fmt::Display for PerfCpuOrder {
1177 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1178 write!(
1179 f,
1180 "capacity bound: {} ({}%)\n",
1181 self.perf_cap,
1182 self.perf_util * 100.0
1183 )?;
1184 write!(f, " primary CPUs: {:?}\n", self.cpus_perf.borrow())?;
1185 write!(f, " overflow CPUs: {:?}", self.cpus_ovflw.borrow())?;
1186 Ok(())
1187 }
1188}