mirror of
https://github.com/JakeHillion/scx.git
synced 2024-11-22 01:41:49 +00:00
scx_layered: Read CPU topology for building CpuPool
Building CpuPool from cache-cpu topology did not apply on arm, because `/sys/devices/system/cpu/cpu{}/cache/index{}/id` file is unavailable. Read CPU topology instead. Signed-off-by: Ming Yang <minos.future@gmail.com>
This commit is contained in:
parent
f37bc0db7f
commit
f3f4726c09
@ -73,6 +73,8 @@ use crate::misc::read_file_usize;
|
||||
use crate::Cpumask;
|
||||
use anyhow::bail;
|
||||
use anyhow::Result;
|
||||
use bitvec::bitvec;
|
||||
use bitvec::vec::BitVec;
|
||||
use glob::glob;
|
||||
use sscanf::sscanf;
|
||||
use std::collections::BTreeMap;
|
||||
@ -373,6 +375,39 @@ impl Topology {
|
||||
pub fn has_little_cores(&self) -> bool {
|
||||
self.cores.iter().any(|c| c.core_type == CoreType::Little)
|
||||
}
|
||||
|
||||
/// Returns a BitVec of online CPUs.
|
||||
pub fn cpus_bitvec(&self) -> BitVec {
|
||||
let mut cpus = bitvec![0; *NR_CPUS_POSSIBLE];
|
||||
for (id, _) in self.cpus.iter() {
|
||||
cpus.set(*id, true);
|
||||
}
|
||||
cpus
|
||||
}
|
||||
|
||||
/// Returns a vector that maps the index of each logical core to the sibling core.
|
||||
/// This represents the "next sibling" core within a package in systems that support SMT.
|
||||
/// The sibling core is the other logical core that shares the physical resources
|
||||
/// of the same physical core.
|
||||
///
|
||||
/// Assuming each core holds exactly at most two cpus.
|
||||
pub fn sibling_cpus(&self) -> Vec<i32> {
|
||||
let mut sibling_cpu = vec![-1i32; *NR_CPUS_POSSIBLE];
|
||||
for core in self.cores() {
|
||||
let mut first = -1i32;
|
||||
for (cpu_id, _) in core.cpus() {
|
||||
let cpu = *cpu_id;
|
||||
if first < 0 {
|
||||
first = cpu as i32;
|
||||
} else {
|
||||
sibling_cpu[first as usize] = cpu as i32;
|
||||
sibling_cpu[cpu as usize] = first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
sibling_cpu
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a topology map from a Topology object, represented as an array of arrays.
|
||||
@ -416,6 +451,39 @@ impl TopologyMap {
|
||||
pub fn iter(&self) -> Iter<Vec<usize>> {
|
||||
self.map.iter()
|
||||
}
|
||||
|
||||
/// Returns a vector of bit masks, each representing the mapping between
|
||||
/// physical cores and the logical cores that run on them.
|
||||
/// The index in the vector represents the physical core, and each bit in the
|
||||
/// corresponding `BitVec` represents whether a logical core belongs to that physical core.
|
||||
pub fn core_cpus_bitvec(&self) -> Vec<BitVec> {
|
||||
let mut core_cpus = Vec::<BitVec>::new();
|
||||
for (core_id, core) in self.iter().enumerate() {
|
||||
if core_cpus.len() < core_id + 1 {
|
||||
core_cpus.resize(core_id + 1, bitvec![0; *NR_CPUS_POSSIBLE]);
|
||||
}
|
||||
for cpu in core {
|
||||
core_cpus[core_id].set(*cpu, true);
|
||||
}
|
||||
}
|
||||
core_cpus
|
||||
}
|
||||
|
||||
/// Returns mapping between logical core and physical core ids
|
||||
/// The index in the vector represents the logical core, and each corresponding value
|
||||
/// represents whether the physical core id of the logical core.
|
||||
pub fn cpu_core_mapping(&self) -> Vec<usize> {
|
||||
let mut cpu_core_mapping = Vec::new();
|
||||
for (core_id, core) in self.iter().enumerate() {
|
||||
for cpu in core {
|
||||
if cpu_core_mapping.len() < cpu + 1 {
|
||||
cpu_core_mapping.resize(cpu + 1, 0);
|
||||
}
|
||||
cpu_core_mapping[*cpu] = core_id;
|
||||
}
|
||||
}
|
||||
cpu_core_mapping
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************
|
||||
|
@ -8,10 +8,8 @@ mod layer_core_growth;
|
||||
pub mod bpf_intf;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use anyhow::bail;
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use bitvec::prelude::*;
|
||||
pub use config::LayerConfig;
|
||||
@ -23,9 +21,9 @@ use log::debug;
|
||||
use log::info;
|
||||
use scx_utils::Core;
|
||||
use scx_utils::Topology;
|
||||
use scx_utils::TopologyMap;
|
||||
|
||||
const MAX_CPUS: usize = bpf_intf::consts_MAX_CPUS as usize;
|
||||
const CORE_CACHE_LEVEL: u32 = 2;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref NR_POSSIBLE_CPUS: usize = libbpf_rs::num_possible_cpus().unwrap();
|
||||
@ -61,8 +59,9 @@ pub struct CpuPool {
|
||||
/// of the same physical core.
|
||||
pub sibling_cpu: Vec<i32>,
|
||||
|
||||
/// A list of physical core IDs.
|
||||
/// Each entry in this vector corresponds to a unique physical core.
|
||||
/// Mapping between logical core and physical core ids
|
||||
/// The index in the vector represents the logical core, and each corresponding value
|
||||
/// represents whether the physical core id of the logical core.
|
||||
cpu_core: Vec<usize>,
|
||||
|
||||
/// A bit mask representing all available physical cores.
|
||||
@ -93,72 +92,14 @@ impl CpuPool {
|
||||
);
|
||||
}
|
||||
|
||||
let mut cpu_to_cache = vec![]; // (cpu_id, Option<cache_id>)
|
||||
let mut cache_ids = BTreeSet::<usize>::new();
|
||||
let mut nr_offline = 0;
|
||||
let topo_map = TopologyMap::new(&topo).unwrap();
|
||||
|
||||
// Build cpu -> cache ID mapping.
|
||||
for cpu in 0..*NR_POSSIBLE_CPUS {
|
||||
let path = format!(
|
||||
"/sys/devices/system/cpu/cpu{}/cache/index{}/id",
|
||||
cpu, CORE_CACHE_LEVEL
|
||||
);
|
||||
let id = match std::fs::read_to_string(&path) {
|
||||
Ok(val) => Some(val.trim().parse::<usize>().with_context(|| {
|
||||
format!("Failed to parse {:?}'s content {:?}", &path, &val)
|
||||
})?),
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||
nr_offline += 1;
|
||||
None
|
||||
}
|
||||
Err(e) => return Err(e).with_context(|| format!("Failed to open {:?}", &path)),
|
||||
};
|
||||
|
||||
cpu_to_cache.push(id);
|
||||
if let Some(id) = id {
|
||||
cache_ids.insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
let nr_cpus = *NR_POSSIBLE_CPUS - nr_offline;
|
||||
|
||||
// Cache IDs may have holes. Assign consecutive core IDs to existing
|
||||
// cache IDs.
|
||||
let mut cache_to_core = BTreeMap::<usize, usize>::new();
|
||||
let mut nr_cores = 0;
|
||||
for cache_id in cache_ids.iter() {
|
||||
cache_to_core.insert(*cache_id, nr_cores);
|
||||
nr_cores += 1;
|
||||
}
|
||||
|
||||
// Build core -> cpumask and cpu -> core mappings.
|
||||
let mut all_cpus = bitvec![0; *NR_POSSIBLE_CPUS];
|
||||
let mut core_cpus = vec![bitvec![0; *NR_POSSIBLE_CPUS]; nr_cores];
|
||||
let mut cpu_core = vec![];
|
||||
|
||||
for (cpu, cache) in cpu_to_cache.iter().enumerate().take(*NR_POSSIBLE_CPUS) {
|
||||
if let Some(cache_id) = cache {
|
||||
let core_id = cache_to_core[cache_id];
|
||||
all_cpus.set(cpu, true);
|
||||
core_cpus[core_id].set(cpu, true);
|
||||
cpu_core.push(core_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Build sibling_cpu[]
|
||||
let mut sibling_cpu = vec![-1i32; *NR_POSSIBLE_CPUS];
|
||||
for cpus in &core_cpus {
|
||||
let mut first = -1i32;
|
||||
for cpu in cpus.iter_ones() {
|
||||
if first < 0 {
|
||||
first = cpu as i32;
|
||||
} else {
|
||||
sibling_cpu[first as usize] = cpu as i32;
|
||||
sibling_cpu[cpu as usize] = first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let all_cpus = topo.cpus_bitvec();
|
||||
let nr_cpus = topo.nr_cpus_online();
|
||||
let core_cpus = topo_map.core_cpus_bitvec();
|
||||
let nr_cores = topo.cores().len();
|
||||
let sibling_cpu = topo.sibling_cpus();
|
||||
let cpu_core = topo_map.cpu_core_mapping();
|
||||
|
||||
// Build core_topology_to_id
|
||||
let mut core_topology_to_id = BTreeMap::new();
|
||||
|
Loading…
Reference in New Issue
Block a user