mod pq_tree;
use crate::{error::BuilderErrorKind, sys::hardware_topology, CpuLocation, GlommioError};
use pq_tree::{
marker::{Pack, Priority, Spread},
Level,
Node,
};
use std::{
collections::{
hash_map::RandomState,
hash_set::{Difference, Intersection, IntoIter, Iter, SymmetricDifference, Union},
HashSet,
},
convert::TryInto,
hash::{Hash, Hasher},
iter::FromIterator,
};
type Result<T> = crate::Result<T, ()>;
#[cfg(doc)]
use super::{LocalExecutor, LocalExecutorBuilder, LocalExecutorPoolBuilder};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PoolPlacement {
Unbound(usize),
Fenced(usize, CpuSet),
MaxSpread(usize, Option<CpuSet>),
MaxPack(usize, Option<CpuSet>),
Custom(Vec<CpuSet>),
}
impl PoolPlacement {
pub fn executor_count(&self) -> usize {
match self {
PoolPlacement::Unbound(cpus) => *cpus,
PoolPlacement::Fenced(cpus, _) => *cpus,
PoolPlacement::MaxSpread(cpus, _) => *cpus,
PoolPlacement::MaxPack(cpus, _) => *cpus,
PoolPlacement::Custom(cpus) => cpus.len(),
}
}
pub fn generate_cpu_set(self) -> Result<CpuSetGenerator> {
CpuSetGenerator::pool(self)
}
pub(super) fn shrink_to(self, count: usize) -> Self {
if count > self.executor_count() {
return self;
}
match self {
PoolPlacement::Unbound(_) => PoolPlacement::Unbound(count),
PoolPlacement::Fenced(_, set) => PoolPlacement::Fenced(count, set),
PoolPlacement::MaxSpread(_, set) => PoolPlacement::MaxSpread(count, set),
PoolPlacement::MaxPack(_, set) => PoolPlacement::MaxPack(count, set),
PoolPlacement::Custom(mut cpus) => {
cpus.truncate(count);
PoolPlacement::Custom(cpus)
}
}
}
}
impl From<Placement> for PoolPlacement {
fn from(placement: Placement) -> Self {
match placement {
Placement::Unbound => PoolPlacement::Unbound(1),
Placement::Fenced(set) => PoolPlacement::Fenced(1, set),
Placement::Fixed(cpu) => {
PoolPlacement::Custom(vec![CpuSet::online().unwrap().filter(|x| x.cpu == cpu)])
}
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Placement {
Unbound,
Fenced(CpuSet),
Fixed(usize),
}
impl Placement {
pub fn generate_cpu_set(self) -> Result<CpuSetGenerator> {
CpuSetGenerator::one(self)
}
}
#[derive(Clone, Debug, Eq)]
pub struct CpuSet(HashSet<CpuLocation>);
impl Hash for CpuSet {
fn hash<H: Hasher>(&self, state: &mut H) {
for loc in &self.0 {
loc.hash(state);
}
}
}
impl PartialEq for CpuSet {
fn eq(&self, other: &Self) -> bool {
self.0.eq(&other.0)
}
}
impl FromIterator<CpuLocation> for CpuSet {
fn from_iter<I: IntoIterator<Item = CpuLocation>>(cpus: I) -> Self {
Self(HashSet::<CpuLocation>::from_iter(cpus.into_iter()))
}
}
impl<'a> IntoIterator for &'a CpuSet {
type Item = &'a CpuLocation;
type IntoIter = Iter<'a, CpuLocation>;
#[inline]
fn into_iter(self) -> Iter<'a, CpuLocation> {
self.0.iter()
}
}
impl IntoIterator for CpuSet {
type Item = CpuLocation;
type IntoIter = IntoIter<CpuLocation>;
#[inline]
fn into_iter(self) -> IntoIter<CpuLocation> {
self.0.into_iter()
}
}
impl CpuSet {
pub fn online() -> Result<Self> {
let topo = hardware_topology::get_machine_topology_unsorted()?;
Ok(Self::from_iter(topo))
}
#[must_use]
pub fn filter<F>(mut self, f: F) -> Self
where
F: FnMut(&CpuLocation) -> bool,
{
self.0 = self.0.into_iter().filter(f).collect();
self
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn iter(&self) -> Iter<'_, CpuLocation> {
self.0.iter()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn contains(&self, value: &CpuLocation) -> bool {
self.0.contains(value)
}
pub fn difference<'a>(&'a self, other: &'a Self) -> Difference<'a, CpuLocation, RandomState> {
self.0.difference(&other.0)
}
pub fn intersection<'a>(
&'a self,
other: &'a Self,
) -> Intersection<'a, CpuLocation, RandomState> {
self.0.intersection(&other.0)
}
pub fn is_disjoint(&self, other: &Self) -> bool {
self.0.is_disjoint(&other.0)
}
pub fn is_subset(&self, other: &Self) -> bool {
self.0.is_subset(&other.0)
}
pub fn is_superset(&self, other: &Self) -> bool {
self.0.is_superset(&other.0)
}
pub fn symmetric_difference<'a>(
&'a self,
other: &'a Self,
) -> SymmetricDifference<'a, CpuLocation, RandomState> {
self.0.symmetric_difference(&other.0)
}
pub fn union<'a>(&'a self, other: &'a Self) -> Union<'a, CpuLocation, RandomState> {
self.0.union(&other.0)
}
}
#[derive(Clone, Debug)]
pub enum CpuIter {
Unbound,
Single(CpuLocation),
Multi(Vec<CpuLocation>),
}
impl CpuIter {
fn from_vec(v: Vec<CpuLocation>) -> Self {
Self::Multi(v)
}
fn from_option(cpu_loc: Option<CpuLocation>) -> Self {
match cpu_loc {
None => Self::Unbound,
Some(cpu) => Self::Single(cpu),
}
}
pub fn cpu_binding(self) -> Option<impl IntoIterator<Item = usize>> {
match self {
Self::Unbound => None,
Self::Single(_) | Self::Multi(_) => Some(self.map(|l| l.cpu)),
}
}
}
impl Iterator for CpuIter {
type Item = CpuLocation;
fn next(&mut self) -> Option<Self::Item> {
match self {
Self::Unbound => None,
Self::Single(_) => match std::mem::replace(self, Self::Unbound) {
Self::Single(cpu) => Some(cpu),
_ => unreachable!("expected CpuIter::Single"),
},
Self::Multi(v) => v.pop(),
}
}
}
#[derive(Debug)]
pub enum CpuSetGenerator {
Unbound,
Fenced(CpuSet),
MaxSpread(MaxSpreader),
MaxPack(MaxPacker),
Custom(Vec<CpuSet>),
}
impl CpuSetGenerator {
pub(crate) fn pool(placement: PoolPlacement) -> Result<Self> {
let this = match placement {
PoolPlacement::Unbound(nr_shards) => {
Self::check_nr_executors(1, nr_shards)?;
Self::Unbound
}
PoolPlacement::Fenced(nr_shards, cpus) => {
Self::check_nr_executors(1, nr_shards)?;
Self::check_nr_cpus(1, &cpus)?;
for cpu in cpus.iter() {
Self::check_cpu(cpu.cpu)?;
}
Self::Fenced(cpus)
}
PoolPlacement::MaxSpread(nr_shards, cpus) => {
Self::check_nr_executors(1, nr_shards)?;
let cpus = match cpus {
Some(cpus) => cpus,
None => CpuSet::online()?,
};
Self::check_nr_cpus(nr_shards, &cpus)?;
for cpu in cpus.iter() {
Self::check_cpu(cpu.cpu)?;
}
Self::MaxSpread(MaxSpreader::from_cpu_set(cpus))
}
PoolPlacement::MaxPack(nr_shards, cpus) => {
Self::check_nr_executors(1, nr_shards)?;
let cpus = match cpus {
Some(cpus) => cpus,
None => CpuSet::online()?,
};
Self::check_nr_cpus(nr_shards, &cpus)?;
for cpu in cpus.iter() {
Self::check_cpu(cpu.cpu)?;
}
Self::MaxPack(MaxPacker::from_cpu_set(cpus))
}
PoolPlacement::Custom(cpu_sets) => {
for cpu_set in &cpu_sets {
Self::check_nr_cpus(1, cpu_set)?;
for cpu in cpu_set.iter() {
Self::check_cpu(cpu.cpu)?;
}
}
Self::Custom(cpu_sets)
}
};
Ok(this)
}
pub(crate) fn one(placement: Placement) -> Result<Self> {
let this = match placement {
Placement::Unbound => Self::Unbound,
Placement::Fenced(cpus) => {
Self::check_nr_cpus(1, &cpus)?;
Self::Fenced(cpus)
}
Placement::Fixed(cpu) => {
Self::check_cpu(cpu)?;
Self::Custom(vec![CpuSet::online()?.filter(|x| x.cpu == cpu)])
}
};
Ok(this)
}
fn check_nr_executors(minimum: usize, shards: usize) -> Result<()> {
if minimum <= shards {
Ok(())
} else {
Err(GlommioError::BuilderError(BuilderErrorKind::NrShards {
minimum,
shards,
}))
}
}
fn check_nr_cpus(required: usize, cpu_set: &CpuSet) -> Result<()> {
let available = cpu_set.len();
if required <= available {
Ok(())
} else {
Err(GlommioError::BuilderError(
BuilderErrorKind::InsufficientCpus {
required,
available,
},
))
}
}
#[cfg(not(test))]
fn check_cpu(id: usize) -> Result<()> {
if CpuSet::online()?.filter(|cpu| cpu.cpu == id).is_empty() {
Err(GlommioError::BuilderError(
BuilderErrorKind::NonExistentCpus { cpu: id },
))
} else {
Ok(())
}
}
#[cfg(test)]
fn check_cpu(_: usize) -> Result<()> {
Ok(())
}
pub fn next(&mut self) -> CpuIter {
match self {
Self::Unbound => CpuIter::Unbound,
Self::Fenced(cpus) => CpuIter::from_vec(cpus.clone().into_iter().collect()),
Self::MaxSpread(it) => CpuIter::from_option(it.next()),
Self::MaxPack(it) => CpuIter::from_option(it.next()),
Self::Custom(cpu_sets) => CpuIter::Multi(
cpu_sets
.pop()
.expect("insufficient cpu sets")
.into_iter()
.collect(),
),
}
}
}
type MaxSpreader = TopologyIter<Spread>;
type MaxPacker = TopologyIter<Pack>;
#[derive(Debug)]
pub struct TopologyIter<T> {
tree: Node<T>,
}
impl<T: Priority> Iterator for TopologyIter<T> {
type Item = CpuLocation;
fn next(&mut self) -> Option<Self::Item> {
self.tree.select_cpu().try_into().ok()
}
}
impl<T> TopologyIter<T>
where
T: Priority,
{
fn from_cpu_set(cpus: CpuSet) -> Self {
Self::from_topology(cpus.into_iter().collect())
}
pub fn from_topology(mut topology: Vec<CpuLocation>) -> Self {
let nr_numa_node = Self::count_unique_by(&topology, |c| c.numa_node);
let nr_package = Self::count_unique_by(&topology, |c| c.package);
let f_level: fn(&CpuLocation, usize) -> Level = if nr_package < nr_numa_node {
topology.sort_by_key(|l| (l.package, l.numa_node, l.core, l.cpu));
|cpu_loc, depth| -> Level {
match depth {
0 => Level::SystemRoot,
1 => Level::Package(cpu_loc.package),
2 => Level::NumaNode(cpu_loc.numa_node),
3 => Level::Core(cpu_loc.core),
4 => Level::Cpu(cpu_loc.cpu),
_ => unreachable!("unexpected tree level: {}", depth),
}
}
} else {
topology.sort_by_key(|l| (l.numa_node, l.package, l.core, l.cpu));
|cpu_loc, depth| -> Level {
match depth {
0 => Level::SystemRoot,
1 => Level::NumaNode(cpu_loc.numa_node),
2 => Level::Package(cpu_loc.package),
3 => Level::Core(cpu_loc.core),
4 => Level::Cpu(cpu_loc.cpu),
_ => unreachable!("unexpected tree level: {}", depth),
}
}
};
let mut node_root = Node::<T>::new(Level::SystemRoot);
let mut iter = topology.into_iter();
if let mut next @ Some(_) = iter.next() {
Self::build_sub_tree(&mut node_root, &mut None, &mut next, &mut iter, &f_level, 1);
}
Self { tree: node_root }
}
fn build_sub_tree<I, F>(
parent: &mut Node<T>,
prev: &mut Option<CpuLocation>,
next: &mut Option<CpuLocation>,
iter: &mut I,
f_level: &F,
depth: usize,
) where
I: Iterator<Item = CpuLocation>,
F: Fn(&CpuLocation, usize) -> Level,
{
loop {
match (&prev, &next) {
(None, Some(loc_n)) => {
let mut node = Node::<T>::new(f_level(loc_n, depth));
match node.level() {
Level::Cpu(_) => {
std::mem::swap(prev, next);
*next = iter.next();
}
_ => Self::build_sub_tree(&mut node, prev, next, iter, f_level, depth + 1),
}
parent.push_child(node);
}
(Some(loc_p), Some(loc_n))
if f_level(loc_p, depth - 1) == f_level(loc_n, depth - 1) =>
{
*prev = None
}
_ => return,
}
}
}
fn count_unique_by<L, ID>(it: impl IntoIterator<Item = L>, mut f: impl FnMut(L) -> ID) -> usize
where
ID: Eq + std::hash::Hash,
{
it.into_iter()
.fold(HashSet::new(), |mut set, item| {
set.insert(f(item));
set
})
.len()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::sys::hardware_topology::test_helpers::cpu_loc;
#[test]
fn cpu_set() {
let set = CpuSet::online().unwrap();
let len = set.len();
assert!(len > 0);
let set = set.filter(|_| true);
assert_eq!(len, set.len());
let set = set.filter(|_| false);
assert_eq!(0, set.len());
assert!(set.is_empty());
assert_eq!(0, set.into_iter().count());
}
#[test]
fn placement_unbound_clone() {
assert_eq!(PoolPlacement::Unbound(5).clone(), PoolPlacement::Unbound(5));
}
#[test]
fn placement_fenced_clone() {
let set = CpuSet::online().unwrap();
let placement = PoolPlacement::Fenced(1, set);
let placement_clone = placement.clone();
assert_eq!(placement_clone, placement);
}
#[test]
fn placement_max_spread_clone() {
let set = CpuSet::online().unwrap();
let some_placement = PoolPlacement::MaxSpread(1, Some(set));
let some_placement_clone = some_placement.clone();
assert_eq!(some_placement_clone, some_placement);
let none_placement = PoolPlacement::MaxSpread(1, None);
let none_placement_clone = none_placement.clone();
assert_eq!(none_placement_clone, none_placement);
}
#[test]
fn placement_max_pack_clone() {
let set = CpuSet::online().unwrap();
let some_placement = PoolPlacement::MaxPack(1, Some(set));
let some_placement_clone = some_placement.clone();
assert_eq!(some_placement_clone, some_placement);
let none_placement = PoolPlacement::MaxPack(1, None);
let none_placement_clone = none_placement.clone();
assert_eq!(none_placement_clone, none_placement);
}
#[test]
fn placement_custom_clone() {
let set1 = CpuSet::online().unwrap();
let set2 = CpuSet::online().unwrap();
assert!(!set1.is_empty());
assert!(!set2.is_empty());
let vec_set = vec![set1, set2];
let placement = PoolPlacement::Custom(vec_set);
assert_eq!(placement.clone(), placement);
}
#[test]
fn max_spreader_this_machine() {
let n = 4096;
let mut max_spreader = MaxSpreader::from_cpu_set(CpuSet::online().unwrap());
assert_eq!(0, max_spreader.tree.nr_slots_selected());
for _ in 0..n {
let _cpu_location: CpuLocation = max_spreader.next().unwrap();
}
assert_eq!(n, max_spreader.tree.nr_slots_selected());
}
#[test]
fn max_packer_this_machine() {
let n = 4096;
let mut max_packer = MaxSpreader::from_cpu_set(CpuSet::online().unwrap());
for _ in 0..n {
let _cpu_location: CpuLocation = max_packer.next().unwrap();
}
}
#[test]
fn max_spreader_with_topology_empty() {
let topology = Vec::new();
assert!(MaxSpreader::from_topology(topology).next().is_none());
}
#[test]
fn max_packer_with_topology_empty() {
let topology = Vec::new();
assert!(MaxPacker::from_topology(topology).next().is_none());
}
#[test]
fn max_spreader_with_topology_0() {
let mut counts = [0; 4];
let topology = vec![
cpu_loc(0, 0, 0, 2),
cpu_loc(0, 0, 0, 0),
cpu_loc(0, 0, 0, 3),
cpu_loc(0, 0, 0, 1),
];
let mut max_spreader = MaxSpreader::from_topology(topology);
assert_eq!(4, max_spreader.tree.nr_slots());
assert_eq!(0, max_spreader.tree.nr_slots_selected());
for _ in 0..4 {
let cpu_location = max_spreader.next().unwrap();
counts[cpu_location.cpu] += 1;
}
assert_eq!(counts, [1, 1, 1, 1]);
assert_eq!(4, max_spreader.tree.nr_slots_selected());
for _ in 0..4 {
let cpu_location = max_spreader.next().unwrap();
counts[cpu_location.cpu] += 1;
}
assert_eq!(counts, [2, 2, 2, 2]);
assert_eq!(8, max_spreader.tree.nr_slots_selected());
for _ in 0..4 * 8 {
let cpu_location = max_spreader.next().unwrap();
counts[cpu_location.cpu] += 1;
}
assert_eq!(counts, [10, 10, 10, 10]);
assert_eq!(40, max_spreader.tree.nr_slots_selected());
assert_eq!(4, max_spreader.tree.nr_slots());
}
#[test]
fn max_spreader_with_topology_1() {
let mut counts = [0; 4];
let topology = vec![
cpu_loc(0, 0, 0, 2),
cpu_loc(0, 0, 1, 3),
cpu_loc(0, 0, 0, 0),
cpu_loc(0, 0, 0, 1),
];
let mut max_spreader = MaxSpreader::from_topology(topology);
assert_eq!(4, max_spreader.tree.nr_slots());
for _ in 0..2 {
let cpu_location = max_spreader.next().unwrap();
counts[cpu_location.cpu] += 1;
}
assert_eq!(1, counts[..3].iter().sum::<i32>());
assert_eq!(1, counts[3]);
for _ in 2..4 {
let cpu_location = max_spreader.next().unwrap();
counts[cpu_location.cpu] += 1;
}
assert_eq!(counts, [1, 1, 1, 1]);
assert_eq!(4, max_spreader.tree.nr_slots_selected());
for _ in 4..6 {
let cpu_location = max_spreader.next().unwrap();
counts[cpu_location.cpu] += 1;
}
assert_eq!(4, counts[..3].iter().sum::<i32>());
assert_eq!(2, counts[3]);
for _ in 6..8 {
let cpu_location = max_spreader.next().unwrap();
counts[cpu_location.cpu] += 1;
}
assert_eq!(counts, [2, 2, 2, 2]);
assert_eq!(8, max_spreader.tree.nr_slots_selected());
assert_eq!(4, max_spreader.tree.nr_slots());
}
#[test]
fn max_spreader_with_topology_2() {
let mut numa = 0;
let mut pkg = 0;
let mut core = 0;
let mut cpu = 0;
let levels = vec![2, 3, 5, 7];
let nr_cpu = levels.iter().product();
let mut counts = vec![0; nr_cpu];
let mut topology = Vec::new();
#[allow(clippy::explicit_counter_loop)]
for _ in 0..levels[0] {
for _ in 0..levels[1] {
for _ in 0..levels[2] {
for _ in 0..levels[3] {
topology.push(cpu_loc(numa, pkg, core, cpu));
cpu += 1;
}
core += 1;
}
pkg += 1;
}
numa += 1;
}
let mut max_spreader = MaxSpreader::from_topology(topology);
let mut selected_prev = 0;
let mut selected = 1;
for nn in levels.into_iter() {
selected *= nn;
for _ in 0..selected - selected_prev {
let cpu_location = max_spreader.next().unwrap();
counts[cpu_location.cpu] += 1;
}
counts
.chunks(nr_cpu / selected)
.for_each(|c| assert_eq!(1, c.iter().sum::<i32>()));
assert_eq!(selected, max_spreader.tree.nr_slots_selected());
selected_prev = selected;
}
}
#[test]
fn max_packer_with_topology_0() {
let mut counts = [0; 7];
let topology = vec![
cpu_loc(0, 0, 1, 6),
cpu_loc(0, 0, 0, 2),
cpu_loc(0, 0, 0, 0),
cpu_loc(0, 1, 2, 5),
cpu_loc(1, 2, 3, 4),
cpu_loc(0, 0, 0, 3),
cpu_loc(0, 0, 0, 1),
];
super::hardware_topology::test_helpers::check_topolgy(topology.clone());
let mut max_packer = MaxPacker::from_topology(topology);
let cpu_location = max_packer.next().unwrap();
counts[cpu_location.cpu] += 1;
assert_eq!(1, counts[..4].iter().sum::<i32>());
assert_eq!(0, counts[5..].iter().sum::<i32>());
for _ in 1..4 {
let cpu_location = max_packer.next().unwrap();
counts[cpu_location.cpu] += 1;
}
assert_eq!(4, counts[..4].iter().sum::<i32>());
assert_eq!(0, counts[5..].iter().sum::<i32>());
let cpu_location = max_packer.next().unwrap();
counts[cpu_location.cpu] += 1;
assert_eq!(1, counts[6]);
assert_eq!(5, counts.iter().sum::<i32>());
let cpu_location = max_packer.next().unwrap();
counts[cpu_location.cpu] += 1;
assert_eq!(1, counts[5]);
assert_eq!(6, counts.iter().sum::<i32>());
let cpu_location = max_packer.next().unwrap();
counts[cpu_location.cpu] += 1;
assert_eq!(1, counts[4]);
assert_eq!(7, counts.iter().sum::<i32>());
let cpu_location = max_packer.next().unwrap();
counts[cpu_location.cpu] += 1;
assert_eq!(5, counts[..4].iter().sum::<i32>());
assert_eq!(8, counts.iter().sum::<i32>());
for _ in 8..10 * counts.len() {
let cpu_location = max_packer.next().unwrap();
counts[cpu_location.cpu] += 1;
}
assert_eq!(counts, [10, 10, 10, 10, 10, 10, 10]);
}
#[test]
fn max_packer_numa_in_pkg() {
let mut counts = [0; 7];
let topology = vec![
cpu_loc(0, 0, 1, 4),
cpu_loc(0, 0, 0, 5),
cpu_loc(0, 0, 0, 6),
cpu_loc(1, 0, 2, 3),
cpu_loc(2, 1, 4, 1),
cpu_loc(2, 1, 4, 2),
cpu_loc(3, 1, 3, 0),
];
super::hardware_topology::test_helpers::check_topolgy(topology.clone());
let mut max_packer = MaxPacker::from_topology(topology);
let cpu_location = max_packer.next().unwrap();
counts[cpu_location.cpu] += 1;
assert_eq!(1, counts[4..].iter().sum::<i32>());
assert_eq!(0, counts[..4].iter().sum::<i32>());
for _ in 1..3 {
let cpu_location = max_packer.next().unwrap();
counts[cpu_location.cpu] += 1;
}
assert_eq!(3, counts[4..].iter().sum::<i32>());
assert_eq!(0, counts[..4].iter().sum::<i32>());
let cpu_location = max_packer.next().unwrap();
counts[cpu_location.cpu] += 1;
assert_eq!(4, counts[3..].iter().sum::<i32>());
assert_eq!(0, counts[..3].iter().sum::<i32>());
for _ in 4..6 {
let cpu_location = max_packer.next().unwrap();
counts[cpu_location.cpu] += 1;
}
assert_eq!(6, counts[1..].iter().sum::<i32>());
assert_eq!(0, counts[..1].iter().sum::<i32>());
let cpu_location = max_packer.next().unwrap();
counts[cpu_location.cpu] += 1;
assert_eq!(1, *counts.iter().max().unwrap());
for _ in 7..10 * counts.len() {
let cpu_location = max_packer.next().unwrap();
counts[cpu_location.cpu] += 1;
}
assert_eq!(counts, [10, 10, 10, 10, 10, 10, 10]);
}
#[test]
fn custom_placement() {
let set1 = CpuSet::from_iter(vec![
cpu_loc(0, 0, 0, 2),
cpu_loc(0, 0, 0, 0),
cpu_loc(0, 0, 0, 3),
cpu_loc(0, 0, 0, 1),
]);
let set2 = CpuSet::from_iter(vec![
cpu_loc(1, 1, 1, 5),
cpu_loc(0, 0, 0, 0),
cpu_loc(1, 1, 1, 4),
cpu_loc(0, 0, 0, 1),
]);
let p = PoolPlacement::Custom(vec![set1, set2]);
let mut gen = CpuSetGenerator::pool(p).unwrap();
let mut bindings = vec![];
for _ in 0..2 {
let v = gen
.next()
.cpu_binding()
.unwrap()
.into_iter()
.collect::<HashSet<_>>();
bindings.push(v);
}
assert_eq!(
bindings,
vec![
HashSet::from_iter(vec![1, 4, 0, 5]),
HashSet::from_iter(vec![1, 3, 0, 2])
]
);
}
#[test]
fn cpuset_disjoint() {
let xs = CpuSet::from_iter(vec![]);
let ys = CpuSet::from_iter(vec![]);
assert!(xs.is_disjoint(&ys));
assert!(ys.is_disjoint(&xs));
let xs = CpuSet::from_iter(vec![cpu_loc(0, 0, 0, 2)]);
let ys = CpuSet::from_iter(vec![cpu_loc(1, 1, 1, 5)]);
assert!(xs.is_disjoint(&ys));
assert!(ys.is_disjoint(&xs));
let xs = CpuSet::from_iter(vec![
cpu_loc(0, 0, 0, 2),
cpu_loc(0, 0, 0, 3),
cpu_loc(0, 0, 0, 1),
]);
let ys = CpuSet::from_iter(vec![
cpu_loc(1, 1, 1, 5),
cpu_loc(1, 1, 1, 4),
cpu_loc(1, 1, 1, 3),
]);
assert!(xs.is_disjoint(&ys));
assert!(ys.is_disjoint(&xs));
}
#[test]
fn cpuset_subset_and_superset() {
let xs = CpuSet::from_iter(vec![]);
let ys = CpuSet::from_iter(vec![]);
assert!(xs.is_subset(&ys));
assert!(xs.is_superset(&ys));
assert!(ys.is_subset(&xs));
assert!(ys.is_superset(&xs));
let xs = CpuSet::from_iter(vec![cpu_loc(0, 0, 0, 2)]);
let ys = CpuSet::from_iter(vec![cpu_loc(1, 1, 1, 5)]);
assert!(!xs.is_subset(&ys));
assert!(!xs.is_superset(&ys));
assert!(!ys.is_subset(&xs));
assert!(!ys.is_superset(&xs));
let xs = CpuSet::from_iter(vec![
cpu_loc(0, 0, 0, 2),
cpu_loc(0, 0, 0, 0),
cpu_loc(0, 0, 0, 3),
cpu_loc(0, 0, 0, 1),
]);
let ys = CpuSet::from_iter(vec![
cpu_loc(1, 1, 1, 5),
cpu_loc(0, 0, 0, 0),
cpu_loc(1, 1, 1, 4),
cpu_loc(0, 0, 0, 1),
]);
assert!(!xs.is_subset(&ys));
assert!(!xs.is_superset(&ys));
assert!(!ys.is_subset(&xs));
assert!(!ys.is_superset(&xs));
let xs = CpuSet::from_iter(vec![cpu_loc(0, 0, 0, 2)]);
let ys = CpuSet::from_iter(vec![cpu_loc(0, 0, 0, 2), cpu_loc(1, 1, 1, 5)]);
assert!(xs.is_subset(&ys));
assert!(!xs.is_superset(&ys));
assert!(!ys.is_subset(&xs));
assert!(ys.is_superset(&xs));
let xs = CpuSet::from_iter(vec![cpu_loc(1, 1, 1, 5), cpu_loc(0, 0, 0, 2)]);
let ys = CpuSet::from_iter(vec![cpu_loc(1, 1, 1, 5)]);
assert!(!xs.is_subset(&ys));
assert!(xs.is_superset(&ys));
assert!(ys.is_subset(&xs));
assert!(!ys.is_superset(&xs));
}
#[test]
fn cpuset_iterate() {
let a = CpuSet::from_iter(vec![
cpu_loc(0, 0, 0, 2),
cpu_loc(0, 0, 0, 0),
cpu_loc(0, 0, 0, 3),
cpu_loc(0, 0, 0, 1),
]);
let mut observed: u32 = 0;
for _ in &a {
observed += 1
}
assert_eq!(observed, 4);
}
#[test]
fn cpuset_intersection() {
let a = CpuSet::from_iter(vec![]);
let b = CpuSet::from_iter(vec![]);
assert!(a.intersection(&b).next().is_none());
let a = CpuSet::from_iter(vec![
cpu_loc(0, 0, 0, 2),
cpu_loc(0, 0, 0, 0),
cpu_loc(0, 0, 0, 3),
cpu_loc(0, 0, 0, 1),
]);
let b = CpuSet::from_iter(vec![
cpu_loc(1, 1, 1, 5),
cpu_loc(0, 0, 0, 0),
cpu_loc(1, 1, 1, 4),
cpu_loc(0, 0, 0, 1),
]);
let mut i = 0;
let expected = [cpu_loc(0, 0, 0, 0), cpu_loc(0, 0, 0, 1)];
for x in a.intersection(&b) {
assert!(expected.contains(x));
i += 1
}
assert_eq!(i, expected.len());
let a = CpuSet::from_iter(vec![
cpu_loc(0, 0, 0, 2),
cpu_loc(0, 0, 0, 0),
cpu_loc(0, 0, 0, 3),
cpu_loc(0, 0, 0, 1),
cpu_loc(0, 0, 0, 4),
]);
let b = CpuSet::from_iter(vec![
cpu_loc(1, 1, 1, 5),
cpu_loc(0, 0, 0, 0),
cpu_loc(1, 1, 1, 4),
cpu_loc(0, 0, 0, 1),
]);
i = 0;
for x in a.intersection(&b) {
assert!(expected.contains(x));
i += 1
}
assert_eq!(i, expected.len());
i = 0;
for x in b.intersection(&a) {
assert!(expected.contains(x));
i += 1
}
assert_eq!(i, expected.len());
}
#[test]
fn cpuset_difference() {
let a = CpuSet::from_iter(vec![
cpu_loc(0, 0, 0, 2),
cpu_loc(0, 0, 0, 0),
cpu_loc(0, 0, 0, 3),
cpu_loc(0, 0, 0, 1),
cpu_loc(0, 0, 0, 4),
]);
let b = CpuSet::from_iter(vec![
cpu_loc(1, 1, 1, 5),
cpu_loc(0, 0, 0, 0),
cpu_loc(1, 1, 1, 4),
cpu_loc(0, 0, 0, 1),
]);
let mut i = 0;
let expected = [
cpu_loc(0, 0, 0, 2),
cpu_loc(0, 0, 0, 3),
cpu_loc(0, 0, 0, 4),
];
for x in a.difference(&b) {
assert!(expected.contains(x));
i += 1
}
assert_eq!(i, expected.len());
}
#[test]
fn cpuset_symmetric_difference() {
let a = CpuSet::from_iter(vec![
cpu_loc(0, 0, 0, 2),
cpu_loc(0, 0, 0, 0),
cpu_loc(0, 0, 0, 3),
cpu_loc(0, 0, 0, 1),
cpu_loc(0, 0, 0, 4),
]);
let b = CpuSet::from_iter(vec![
cpu_loc(1, 1, 1, 5),
cpu_loc(0, 0, 0, 0),
cpu_loc(1, 1, 1, 4),
cpu_loc(0, 0, 0, 1),
cpu_loc(1, 1, 1, 3),
]);
let mut i = 0;
let expected = [
cpu_loc(0, 0, 0, 2),
cpu_loc(0, 0, 0, 3),
cpu_loc(0, 0, 0, 4),
cpu_loc(1, 1, 1, 5),
cpu_loc(1, 1, 1, 4),
cpu_loc(1, 1, 1, 3),
];
for x in a.symmetric_difference(&b) {
assert!(expected.contains(x));
i += 1
}
assert_eq!(i, expected.len());
}
#[test]
fn cpuset_union() {
let a = CpuSet::from_iter(vec![]);
let b = CpuSet::from_iter(vec![]);
assert!(a.union(&b).next().is_none());
assert!(b.union(&a).next().is_none());
let a = CpuSet::from_iter(vec![
cpu_loc(0, 0, 0, 2),
cpu_loc(0, 0, 0, 0),
cpu_loc(0, 0, 0, 3),
cpu_loc(0, 0, 0, 1),
cpu_loc(0, 0, 0, 4),
]);
let b = CpuSet::from_iter(vec![
cpu_loc(1, 1, 1, 5),
cpu_loc(0, 0, 0, 0),
cpu_loc(1, 1, 1, 4),
cpu_loc(0, 0, 0, 1),
cpu_loc(1, 1, 1, 3),
]);
let mut i = 0;
let expected = [
cpu_loc(0, 0, 0, 2),
cpu_loc(0, 0, 0, 0),
cpu_loc(0, 0, 0, 3),
cpu_loc(0, 0, 0, 1),
cpu_loc(0, 0, 0, 4),
cpu_loc(1, 1, 1, 5),
cpu_loc(1, 1, 1, 4),
cpu_loc(1, 1, 1, 3),
];
for x in a.union(&b) {
assert!(expected.contains(x));
i += 1
}
assert_eq!(i, expected.len());
let a = CpuSet::from_iter(vec![
cpu_loc(0, 0, 0, 2),
cpu_loc(0, 0, 0, 0),
cpu_loc(0, 0, 0, 3),
cpu_loc(0, 0, 0, 1),
cpu_loc(0, 0, 0, 4),
cpu_loc(1, 1, 1, 3),
]);
let b = CpuSet::from_iter(vec![
cpu_loc(1, 1, 1, 5),
cpu_loc(0, 0, 0, 0),
cpu_loc(1, 1, 1, 4),
cpu_loc(0, 0, 0, 1),
cpu_loc(1, 1, 1, 3),
]);
i = 0;
for x in a.union(&b) {
assert!(expected.contains(x));
i += 1
}
assert_eq!(i, expected.len());
i = 0;
for x in b.union(&a) {
assert!(expected.contains(x));
i += 1
}
assert_eq!(i, expected.len());
}
}