use crate::domain::{Domain, DomainKind, ProcessorId};
use crate::processor_set::ProcessorSet;
use crate::topology::MachineMemoryTopology;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Granularity<'a> {
Relation(&'a Domain),
Machine,
}
impl<'a> Granularity<'a> {
#[must_use]
pub fn relation(self) -> Option<&'a Domain> {
match self {
Self::Relation(domain) => Some(domain),
Self::Machine => None,
}
}
#[must_use]
pub fn is_machine(self) -> bool {
matches!(self, Self::Machine)
}
#[must_use]
pub fn processors(self, topology: &MachineMemoryTopology) -> ProcessorSet {
match self {
Self::Relation(domain) => domain.processors.clone(),
Self::Machine => topology.machine_processors(),
}
}
}
impl MachineMemoryTopology {
#[must_use]
pub fn machine_processors(&self) -> ProcessorSet {
let mut set = ProcessorSet::empty();
for processor in &self.processors {
set.insert(processor.id.group, processor.id.number);
}
set
}
#[must_use]
pub fn minimal_shared(&self, of: &ProcessorSet) -> Vec<Granularity<'_>> {
if !of.is_subset(&self.machine_processors()) {
return Vec::new();
}
let covering: Vec<&Domain> = self
.domains
.iter()
.filter(|domain| of.is_subset(&domain.processors))
.collect();
if covering.is_empty() {
return vec![Granularity::Machine];
}
covering
.iter()
.filter(|candidate| {
!covering.iter().any(|other| {
other.processors.is_subset(&candidate.processors)
&& other.processors != candidate.processors
})
})
.map(|domain| Granularity::Relation(domain))
.collect()
}
#[must_use]
pub fn proximity(&self, processors: &[ProcessorId]) -> Proximity<'_> {
let mut set = ProcessorSet::empty();
let mut representable = Vec::new();
let mut unrepresentable = Vec::new();
for id in processors {
if ProcessorSet::can_represent(id.number) {
set.insert(id.group, id.number);
representable.push(*id);
} else {
unrepresentable.push(*id);
}
}
Proximity {
shared: self.minimal_shared(&set),
finer_unobserved: representable
.iter()
.any(|id| !self.kinds_covering(*id).is_empty()),
unrepresentable,
}
}
fn kinds_covering(&self, processor: ProcessorId) -> Vec<&'static str> {
let mut missing = Vec::new();
for kind in Self::REPORTED_KINDS {
let mut kind_exists = false;
let mut covers = false;
for domain in &self.domains {
if Self::kind_name(&domain.kind) != kind {
continue;
}
kind_exists = true;
if domain
.processors
.contains(processor.group, processor.number)
{
covers = true;
break;
}
}
if kind_exists && !covers {
missing.push(kind);
}
}
missing
}
const REPORTED_KINDS: [&'static str; 5] = ["cache", "core", "module", "die", "package"];
fn kind_name(kind: &DomainKind) -> &'static str {
match kind {
DomainKind::Group => "group",
DomainKind::Package => "package",
DomainKind::Die => "die",
DomainKind::Module => "module",
DomainKind::Core { .. } => "core",
DomainKind::Cache { .. } => "cache",
DomainKind::Memory { .. } => "memory",
DomainKind::Other { .. } => "other",
}
}
#[must_use]
pub fn is_finer_than(&self, finer: Granularity<'_>, coarser: Granularity<'_>) -> bool {
let finer = finer.processors(self);
let coarser = coarser.processors(self);
finer.is_subset(&coarser) && finer != coarser
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Proximity<'a> {
pub shared: Vec<Granularity<'a>>,
pub finer_unobserved: bool,
pub unrepresentable: Vec<ProcessorId>,
}
impl<'a> Proximity<'a> {
#[must_use]
pub fn only(&self) -> Option<Granularity<'a>> {
match self.shared.as_slice() {
[single] => Some(*single),
_ => None,
}
}
}
#[cfg(test)]
mod tests;