Struct hwlocality::topology::Topology
source · pub struct Topology(/* private fields */);
Expand description
Main entry point to the hwloc API
A Topology
contains everything hwloc knows about the hardware and software
structure of a system. It can be used to query the system topology and to
bind threads and processes to hardware CPU cores and NUMA nodes.
Since there are many things you can do with a Topology
, the API is
broken down into sections roughly following the structure of the upstream
hwloc documentation:
- Topology building
- Full object lists (specific to Rust bindings)
- Object levels, depths and types
- CPU cache statistics (specific to Rust bindings)
- CPU binding
- Memory binding
- Modifying a loaded topology
- Finding objects inside a CPU set
- Finding objects covering at least a CPU set
- Finding other objects
- Distributing work items over a topology
- CPU and node sets of entire topologies
- Finding I/O objects
- Exporting Topologies to XML
- Exporting Topologies to Synthetic
- Retrieve distances between objects
- Comparing memory node attributes for finding where to allocate on (hwloc 2.3+)
- Kinds of CPU cores (hwloc 2.4+)
- Linux-specific helpers
- Windows-specific helpers (hwloc 2.5+)
Implementations§
source§impl Topology
impl Topology
§CPU binding
Some operating systems do not provide all hwloc-supported mechanisms to bind
processes, threads, etc. Topology::feature_support()
may be used to
query about the actual CPU binding support in the currently used operating
system. The documentation of individual CPU binding methods will clarify
which support flags they require.
By default, when the requested binding operation is not available, hwloc
will go for a similar binding operation (with side-effects, smaller
binding set, etc). You can inhibit this with flag STRICT
, at the
expense of reducing portability across operating systems.
sourcepub fn bind_cpu(
&self,
set: impl Deref<Target = CpuSet>,
flags: CpuBindingFlags
) -> Result<(), CpuBindingError>
pub fn bind_cpu( &self, set: impl Deref<Target = CpuSet>, flags: CpuBindingFlags ) -> Result<(), CpuBindingError>
Binds the current process or thread on given CPUs
Some operating systems only support binding threads or processes to a
single PU
. Others allow binding to larger sets such as entire
Core
s or Package
s or even random sets of individual PU
s. In
such operating systems, the scheduler is free to run the task on one of
these PU, then migrate it to another PU
, etc. It is often useful to
call singlify()
on the target CPU set before passing it to the
binding method to avoid these expensive migrations.
set
can be a &'_ CpuSet
or a BitmapRef<'_, CpuSet>
.
To unbind, just call the binding method with either a full cpuset or a cpuset equal to the system cpuset.
You must specify exactly one of the ASSUME_SINGLE_THREAD
,
THREAD
and PROCESS
binding target flags (listed in order of
decreasing portability) when using this method.
On some operating systems, CPU binding may have effects on memory
binding, you can forbid this with flag NO_MEMORY_BINDING
.
Running lstopo --top
or hwloc-ps
can be a very convenient tool to
check how binding actually happened.
Requires CpuBindingSupport::set_current_process()
or
CpuBindingSupport::set_current_thread()
depending on flags.
See also the top-level CPU binding CPU documentation.
§Errors
BadCpuSet
if it is not possible to bind the current process/thread to the requested CPU set, specificallyBadFlags
if the number of specified binding target flags is not exactly oneBadObject(ThisProgram)
if it is not possible to bind the current process/thread to CPUs, generally speaking
sourcepub fn cpu_binding(
&self,
flags: CpuBindingFlags
) -> Result<CpuSet, HybridError<CpuBindingError>>
pub fn cpu_binding( &self, flags: CpuBindingFlags ) -> Result<CpuSet, HybridError<CpuBindingError>>
Get the current process or thread CPU binding
You must specify exactly one of the ASSUME_SINGLE_THREAD
,
THREAD
and PROCESS
binding target flags (listed in order of
decreasing portability) when using this method.
Flag NO_MEMORY_BINDING
should not be used with this method.
Requires CpuBindingSupport::get_current_process()
or
CpuBindingSupport::get_current_thread()
depending on flags.
See also the top-level CPU binding CPU documentation.
§Errors
BadFlags
if flagNO_MEMORY_BINDING
was specified or if the number of binding target flags is not exactly oneBadObject(ThisProgram)
if it is not possible to query the CPU binding of the current process/thread
sourcepub fn bind_process_cpu(
&self,
pid: ProcessId,
set: impl Deref<Target = CpuSet>,
flags: CpuBindingFlags
) -> Result<(), HybridError<CpuBindingError>>
pub fn bind_process_cpu( &self, pid: ProcessId, set: impl Deref<Target = CpuSet>, flags: CpuBindingFlags ) -> Result<(), HybridError<CpuBindingError>>
Binds a process (identified by its pid
) on given CPUs
As a special case on Linux, if a tid (thread ID) is supplied instead of
a pid (process ID) and flag THREAD
is specified, the specified
thread is bound. Otherwise, flag THREAD
should not be used with this
method.
See also Topology::bind_cpu()
for more informations, except binding
target flags other than THREAD
should not be used with this method,
and it requires CpuBindingSupport::set_process()
or
CpuBindingSupport::set_thread()
depending on flags.
§Errors
BadCpuSet
if it is not possible to bind the target process/thread to the requested CPU set, specificallyBadFlags
if flagTHREAD
was specified on an operating system other than Linux, or if any other binding target flag was specifiedBadObject(ProcessOrThread)
if it is not possible to bind the target process/thread to CPUs, generally speaking
§Panics
Some operating systems use signed PIDs, and do not support PIDs greater
than i32::MAX
. This method will panic when passed such an obviously
invalid PID on these operating systems.
sourcepub fn process_cpu_binding(
&self,
pid: ProcessId,
flags: CpuBindingFlags
) -> Result<CpuSet, HybridError<CpuBindingError>>
pub fn process_cpu_binding( &self, pid: ProcessId, flags: CpuBindingFlags ) -> Result<CpuSet, HybridError<CpuBindingError>>
Get the current physical binding of a process, identified by its pid
As a special case on Linux, if a tid (thread ID) is supplied instead of
a pid (process ID) and flag THREAD
is specified, the binding of the
specified thread is returned. Otherwise, flag THREAD
should not be
used with this method.
See Topology::cpu_binding()
for more informations, except binding
target flags other than THREAD
should not be used with this method,
and it requires CpuBindingSupport::get_process()
or
CpuBindingSupport::get_thread()
depending on flags.
§Errors
BadFlags
if one of theNO_MEMORY_BINDING
was specified, if flagTHREAD
was specified on an operating system other than Linux, or if any other binding target flag was specifiedBadObject(ProcessOrThread)
if it is not possible to query the CPU binding of the target process/thread
§Panics
Some operating systems use signed PIDs, and do not support PIDs greater
than i32::MAX
. This method will panic when passed such an obviously
invalid PID on these operating systems.
sourcepub fn bind_thread_cpu(
&self,
tid: ThreadId,
set: impl Deref<Target = CpuSet>,
flags: CpuBindingFlags
) -> Result<(), HybridError<CpuBindingError>>
pub fn bind_thread_cpu( &self, tid: ThreadId, set: impl Deref<Target = CpuSet>, flags: CpuBindingFlags ) -> Result<(), HybridError<CpuBindingError>>
Bind a thread, identified by its tid
, on the given CPUs
See Topology::bind_cpu()
for more informations, except binding
target flags should not be used with this method, and it always
requires CpuBindingSupport::set_thread()
.
§Errors
BadCpuSet
if it is not possible to bind the target thread to the requested CPU set, specificallyBadFlags
if a binding target flag was specifiedBadObject(Thread)
if it is not possible to bind the target thread to CPUs, generally speaking
sourcepub fn thread_cpu_binding(
&self,
tid: ThreadId,
flags: CpuBindingFlags
) -> Result<CpuSet, HybridError<CpuBindingError>>
pub fn thread_cpu_binding( &self, tid: ThreadId, flags: CpuBindingFlags ) -> Result<CpuSet, HybridError<CpuBindingError>>
Get the current physical binding of thread tid
Flags STRICT
, NO_MEMORY_BINDING
and binding target flags should
not be used with this method.
See Topology::cpu_binding()
for more informations, except binding
target flags should not be used with this method, and it requires
CpuBindingSupport::get_thread()
.
§Errors
BadFlags
if at least one of flagsSTRICT
andNO_MEMORY_BINDING
or a binding target flag was specifiedBadObject(Thread)
if it is not possible to query the CPU binding of the target thread
sourcepub fn last_cpu_location(
&self,
flags: CpuBindingFlags
) -> Result<CpuSet, HybridError<CpuBindingError>>
pub fn last_cpu_location( &self, flags: CpuBindingFlags ) -> Result<CpuSet, HybridError<CpuBindingError>>
Get the last physical CPUs where the current process or thread ran
The operating system may move some tasks from one processor to another at any time according to their binding, so this method may return something that is already outdated.
You must specify exactly one of the ASSUME_SINGLE_THREAD
,
THREAD
and PROCESS
binding target flags (listed in order of
decreasing portability) when using this method.
Flags NO_MEMORY_BINDING
and STRICT
should not be used with this
method.
Requires CpuBindingSupport::get_current_process_last_cpu_location()
or CpuBindingSupport::get_current_thread_last_cpu_location()
depending on flags.
See also the top-level CPU binding CPU documentation.
§Errors
BadFlags
if one of flagsNO_MEMORY_BINDING
andSTRICT
was specified, or if the number of binding target flags is not exactly oneBadObject(ThisProgram)
if it is not possible to query the CPU location of the current process/thread
sourcepub fn last_process_cpu_location(
&self,
pid: ProcessId,
flags: CpuBindingFlags
) -> Result<CpuSet, HybridError<CpuBindingError>>
pub fn last_process_cpu_location( &self, pid: ProcessId, flags: CpuBindingFlags ) -> Result<CpuSet, HybridError<CpuBindingError>>
Get the last physical CPU where a process ran.
As a special case on Linux, if a tid (thread ID) is supplied instead of
a pid (process ID) and flag THREAD
is specified, the last cpu
location of the specified thread is returned. Otherwise, flag THREAD
should not be used with this method.
See Topology::last_cpu_location()
for more informations, except
binding target flags other than THREAD
should not be used with this
method, and it requires
CpuBindingSupport::get_process_last_cpu_location()
.
§Errors
BadFlags
if one of flagsNO_MEMORY_BINDING
andSTRICT
was specified, if flagTHREAD
was specified on an operating system other than Linux, or if any other binding target flag was specifiedBadObject(ProcessOrThread)
if it is not possible to query the CPU binding of the target process/thread
§Panics
Some operating systems use signed PIDs, and do not support PIDs greater
than i32::MAX
. This method will panic when passed such an obviously
invalid PID on these operating systems.
source§impl Topology
impl Topology
§CPU cache statistics
sourcepub fn cpu_cache_stats(&self) -> Option<CpuCacheStats>
pub fn cpu_cache_stats(&self) -> Option<CpuCacheStats>
Compute CPU cache statistics, if cache sizes are known
Returns None if cache size information is unavailable for at least some of the CPU caches on the system.
These statistics can be used to perform simple cache locality optimizations when your performance requirements do not call for full locality-aware scheduling with careful task and memory pinning.
This functionality is an hwlocality-specific extension to the hwloc API.
§Examples
use eyre::eyre;
let stats = topology
.cpu_cache_stats()
.ok_or_else(|| eyre!("CPU cache sizes are not known"))?;
println!(
"Minimal data cache sizes: {:?}",
stats.smallest_data_cache_sizes()
);
println!(
"Minimal data cache sizes per thread: {:?}",
stats.smallest_data_cache_sizes_per_thread()
);
println!(
"Total data cache sizes: {:?}",
stats.total_data_cache_sizes()
);
source§impl Topology
impl Topology
§Finding objects inside a CPU set
sourcepub fn largest_objects_inside_cpuset(
&self,
set: CpuSet
) -> impl FusedIterator<Item = &TopologyObject>
pub fn largest_objects_inside_cpuset( &self, set: CpuSet ) -> impl FusedIterator<Item = &TopologyObject>
Enumerate the largest objects included in the given cpuset set
Objects with empty CPU sets are ignored (otherwise they would be considered included in any given set).
In the common case where set
is a subset of the root cpuset, this
iteration can be more efficiently performed by using
coarsest_cpuset_partition()
.
sourcepub fn coarsest_cpuset_partition(
&self,
set: impl Deref<Target = CpuSet>
) -> Result<Vec<&TopologyObject>, CoarsestPartitionError>
pub fn coarsest_cpuset_partition( &self, set: impl Deref<Target = CpuSet> ) -> Result<Vec<&TopologyObject>, CoarsestPartitionError>
Get the largest objects exactly covering the given cpuset set
set
can be a &'_ CpuSet
or a BitmapRef<'_, CpuSet>
.
Objects with empty CPU sets are ignored (otherwise they would be considered included in any given set).
§Errors
CoarsestPartitionError
ifset
covers more indices than the topology’s root cpuset
sourcepub fn objects_inside_cpuset_at_depth<'result, DepthLike>(
&'result self,
set: impl Deref<Target = CpuSet> + 'result,
depth: DepthLike
) -> impl DoubleEndedIterator<Item = &TopologyObject> + FusedIterator + 'result
pub fn objects_inside_cpuset_at_depth<'result, DepthLike>( &'result self, set: impl Deref<Target = CpuSet> + 'result, depth: DepthLike ) -> impl DoubleEndedIterator<Item = &TopologyObject> + FusedIterator + 'result
Enumerate objects included in the given cpuset set
at a certain depth
Accepted operand types are as follows:
set
can be a&'_ CpuSet
or aBitmapRef<'_, CpuSet>
depth
can be aDepth
, aNormalDepth
or anusize
Objects with empty CPU sets are ignored (otherwise they would be considered included in any given set). Therefore, an empty iterator will always be returned for I/O or Misc depths as those objects have no cpusets.
sourcepub fn object_index_inside_cpuset<'result>(
&'result self,
set: impl Deref<Target = CpuSet> + 'result,
obj: &TopologyObject
) -> Option<usize>
pub fn object_index_inside_cpuset<'result>( &'result self, set: impl Deref<Target = CpuSet> + 'result, obj: &TopologyObject ) -> Option<usize>
Logical index among the objects included in CPU set set
Consult all objects in the same level as obj
and inside CPU set set
in the logical order, and return the index of obj
within them. If
set
covers the entire topology, this is the logical index of obj
.
Otherwise, this is similar to a logical index within the part of the
topology defined by CPU set set
.
set
can be a &'_ CpuSet
or a BitmapRef<'_, CpuSet>
.
Objects with empty CPU sets are ignored (otherwise they would be
considered included in any given set). Therefore, None
will always be
returned for I/O or Misc depths as those objects have no cpusets.
This method will also return None
if called with an obj
that does
not belong to this Topology
.
sourcepub fn objects_inside_cpuset_with_type<'result>(
&'result self,
set: impl Deref<Target = CpuSet> + 'result,
object_type: ObjectType
) -> impl DoubleEndedIterator<Item = &TopologyObject> + FusedIterator + 'result
pub fn objects_inside_cpuset_with_type<'result>( &'result self, set: impl Deref<Target = CpuSet> + 'result, object_type: ObjectType ) -> impl DoubleEndedIterator<Item = &TopologyObject> + FusedIterator + 'result
Get objects included in the given cpuset set
with a certain type
set
can be a &'_ CpuSet
or a BitmapRef<'_, CpuSet>
.
Objects with empty CPU sets are ignored (otherwise they would be considered included in any given set). Therefore, an empty iterator will always be returned for I/O or Misc objects as they don’t have cpusets.
source§impl Topology
impl Topology
§Finding objects covering at least a CPU set
sourcepub fn smallest_object_covering_cpuset(
&self,
set: impl Deref<Target = CpuSet>
) -> Option<&TopologyObject>
pub fn smallest_object_covering_cpuset( &self, set: impl Deref<Target = CpuSet> ) -> Option<&TopologyObject>
Get the lowest object covering at least the given cpuset set
, if any
set
can be a &'_ CpuSet
or a BitmapRef<'_, CpuSet>
.
No object is considered to cover the empty cpuset, therefore such a request will always return None, as if a set going outside of the root cpuset were passed as input.
sourcepub fn first_cache_covering_cpuset(
&self,
set: impl Deref<Target = CpuSet>
) -> Option<&TopologyObject>
pub fn first_cache_covering_cpuset( &self, set: impl Deref<Target = CpuSet> ) -> Option<&TopologyObject>
Get the first data (or unified) cache covering the given cpuset
set
can be a &'_ CpuSet
or a BitmapRef<'_, CpuSet>
.
sourcepub fn objects_covering_cpuset_at_depth<'result, DepthLike>(
&'result self,
set: impl Deref<Target = CpuSet> + 'result,
depth: DepthLike
) -> impl DoubleEndedIterator<Item = &TopologyObject> + FusedIterator + 'result
pub fn objects_covering_cpuset_at_depth<'result, DepthLike>( &'result self, set: impl Deref<Target = CpuSet> + 'result, depth: DepthLike ) -> impl DoubleEndedIterator<Item = &TopologyObject> + FusedIterator + 'result
Enumerate objects covering the given cpuset set
at a certain depth
Accepted operand types are as follows:
set
can be a&'_ CpuSet
or aBitmapRef<'_, CpuSet>
depth
can be aDepth
, aNormalDepth
or anusize
Objects are not considered to cover the empty CPU set (otherwise a list of all objects would be returned). An empty iterator will always be returned for I/O or Misc depths as those objects have no cpusets.
sourcepub fn objects_covering_cpuset_with_type<'result>(
&'result self,
set: impl Deref<Target = CpuSet> + 'result,
object_type: ObjectType
) -> impl DoubleEndedIterator<Item = &TopologyObject> + FusedIterator + 'result
pub fn objects_covering_cpuset_with_type<'result>( &'result self, set: impl Deref<Target = CpuSet> + 'result, object_type: ObjectType ) -> impl DoubleEndedIterator<Item = &TopologyObject> + FusedIterator + 'result
Get objects covering the given cpuset set
with a certain type
set
can be a &'_ CpuSet
or a BitmapRef<'_, CpuSet>
.
Objects are not considered to cover the empty CPU set (otherwise a list of all objects would be returned). An empty iterator will always be returned for I/O or Misc depths as those objects have no cpusets.
source§impl Topology
impl Topology
§Kinds of CPU cores
Platforms with heterogeneous CPUs may have some cores with different features or frequencies. This API exposes identical PUs in sets called CPU kinds. Each PU of the topology may only be in a single kind.
The number of kinds may be obtained with num_cpu_kinds()
. If the
platform is homogeneous, there may be a single kind with all PUs. If the
platform or operating system does not expose any information about CPU
cores, there may be no kind at all.
Information about CPU kinds can also be enumerated using cpu_kinds()
.
For each CPU kind, an abstracted efficiency value is provided, along with
info attributes
such as “CoreType” or “FrequencyMaxMHz”.
A higher efficiency value means greater intrinsic performance (and possibly
less performance/power efficiency). Kinds with lower efficiency values are
ranked first: the first CPU kind yielded by cpu_kinds()
describes CPUs
with lower performance but higher energy-efficiency. Later CPU kinds would
rather describe power-hungry high-performance cores.
When available, efficiency values are gathered from the operating system. If
so, the DiscoverySupport::cpukind_efficiency()
feature support flag
will be set. This is currently available on Windows 10, Mac OS X
(Darwin), and on some Linux platforms where core “capacity” is exposed in
sysfs. Efficiency values will range from 0 to the number of CPU kinds minus
one.
If the operating system does not expose core efficiencies natively, hwloc
tries to compute efficiencies by comparing CPU kinds using frequencies
(on ARM), or core types and frequencies (on other architectures). The
environment variable HWLOC_CPUKINDS_RANKING
may be used to change this
heuristics, see Environment
Variables.
If hwloc fails to rank any kind, for instance because the operating system
does not expose efficiencies and core frequencies, all kinds will have an
unknown efficiency (None
), and they are not ordered in any specific way.
The kind that describes a given CPU set (if any, and not partially) may also
be queried with cpu_kind_from_set()
.
sourcepub fn num_cpu_kinds(&self) -> Result<NonZeroUsize, NoData>
Available on crate feature hwloc-2_4_0
only.
pub fn num_cpu_kinds(&self) -> Result<NonZeroUsize, NoData>
hwloc-2_4_0
only.sourcepub fn cpu_kinds(
&self
) -> Result<impl DoubleEndedIterator<Item = CpuKind<'_>> + Clone + ExactSizeIterator + FusedIterator, NoData>
Available on crate feature hwloc-2_4_0
only.
pub fn cpu_kinds( &self ) -> Result<impl DoubleEndedIterator<Item = CpuKind<'_>> + Clone + ExactSizeIterator + FusedIterator, NoData>
hwloc-2_4_0
only.Enumerate CPU kinds, from least efficient efficient to most efficient
For each CPU kind, provide the CpuSet
of PUs belonging to that kind,
how efficient this CPU kind is (if CPU kind efficiencies are known) and
other things we know about
it.
§Errors
NoData
if no information about CPU kinds was found
sourcepub fn cpu_kind_from_set(
&self,
set: impl Deref<Target = CpuSet>
) -> Result<CpuKind<'_>, FromSetError>
Available on crate feature hwloc-2_4_0
only.
pub fn cpu_kind_from_set( &self, set: impl Deref<Target = CpuSet> ) -> Result<CpuKind<'_>, FromSetError>
hwloc-2_4_0
only.Query information about the CPU kind that contains CPUs listed in set
set
can be a &'_ CpuSet
or a BitmapRef<'_, CpuSet>
.
§Errors
PartiallyIncluded
ifset
is only partially included in some kind (i.e. some CPUs in the set belong to a kind, others to other kind(s))NotIncluded
ifset
is not included in any kind, even partially (i.e. CPU kind info isn’t known or CPU set does not cover real CPUs)
source§impl Topology
impl Topology
§Linux-specific helpers
This includes helpers for manipulating Linux kernel cpumap files, and hwloc
equivalents of the Linux sched_setaffinity
and sched_getaffinity
system
calls.
sourcepub fn bind_tid_cpu(
&self,
tid: pid_t,
set: impl Deref<Target = CpuSet>
) -> Result<(), RawHwlocError>
Available on Linux only.
pub fn bind_tid_cpu( &self, tid: pid_t, set: impl Deref<Target = CpuSet> ) -> Result<(), RawHwlocError>
Bind a thread tid
on cpus given in set
set
can be a &'_ CpuSet
or a BitmapRef<'_, CpuSet>
.
The behavior is exactly the same as the Linux sched_setaffinity
system
call, but uses a hwloc CpuSet
.
This is equivalent to calling bind_process_cpu()
with the THREAD
binding flag.
sourcepub fn tid_cpu_binding(&self, tid: pid_t) -> Result<CpuSet, RawHwlocError>
Available on Linux only.
pub fn tid_cpu_binding(&self, tid: pid_t) -> Result<CpuSet, RawHwlocError>
Current binding of thread tid
.
Returns the CpuSet
of PUs which the thread was last bound to.
The behavior is exactly the same as the Linux sched_getaffinity
system
call, but uses a hwloc CpuSet
.
This is equivalent to calling process_cpu_binding()
with the
THREAD
binding flag.
sourcepub fn tid_last_cpu_location(&self, tid: pid_t) -> Result<CpuSet, RawHwlocError>
Available on Linux only.
pub fn tid_last_cpu_location(&self, tid: pid_t) -> Result<CpuSet, RawHwlocError>
Last physical CPU where thread tid
ran.
Indicates the PU which the thread last ran on, as a singleton CpuSet
.
This is equivalent to calling last_process_cpu_location()
with the
THREAD
binding flag.
sourcepub fn read_path_as_cpumask(
&self,
path: impl AsRef<Path>
) -> Result<CpuSet, HybridError<PathError>>
Available on Linux only.
pub fn read_path_as_cpumask( &self, path: impl AsRef<Path> ) -> Result<CpuSet, HybridError<PathError>>
Convert a linux kernel cpumask file path into a hwloc bitmap set.
Might be used when reading CPU sets from sysfs attributes such as topology and caches for processors, or local_cpus for devices.
Note that this function ignores the HWLOC_FSROOT environment variable.
§Errors
ContainsNul
ifpath
contains NUL chars.NotUnicode
ifpath
contains non-Unicode data
§Example
use eyre::eyre;
// Read cpuset of first core via Linux sysfs
let core_set = topology
.read_path_as_cpumask("/sys/devices/system/cpu/cpu0/topology/core_cpus")?;
// Check that the hwloc version is consistent
assert_eq!(
core_set,
topology
.objects_with_type(ObjectType::Core)
.next()
.ok_or_else(|| eyre!("Linux systesm should have CPU cores"))?
.cpuset()
.ok_or_else(|| eyre!("CPU cores should have cpusets"))?
);
source§impl Topology
impl Topology
§Windows-specific helpers
These functions query Windows processor groups. These groups partition the operating system into virtual sets of up to 64 neighbor PUs. Threads and processes may only be bound inside a single group. Although Windows processor groups may be exposed in the hwloc hierarchy as hwloc Groups, they are also often merged into existing hwloc objects such as NUMA nodes or Packages. This API provides explicit information about Windows processor groups so that applications know whether binding to a large set of PUs may fail because it spans over multiple Windows processor groups.
sourcepub fn num_processor_groups(&self) -> Result<NonZeroUsize, RawHwlocError>
Available on crate feature hwloc-2_5_0
and Windows only.
pub fn num_processor_groups(&self) -> Result<NonZeroUsize, RawHwlocError>
hwloc-2_5_0
and Windows only.Number of Windows processor groups
§Errors
One reason why this function can fail is if the topology does not match the current system (e.g. loaded from another machine through XML).
sourcepub fn processor_groups(
&self
) -> Result<impl DoubleEndedIterator<Item = Result<CpuSet, RawHwlocError>> + Clone + ExactSizeIterator + FusedIterator + '_, RawHwlocError>
Available on crate feature hwloc-2_5_0
and Windows only.
pub fn processor_groups( &self ) -> Result<impl DoubleEndedIterator<Item = Result<CpuSet, RawHwlocError>> + Clone + ExactSizeIterator + FusedIterator + '_, RawHwlocError>
hwloc-2_5_0
and Windows only.Enumerate the cpusets of Windows processor groups
§Errors
One reason why this function can fail is if the topology does not match the current system (e.g. loaded from another machine through XML).
source§impl Topology
impl Topology
§Comparing memory node attributes for finding where to allocate on
Platforms with heterogeneous memory require ways to decide whether a buffer should be allocated on “fast” memory (such as HBM), “normal” memory (DDR) or even “slow” but large-capacity memory (non-volatile memory). These memory nodes are called “Targets” while the CPU accessing them is called the “Initiator”. Access performance depends on their locality (NUMA platforms) as well as the intrinsic performance of the targets (heterogeneous platforms).
The following attributes describe the performance of memory accesses from an Initiator to a memory Target, for instance their latency or bandwidth. Initiators performing these memory accesses are usually some PUs or Cores (described as a CPU set). Hence a Core may choose where to allocate a memory buffer by comparing the attributes of different target memory nodes nearby.
There are also some attributes that are system-wide. Their value does not depend on a specific initiator performing an access. The memory node capacity is an example of such attribute without initiator.
One way to use this API is to start with a cpuset describing the Cores where
a program is bound. The best target NUMA node for allocating memory in this
program on these Cores may be obtained by passing this cpuset as an
initiator to MemoryAttribute::best_target()
with the relevant
memory attribute. For instance, if the code is latency limited, use the
Latency attribute.
A more flexible approach consists in getting the list of local NUMA nodes by
passing this cpuset to Topology::local_numa_nodes()
. Attribute values
for these nodes, if any, may then be obtained with
MemoryAttribute::value()
and manually compared with the desired criteria.
The API also supports specific objects as initiator, but it is currently not used internally by hwloc. Users may for instance use it to provide custom performance values for host memory accesses performed by GPUs. The interface actually also accepts targets that are not NUMA nodes.
sourcepub fn memory_attribute_named(
&self,
name: &str
) -> Result<Option<MemoryAttribute<'_>>, NulError>
Available on crate feature hwloc-2_3_0
only.
pub fn memory_attribute_named( &self, name: &str ) -> Result<Option<MemoryAttribute<'_>>, NulError>
hwloc-2_3_0
only.Identifier of the memory attribute with the given name
Note that a number of predefined memory attributes have hard-coded
identifiers and need not be queried by name at runtime, see the
constructors of MemoryAttribute
for more information.
§Errors
NulError
ifname
contains NUL chars.
sourcepub fn local_numa_nodes<'target>(
&self,
target: impl Into<TargetNumaNodes<'target>>
) -> Result<Vec<&TopologyObject>, HybridError<ForeignObjectError>>
Available on crate feature hwloc-2_3_0
only.
pub fn local_numa_nodes<'target>( &self, target: impl Into<TargetNumaNodes<'target>> ) -> Result<Vec<&TopologyObject>, HybridError<ForeignObjectError>>
hwloc-2_3_0
only.Find local NUMA nodes
If target
is given as a TopologyObject
, its CPU set is used to
find NUMA nodes with the corresponding locality. If the object does not
have a CPU set (e.g. I/O object), the CPU parent (where the I/O object
is attached) is used.
Some of these NUMA nodes may not have any memory attribute values and hence not be reported as actual targets in other functions.
When an object CPU set is given as locality, for instance a Package, and
when flags
contains both LocalNUMANodeFlags::LARGER_LOCALITY
and
LocalNUMANodeFlags::SMALLER_LOCALITY
, the returned array corresponds
to the nodeset of that object.
§Errors
ForeignObjectError
iftarget
refers to aTopologyObject
that does not belong to this topology.
source§impl Topology
impl Topology
§Memory binding
Memory binding can be done three ways:
- Explicit memory allocation through
allocate_bound_memory()
and friends: the binding will have effect on the memory allocated by these methods. - Implicit memory binding through process/thread binding policy through
bind_memory()
and friends: the binding will be applied to subsequent memory allocations by the target process/thread. - Migration of existing memory ranges through
bind_memory_area()
and friends: already-allocated data will be migrated to the target NUMA nodes.
Not all operating systems support all three ways.
Topology::feature_support()
may be used to query about the actual memory
binding support in the currently used operating system. Individual memory
binding methods will clarify which support flags they require. The most
portable operation, where usable, is binding_allocate_memory()
.
By default, when the requested binding operation is not available, hwloc
will go for a similar binding operation (with side-effects, smaller
binding set, etc). You can inhibit this with flag STRICT
, at the
expense of reducing portability across operating systems.
Memory can be bound by CpuSet
or NodeSet
, but memory binding by
CPU set cannot work for CPU-less NUMA memory nodes. Binding by node set
should therefore be preferred whenever possible.
You should specify one of the ASSUME_SINGLE_THREAD
, PROCESS
and
THREAD
flags (the former being best for portability) when using any of
the methods that target a process, but some methods may only support a
subset of these flags.
On some operating systems, memory binding affects CPU binding. You can avoid
this at the cost of reducing portability by specifying the
NO_CPU_BINDING
flag.
sourcepub fn allocate_memory(
&self,
len: usize
) -> Result<Bytes<'_>, MemoryAllocationError<NodeSet>>
pub fn allocate_memory( &self, len: usize ) -> Result<Bytes<'_>, MemoryAllocationError<NodeSet>>
Allocate some memory
This is equivalent to libc::malloc()
, except that it tries to
allocate page-aligned memory from the OS.
§Errors
AllocationFailed
if memory allocation failedUnsupported
if the system cannot allocate page-aligned memory
sourcepub fn allocate_bound_memory<Set: SpecializedBitmap>(
&self,
len: usize,
set: &Set,
policy: MemoryBindingPolicy,
flags: MemoryBindingFlags
) -> Result<Bytes<'_>, MemoryAllocationError<Set::Owned>>
pub fn allocate_bound_memory<Set: SpecializedBitmap>( &self, len: usize, set: &Set, policy: MemoryBindingPolicy, flags: MemoryBindingFlags ) -> Result<Bytes<'_>, MemoryAllocationError<Set::Owned>>
Allocate some memory on NUMA nodes specified by set
If you do not care about changing the binding of the current process or
thread, you can maximize portability by using
Topology::binding_allocate_memory()
instead.
Memory can be bound by either CpuSet
or NodeSet
. Binding by
NodeSet
is preferred because some NUMA memory nodes are not attached
to CPUs, and thus cannot be bound by CpuSet
.
Binding target flags ASSUME_SINGLE_THREAD
, PROCESS
,
THREAD
and MIGRATE
should not be used with this method.
Requires MemoryBindingSupport::allocate_bound()
.
§Errors
AllocationFailed
if memory allocation failedBadFlags
if binding target flags were specifiedBadSet
if the system can’t bind memory to that CPU/node setUnsupported
if the system cannot allocate bound memory with the requested policy
sourcepub fn binding_allocate_memory<Set: SpecializedBitmap>(
&self,
len: usize,
set: &Set,
policy: MemoryBindingPolicy,
flags: MemoryBindingFlags
) -> Result<Bytes<'_>, MemoryAllocationError<Set::Owned>>
pub fn binding_allocate_memory<Set: SpecializedBitmap>( &self, len: usize, set: &Set, policy: MemoryBindingPolicy, flags: MemoryBindingFlags ) -> Result<Bytes<'_>, MemoryAllocationError<Set::Owned>>
Allocate some memory on NUMA nodes specified by set
and flags
,
possibly rebinding current process or thread if needed
This works like Topology::allocate_bound_memory()
unless the
allocation fails, in which case hwloc will attempt to change the current
process or thread memory binding policy as directed instead before
performing a normal allocation.
Allocating memory that matches the current process/thread configuration is supported on more operating systems, so this is the most portable way to obtain a bound memory buffer.
You must specify exactly one of the ASSUME_SINGLE_THREAD
,
PROCESS
and THREAD
binding target flags when using this method.
Requires either MemoryBindingSupport::allocate_bound()
, or one of
MemoryBindingSupport::set_current_process()
and
MemoryBindingSupport::set_current_thread()
depending on flags.
§Errors
AllocationFailed
if memory allocation failedBadFlags
if the number of specified binding target flags is not exactly oneBadSet
if the system can’t bind memory to that CPU/node setUnsupported
if the system can neither allocate bound memory nor rebind the current thread/process with the requested policy
sourcepub fn bind_memory<Set: SpecializedBitmap>(
&self,
set: &Set,
policy: MemoryBindingPolicy,
flags: MemoryBindingFlags
) -> Result<(), MemoryBindingError<Set::Owned>>
pub fn bind_memory<Set: SpecializedBitmap>( &self, set: &Set, policy: MemoryBindingPolicy, flags: MemoryBindingFlags ) -> Result<(), MemoryBindingError<Set::Owned>>
Set the default memory binding policy of the current process or thread
to prefer the NUMA node(s) specified by set
.
Memory can be bound by either CpuSet
or NodeSet
. Binding by
NodeSet
is preferred because some NUMA memory nodes are not attached
to CPUs, and thus cannot be bound by CpuSet
.
You must specify exactly one of the ASSUME_SINGLE_THREAD
,
PROCESS
and THREAD
binding target flags when using this method.
Requires MemoryBindingSupport::set_current_process()
or
MemoryBindingSupport::set_current_thread()
depending on flags.
§Errors
BadFlags
if the number of specified binding target flags is not exactly oneBadSet
if the system can’t bind memory to that CPU/node setUnsupported
if the system cannot bind the current thread/process with the requested policy
sourcepub fn unbind_memory(
&self,
flags: MemoryBindingFlags
) -> Result<(), MemoryBindingError<NodeSet>>
pub fn unbind_memory( &self, flags: MemoryBindingFlags ) -> Result<(), MemoryBindingError<NodeSet>>
Reset the memory allocation policy of the current process or thread to the system default
Depending on the operating system, this may correspond to
MemoryBindingPolicy::FirstTouch
(Linux, FreeBSD) or
MemoryBindingPolicy::Bind
(AIX, HP-UX, Solaris, Windows).
You must specify exactly one of the ASSUME_SINGLE_THREAD
,
PROCESS
and THREAD
binding target flags when using this method,
but the STRICT
and MIGRATE
flags should not be used with
this method.
Requires MemoryBindingSupport::set_current_process()
or
MemoryBindingSupport::set_current_thread()
depending on flags.
§Errors
BadFlags
if one of flagsSTRICT
andMIGRATE
was specified, or if the number of specified binding target flags is not exactly oneUnsupported
if the system cannot unbind the current thread/process
sourcepub fn memory_binding<OwnedSet: OwnedSpecializedBitmap>(
&self,
flags: MemoryBindingFlags
) -> Result<(OwnedSet, Option<MemoryBindingPolicy>), MemoryBindingError<OwnedSet>>
pub fn memory_binding<OwnedSet: OwnedSpecializedBitmap>( &self, flags: MemoryBindingFlags ) -> Result<(OwnedSet, Option<MemoryBindingPolicy>), MemoryBindingError<OwnedSet>>
Query the default memory binding policy and physical locality of the current process or thread
You must specify one of the ASSUME_SINGLE_THREAD
, PROCESS
and
THREAD
binding target flags when using this method. However, flags
MIGRATE
and NO_CPU_BINDING
should not be used with this
method.
The STRICT
flag is only meaningful when PROCESS
is also
specified. In this case, hwloc will check the default memory policies
and nodesets for all threads in the process. If they are not identical,
Err(MixedResults
) is returned. Otherwise,
the shared configuration is returned.
Otherwise, if PROCESS
is specified and STRICT
is not specified,
the default sets from each thread are logically OR’ed together. If all
threads’ default policies are the same, that shared policy is returned,
otherwise no policy is returned.
In the THREAD
and ASSUME_SINGLE_THREAD
case, there is only one
set and policy, they are returned.
Bindings can be queried as CpuSet
or NodeSet
. Querying by
NodeSet
is preferred because some NUMA memory nodes are not attached
to CPUs, and thus cannot be bound by CpuSet
.
Requires MemoryBindingSupport::get_current_process()
or
MemoryBindingSupport::get_current_thread()
depending on flags.
§Errors
BadFlags
if one of flagsMIGRATE
andNO_CPU_BINDING
was specified, if flagSTRICT
was specified withoutPROCESS
, or if the number of specified binding target flags is not exactly oneMixedResults
if flagsSTRICT
andPROCESS
were specified and memory binding is inhomogeneous across threads in the processUnsupported
if the system cannot query the current thread/process binding
sourcepub fn bind_process_memory<Set: SpecializedBitmap>(
&self,
pid: ProcessId,
set: &Set,
policy: MemoryBindingPolicy,
flags: MemoryBindingFlags
) -> Result<(), MemoryBindingError<Set::Owned>>
pub fn bind_process_memory<Set: SpecializedBitmap>( &self, pid: ProcessId, set: &Set, policy: MemoryBindingPolicy, flags: MemoryBindingFlags ) -> Result<(), MemoryBindingError<Set::Owned>>
Set the default memory binding policy of the specified process to prefer
the NUMA node(s) specified by set
.
See also Topology::bind_memory()
for general semantics, except
binding target flag THREAD
should not be used with this method, and
it requires MemoryBindingSupport::set_process()
.
§Errors
BadFlags
if flagTHREAD
was specified, or if the number of specified binding target flags is not exactly oneBadSet
if the system can’t bind memory to that CPU/node setUnsupported
if the system cannot bind the specified thread/process with the requested policy
§Panics
Some operating systems use signed PIDs, and do not support PIDs greater
than i32::MAX
. This method will panic when passed such an obviously
invalid PID on these operating systems.
sourcepub fn unbind_process_memory(
&self,
pid: ProcessId,
flags: MemoryBindingFlags
) -> Result<(), MemoryBindingError<NodeSet>>
pub fn unbind_process_memory( &self, pid: ProcessId, flags: MemoryBindingFlags ) -> Result<(), MemoryBindingError<NodeSet>>
Reset the memory allocation policy of the specified process to the system default
See also Topology::unbind_memory()
for general semantics, except
binding target flag THREAD
should not be used with this method, and
it requires MemoryBindingSupport::set_process()
.
§Errors
BadFlags
if one of flagsMIGRATE
,STRICT
andTHREAD
was specified, or if the number of specified binding target flags is not exactly oneUnsupported
if the system cannot unbind the specified thread/process
§Panics
Some operating systems use signed PIDs, and do not support PIDs greater
than i32::MAX
. This method will panic when passed such an obviously
invalid PID on these operating systems.
sourcepub fn process_memory_binding<OwnedSet: OwnedSpecializedBitmap>(
&self,
pid: ProcessId,
flags: MemoryBindingFlags
) -> Result<(OwnedSet, Option<MemoryBindingPolicy>), MemoryBindingError<OwnedSet>>
pub fn process_memory_binding<OwnedSet: OwnedSpecializedBitmap>( &self, pid: ProcessId, flags: MemoryBindingFlags ) -> Result<(OwnedSet, Option<MemoryBindingPolicy>), MemoryBindingError<OwnedSet>>
Query the default memory binding policy and physical locality of the specified process
See Topology::memory_binding()
for general semantics, except binding
target flag THREAD
should not be used with this method, and it
requires MemoryBindingSupport::get_process()
.
§Errors
BadFlags
if one of flagsMIGRATE
,NO_CPU_BINDING
andTHREAD
was specified, if flagSTRICT
was specified withoutPROCESS
, or if the number of specified binding target flags is not exactly oneMixedResults
if flagsSTRICT
andPROCESS
were specified and memory binding is inhomogeneous across threads in the processUnsupported
if the system cannot query the specified thread/process’ binding
§Panics
Some operating systems use signed PIDs, and do not support PIDs greater
than i32::MAX
. This method will panic when passed such an obviously
invalid PID on these operating systems.
sourcepub fn bind_memory_area<Target: ?Sized, Set: SpecializedBitmap>(
&self,
target: &Target,
set: &Set,
policy: MemoryBindingPolicy,
flags: MemoryBindingFlags
) -> Result<(), MemoryBindingError<Set::Owned>>
pub fn bind_memory_area<Target: ?Sized, Set: SpecializedBitmap>( &self, target: &Target, set: &Set, policy: MemoryBindingPolicy, flags: MemoryBindingFlags ) -> Result<(), MemoryBindingError<Set::Owned>>
Bind the memory identified by target
to the NUMA node(s) specified by
set
Beware that only the memory directly targeted by the target
reference
will be covered. So for example, you cannot pass in an &Vec<T>
and
expect the Vec’s contents to be covered, instead you must pass in the
&[T]
corresponding to the Vec’s contents as &vec[..]
. You may want
to manually specify the Target
type via turbofish to make sure that
you don’t get tripped up by references of references like &&[T]
.
See also Topology::bind_memory()
for general semantics, except
binding target flags should not be used with this method, and it
requires MemoryBindingSupport::set_area()
.
§Errors
BadFlags
if a binding target flag was specifiedBadSet
if the system can’t bind memory to that CPU/node setBadTarget
iftarget
is a zero-sized objectUnsupported
if the system cannot bind the specified memory area with the requested policy
sourcepub fn unbind_memory_area<Target: ?Sized>(
&self,
target: &Target,
flags: MemoryBindingFlags
) -> Result<(), MemoryBindingError<NodeSet>>
pub fn unbind_memory_area<Target: ?Sized>( &self, target: &Target, flags: MemoryBindingFlags ) -> Result<(), MemoryBindingError<NodeSet>>
Reset the memory allocation policy of the memory identified by target
to the system default
The warning about Target
coverage in the documentation of
Topology::bind_memory_area()
also applies here.
See also Topology::unbind_memory()
for general semantics, except
binding target flags should not be used with this method, and it
requiresMemoryBindingSupport::set_area()
.
§Errors
BadFlags
if one of flagsMIGRATE
andSTRICT
was specified, or if a binding target flag was specified.BadTarget
iftarget
is a zero-sized objectUnsupported
if the system cannot unbind the specified memory area
sourcepub fn area_memory_binding<Target: ?Sized, OwnedSet: OwnedSpecializedBitmap>(
&self,
target: &Target,
flags: MemoryBindingFlags
) -> Result<(OwnedSet, Option<MemoryBindingPolicy>), MemoryBindingError<OwnedSet>>
pub fn area_memory_binding<Target: ?Sized, OwnedSet: OwnedSpecializedBitmap>( &self, target: &Target, flags: MemoryBindingFlags ) -> Result<(OwnedSet, Option<MemoryBindingPolicy>), MemoryBindingError<OwnedSet>>
Query the memory binding policy and physical locality of the
memory identified by target
The warning about Target
coverage in the documentation of
Topology::bind_memory_area()
also applies here.
If the STRICT
flag is specified, hwloc will check the default memory
policies and nodesets for all memory pages covered by target
. If these
are not identical,
Err(MixedResults
) is returned. Otherwise,
the shared configuration is returned.
If STRICT
is not specified, the union of all NUMA nodes containing
pages in the address range is calculated. If all pages in the target
have the same policy, it is returned, otherwise no policy is returned.
See also Topology::memory_binding()
for general semantics, except…
- Binding target flags should not be used with this method
- As mentioned above,
STRICT
has a specific meaning in the context of this method. - This method requires
MemoryBindingSupport::get_area()
.
§Errors
BadFlags
if one of flagsMIGRATE
andNO_CPU_BINDING
was specified, or if a binding target flag was specified.BadTarget
iftarget
is a zero-sized objectMixedResults
if flagSTRICT
was specified and memory binding is inhomogeneous across target memory pagesUnsupported
if the system cannot query the specified memory area’s binding
sourcepub fn area_memory_location<Target: ?Sized, OwnedSet: OwnedSpecializedBitmap>(
&self,
target: &Target,
flags: MemoryBindingFlags
) -> Result<OwnedSet, MemoryBindingError<OwnedSet>>
pub fn area_memory_location<Target: ?Sized, OwnedSet: OwnedSpecializedBitmap>( &self, target: &Target, flags: MemoryBindingFlags ) -> Result<OwnedSet, MemoryBindingError<OwnedSet>>
Get the NUMA nodes where the memory identified by target
is physically
allocated
The warning about Target
coverage in the documentation of
Topology::bind_memory_area()
also applies here.
If pages spread to multiple nodes, it is not specified whether they spread equitably, or whether most of them are on a single node, etc.
The operating system may move memory pages from one processor to another at any time according to their binding, so this method may return something that is already outdated.
See also Topology::memory_binding()
for general semantics, except
binding target flags should not be used with this method, and it
requires MemoryBindingSupport::get_area_memory_location()
.
§Errors
BadFlags
if one of flagsMIGRATE
andNO_CPU_BINDING
was specified, or if a binding target flag was specified.BadTarget
iftarget
is a zero-sized objectMixedResults
if flagSTRICT
was specified and memory binding is inhomogeneous across target memory pagesUnsupported
if the system cannot query the specified memory area’s location
source§impl Topology
impl Topology
§Retrieve distances between objects
sourcepub fn distances(
&self,
kind: Option<DistancesKind>
) -> Result<Vec<Distances<'_>>, RawHwlocError>
pub fn distances( &self, kind: Option<DistancesKind> ) -> Result<Vec<Distances<'_>>, RawHwlocError>
Retrieve distance matrices from the topology
By default, all available distance matrices are returned. Some filtering
may be applied using the kind
parameter: if it contains some
DistancesKind
::FROM_xyz
options, only distance matrices matching
one of them is returned. The same applies for MEANS_xyz
options.
sourcepub fn distances_at_depth<DepthLike>(
&self,
kind: Option<DistancesKind>,
depth: DepthLike
) -> Result<Vec<Distances<'_>>, RawHwlocError>
pub fn distances_at_depth<DepthLike>( &self, kind: Option<DistancesKind>, depth: DepthLike ) -> Result<Vec<Distances<'_>>, RawHwlocError>
Retrieve distance matrices for objects at a specific depth in the topology (if any)
Identical to distances()
with the additional depth
filter.
depth
can be a Depth
, a NormalDepth
or an usize
.
sourcepub fn distances_with_type(
&self,
kind: Option<DistancesKind>,
ty: ObjectType
) -> Result<Vec<Distances<'_>>, RawHwlocError>
pub fn distances_with_type( &self, kind: Option<DistancesKind>, ty: ObjectType ) -> Result<Vec<Distances<'_>>, RawHwlocError>
Retrieve distance matrices for object with a specific type
Identical to distances()
with the additional ty
filter.
sourcepub fn distances_with_name(
&self,
name: &str
) -> Result<Vec<Distances<'_>>, HybridError<NulError>>
Available on crate feature hwloc-2_1_0
only.
pub fn distances_with_name( &self, name: &str ) -> Result<Vec<Distances<'_>>, HybridError<NulError>>
hwloc-2_1_0
only.Retrieve a distance matrix with the given name
Usually only one distances matrix may match a given name.
Names of distances matrices currently created by hwloc may be found in the hwloc documentation.
§Errors
NulError
ifname
contains NUL chars.
source§impl Topology
impl Topology
§Object levels, depths and types
Be sure to see read through the Terms and Definitions section of the upstream hwloc documentation to avoid any confusion about depths, child/sibling/cousin relationships, and see an example of an asymmetric topology where one package has fewer caches than its peers.
sourcepub fn depth(&self) -> NormalDepth
pub fn depth(&self) -> NormalDepth
Depth of the hierarchical tree of objects
This is the depth of ObjectType::PU
plus one. NUMA nodes, I/O and
Misc objects are ignored when computing the depth of the tree (they are
placed at special depths).
§Examples
let depth = topology.depth();
assert!(depth >= 2, "Machine and PU are always present");
assert_eq!(
depth,
topology.depth_for_type(ObjectType::PU)?.expect_normal() + 1
);
sourcepub fn memory_parents_depth(&self) -> Result<NormalDepth, TypeToDepthError>
pub fn memory_parents_depth(&self) -> Result<NormalDepth, TypeToDepthError>
Depth of normal parents where memory objects are attached
§Errors
TypeToDepthError::Multiple
if memory objects are attached at multiple depths, e.g. some toPackage
s and some toGroup
s
§Examples
if let Ok(depth) = topology.memory_parents_depth() {
let num_memory_objects =
topology.objects_at_depth(depth)
.flat_map(TopologyObject::memory_children)
.count();
assert!(num_memory_objects > 0);
}
sourcepub fn depth_for_type(
&self,
object_type: ObjectType
) -> Result<Depth, TypeToDepthError>
pub fn depth_for_type( &self, object_type: ObjectType ) -> Result<Depth, TypeToDepthError>
Depth for the given ObjectType
§Errors
TypeToDepthError::Nonexistent
if no object of this type is present or if the OS doesn’t provide this kind of information. If a similar type is acceptable, consider usingdepth_or_below_for_type()
ordepth_or_above_for_type()
instead.TypeToDepthError::Multiple
if objects of this type exist at multiple depths (can happen whenobject_type
isGroup
).
§Examples
let machine_depth = topology.depth_for_type(ObjectType::Machine)?;
let pu_depth = topology.depth_for_type(ObjectType::PU)?;
assert_eq!(machine_depth.expect_normal(), 0);
assert!(machine_depth.expect_normal() < pu_depth.expect_normal());
sourcepub fn depth_or_below_for_type(
&self,
object_type: ObjectType
) -> Result<Depth, TypeToDepthError>
pub fn depth_or_below_for_type( &self, object_type: ObjectType ) -> Result<Depth, TypeToDepthError>
Depth for the given ObjectType
or below
If no object of this type is present on the underlying architecture, the
function returns the depth of the first present object typically found
inside object_type
.
This function is only meaningful for normal object types. Passing in a memory, I/O or Misc object type will result in a panic.
§Errors
TypeToDepthError::Multiple
if objects of this type exist at multiple
depths (can happen when object_type
is Group
).
§Panics
If object_type
is not a normal object type.
§Examples
let machine_depth = topology.depth_for_type(ObjectType::Machine)?;
let package_or_below = topology.depth_or_below_for_type(ObjectType::Package)?;
assert!(machine_depth.expect_normal() < package_or_below.expect_normal());
sourcepub fn depth_or_above_for_type(
&self,
object_type: ObjectType
) -> Result<Depth, TypeToDepthError>
pub fn depth_or_above_for_type( &self, object_type: ObjectType ) -> Result<Depth, TypeToDepthError>
Depth for the given ObjectType
or above
If no object of this type is present on the underlying architecture, the
function returns the depth of the first present object typically
containing object_type
.
This function is only meaningful for normal object types. Passing in a memory, I/O or Misc object type will result in a panic.
§Errors
TypeToDepthError::Multiple
if objects of this type exist at multiple
depths (can happen when object_type
is Group
).
§Panics
If object_type
is not a normal object type.
§Examples
let pu_depth = topology.depth_for_type(ObjectType::PU)?;
let core_or_above = topology.depth_or_below_for_type(ObjectType::Core)?;
assert!(core_or_above.expect_normal() < pu_depth.expect_normal());
sourcepub fn depth_for_cache(
&self,
cache_level: usize,
cache_type: Option<CacheType>
) -> Result<Depth, TypeToDepthError>
pub fn depth_for_cache( &self, cache_level: usize, cache_type: Option<CacheType> ) -> Result<Depth, TypeToDepthError>
Depth for the given cache type and level
Returns the depth of the topology level that contains cache objects whose
attributes match cache_level
and cache_type
.
This function is similar to calling depth_for_type()
with
the corresponding type such as ObjectType::L1ICache
, except that it
may also return a unified cache when looking for an instruction cache.
Please note that following hardware nomenclature, cache levels normally start at 1 (corresponding to the hardware L1 cache), not 0.
If cache_type
is None
, it is ignored and multiple levels may match.
The function returns either the depth of a uniquely matching level or
Err(TypeToDepthError::Multiple
).
If cache_type
is Some(CacheType::Unified
), the depth of the unique
matching unified cache level (if any) is returned.
If cache_type
is Some(CacheType::Data
) or
Some(CacheType::Instruction
), either a matching cache or a
unified cache is returned.
§Errors
TypeToDepthError::Nonexistent
if no cache level matchesTypeToDepthError::Multiple
if multiple cache depths match (this can only happen ifcache_type
isNone
).
§Examples
let l1d_depth = topology.depth_for_cache(1, Some(CacheType::Data));
assert!(l1d_depth.is_ok());
sourcepub fn type_at_depth<DepthLike>(&self, depth: DepthLike) -> Option<ObjectType>
pub fn type_at_depth<DepthLike>(&self, depth: DepthLike) -> Option<ObjectType>
Type of objects at the given depth
, if any
depth
can be a Depth
, a NormalDepth
or an usize
.
§Examples
let numa_type = topology.type_at_depth(Depth::NUMANode);
assert_eq!(numa_type, Some(ObjectType::NUMANode));
sourcepub fn num_objects_at_depth<DepthLike>(&self, depth: DepthLike) -> usize
pub fn num_objects_at_depth<DepthLike>(&self, depth: DepthLike) -> usize
Number of objects at the given depth
depth
can be a Depth
, a NormalDepth
or an usize
.
§Examples
let num_roots = topology.num_objects_at_depth(0);
assert_eq!(num_roots, 1);
let num_root_children = topology.num_objects_at_depth(1);
assert!(num_root_children > 0);
sourcepub fn objects_at_depth<DepthLike>(
&self,
depth: DepthLike
) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + ExactSizeIterator + FusedIterator
pub fn objects_at_depth<DepthLike>( &self, depth: DepthLike ) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + ExactSizeIterator + FusedIterator
TopologyObject
s at the given depth
depth
can be a Depth
, a NormalDepth
or an usize
.
§Examples
use eyre::eyre;
let root = topology.root_object();
for node in topology.objects_at_depth(Depth::NUMANode) {
assert_eq!(node.object_type(), ObjectType::NUMANode);
assert!(node.is_in_subtree(root));
assert_eq!(node.normal_arity(), 0);
assert_eq!(node.memory_arity(), 0);
let num_nodes =
node.nodeset()
.ok_or_else(|| eyre!("a NUMANode should have a NodeSet"))?
.weight()
.ok_or_else(|| {
eyre!("a NUMANode's NodeSet should be finite")
})?;
assert_eq!(num_nodes, 1);
}
sourcepub fn root_object(&self) -> &TopologyObject
pub fn root_object(&self) -> &TopologyObject
TopologyObject
at the root of the topology
Its type is ObjectType::Machine
.
§Examples
let root = topology.root_object();
assert_eq!(root.object_type(), ObjectType::Machine);
assert_eq!(root.depth(), NormalDepth::MIN);
assert!(root.parent().is_none());
assert!(root.cpuset().is_some());
assert!(root.nodeset().is_some());
println!("{root:#}");
sourcepub fn objects_with_type(
&self,
object_type: ObjectType
) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + ExactSizeIterator + FusedIterator
pub fn objects_with_type( &self, object_type: ObjectType ) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + ExactSizeIterator + FusedIterator
TopologyObject
s with the given ObjectType
§Examples
use eyre::eyre;
let root = topology.root_object();
for pu in topology.objects_with_type(ObjectType::PU) {
assert_eq!(pu.object_type(), ObjectType::PU);
assert!(pu.is_in_subtree(root));
assert_eq!(pu.normal_arity(), 0);
let num_cpus =
pu
.cpuset()
.ok_or_else(|| eyre!("a PU should have a CpuSet"))?
.weight()
.ok_or_else(|| {
eyre!("a PU's CpuSet should be finite")
})?;
assert_eq!(num_cpus, 1);
}
source§impl Topology
impl Topology
§Full object lists
For some use cases, especially testing, it is convenient to have a full list of all objects contained within a topology. These methods provide just that.
This functionality is unique to the Rust hwloc bindings
sourcepub fn objects(&self) -> impl FusedIterator<Item = &TopologyObject> + Clone
pub fn objects(&self) -> impl FusedIterator<Item = &TopologyObject> + Clone
Full list of objects in the topology, first normal objects ordered by increasing depth then virtual objects ordered by type
sourcepub fn normal_objects(
&self
) -> impl FusedIterator<Item = &TopologyObject> + Clone
pub fn normal_objects( &self ) -> impl FusedIterator<Item = &TopologyObject> + Clone
Full list of objects contains in the normal hierarchy of the topology, ordered by increasing depth
sourcepub fn virtual_objects(
&self
) -> impl FusedIterator<Item = &TopologyObject> + Clone
pub fn virtual_objects( &self ) -> impl FusedIterator<Item = &TopologyObject> + Clone
Full list of virtual objects in the topology, ordered by type
sourcepub fn memory_objects(
&self
) -> impl FusedIterator<Item = &TopologyObject> + Clone
pub fn memory_objects( &self ) -> impl FusedIterator<Item = &TopologyObject> + Clone
Full list of memory objects in the topology, ordered by type
sourcepub fn io_objects(&self) -> impl FusedIterator<Item = &TopologyObject> + Clone
pub fn io_objects(&self) -> impl FusedIterator<Item = &TopologyObject> + Clone
Full list of I/O objects in the topology, ordered by type
source§impl Topology
impl Topology
§Finding I/O objects
sourcepub fn pci_devices(
&self
) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + ExactSizeIterator + FusedIterator
pub fn pci_devices( &self ) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + ExactSizeIterator + FusedIterator
Enumerate PCI devices in the system
sourcepub fn pci_device_by_bus_id(
&self,
domain: PCIDomain,
bus_id: u8,
bus_device: u8,
function: u8
) -> Option<&TopologyObject>
pub fn pci_device_by_bus_id( &self, domain: PCIDomain, bus_id: u8, bus_device: u8, function: u8 ) -> Option<&TopologyObject>
Find the PCI device object matching the PCI bus id given domain, bus device and function PCI bus id
sourcepub fn pci_device_by_bus_id_string(
&self,
bus_id: &str
) -> Result<Option<&TopologyObject>, ParameterError<String>>
pub fn pci_device_by_bus_id_string( &self, bus_id: &str ) -> Result<Option<&TopologyObject>, ParameterError<String>>
Find the PCI device object matching the PCI bus id given as a string of format “xxxx:yy:zz.t” (with domain) or “yy:zz.t” (without domain)
§Errors
ParameterError
if the given string does not match the PCI bus id format given above
sourcepub fn os_devices(
&self
) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + ExactSizeIterator + FusedIterator
pub fn os_devices( &self ) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + ExactSizeIterator + FusedIterator
Enumerate OS devices in the system
sourcepub fn bridges(
&self
) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + ExactSizeIterator + FusedIterator
pub fn bridges( &self ) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + ExactSizeIterator + FusedIterator
Enumerate bridges in the system
source§impl Topology
impl Topology
§Finding other objects
sourcepub fn pu_with_os_index(&self, os_index: usize) -> Option<&TopologyObject>
pub fn pu_with_os_index(&self, os_index: usize) -> Option<&TopologyObject>
Get the object of type ObjectType::PU
with the specified OS index
If you want to convert an entire CPU set into the PU objects it
contains, using pus_from_cpuset()
will be more efficient than
repeatedly calling this function with every OS index from the CpuSet
.
Requires DiscoverySupport::pu_count()
.
sourcepub fn pus_from_cpuset<'result>(
&'result self,
cpuset: impl Deref<Target = CpuSet> + Clone + 'result
) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + FusedIterator + 'result
pub fn pus_from_cpuset<'result>( &'result self, cpuset: impl Deref<Target = CpuSet> + Clone + 'result ) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + FusedIterator + 'result
Get the objects of type ObjectType::PU
covered by the specified cpuset
cpuset
can be a &'_ CpuSet
or a BitmapRef<'_, CpuSet>
.
Requires DiscoverySupport::pu_count()
.
This functionality is specific to the Rust bindings.
sourcepub fn node_with_os_index(&self, os_index: usize) -> Option<&TopologyObject>
pub fn node_with_os_index(&self, os_index: usize) -> Option<&TopologyObject>
Get the object of type NUMANode
with the specified OS index
If you want to convert an entire [NodeSet
into the NUMANode
objects it contains, using nodes_from_nodeset()
will be more
efficient than repeatedly calling this function with every OS index from
the NodeSet
.
Requires DiscoverySupport::numa_count()
.
sourcepub fn nodes_from_nodeset<'result>(
&'result self,
nodeset: impl Deref<Target = NodeSet> + Clone + 'result
) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + FusedIterator + 'result
pub fn nodes_from_nodeset<'result>( &'result self, nodeset: impl Deref<Target = NodeSet> + Clone + 'result ) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + FusedIterator + 'result
Get the objects of type ObjectType::NUMANode
covered by the
specified nodeset
nodeset
can be a &'_ NodeSet
or a BitmapRef<'_, NodeSet>
.
Requires DiscoverySupport::numa_count()
.
This functionality is specific to the Rust bindings.
sourcepub fn objects_closest_to<'result>(
&'result self,
obj: &'result TopologyObject
) -> Result<impl Iterator<Item = &TopologyObject> + Clone + 'result, ClosestObjectsError>
pub fn objects_closest_to<'result>( &'result self, obj: &'result TopologyObject ) -> Result<impl Iterator<Item = &TopologyObject> + Clone + 'result, ClosestObjectsError>
Enumerate objects at the same depth as obj
, but with increasing
physical distance (i.e. from increasingly higher common ancestors in the
topology tree).
This search may only be applied to objects that have a cpuset (normal and memory objects) and belong to this topology.
§Errors
ForeignObject
ifobj
does not belong to this topology.MissingCpuSet
ifobj
does not have a cpuset.
sourcepub fn object_by_type_index_path(
&self,
path: &[(ObjectType, usize)]
) -> Result<Option<&TopologyObject>, ParameterError<ObjectType>>
pub fn object_by_type_index_path( &self, path: &[(ObjectType, usize)] ) -> Result<Option<&TopologyObject>, ParameterError<ObjectType>>
Find an object via a parent->child chain specified by types and indices
For example, if called with &[(NUMANode, 0), (Package, 1), (Core, 2)]
,
this will return the third core object below the second package below
the first NUMA node.
The first object is indexed relative to the topology’s root Machine object and searched amongst its children. As a consequence, the root Machine object cannot be found using this method.
This search may only be applied to object types that have a cpuset (normal and memory objects).
§Errors
ParameterError
if one of the specified object types does not have a cpuset.
sourcepub fn object_with_same_locality(
&self,
src: &TopologyObject,
ty: ObjectType,
subtype: Option<&str>,
name_prefix: Option<&str>
) -> Result<Option<&TopologyObject>, LocalObjectError>
Available on crate feature hwloc-2_5_0
only.
pub fn object_with_same_locality( &self, src: &TopologyObject, ty: ObjectType, subtype: Option<&str>, name_prefix: Option<&str> ) -> Result<Option<&TopologyObject>, LocalObjectError>
hwloc-2_5_0
only.Find an object of a different type with the same locality
The source object src
must belong to this topology, otherwise a
ForeignSource
error will be returned.
If the source object is a normal or memory type, this function returns
an object of type ty
with the same CPU and node sets, either below or
above in the hierarchy.
If the source object is a PCI or an OS device within a PCI device, the function may either return that PCI device, or another OS device in the same PCI parent. This may for instance be useful for converting between OS devices such as “nvml0” or “rsmi1” used in distance structures into the the PCI device, or the CUDA or OpenCL OS device that correspond to the same physical card.
If specified, parameter subtype
restricts the search to objects whose
TopologyObject::subtype()
attribute exists and is equal to subtype
(case-insensitively), for instance “OpenCL” or “CUDA”.
If specified, parameter name_prefix
restricts the search to objects
whose TopologyObject::name()
attribute exists and starts with
name_prefix
(case-insensitively), for instance “rsmi” for matching
“rsmi0”.
If multiple objects match, the first one is returned.
This function will not walk the hierarchy across bridges since the PCI locality may become different. This function cannot also convert between normal/memory objects and I/O or Misc objects.
If no matching object could be found, or if the source object and target
type are incompatible, None
will be returned.
§Errors
ForeignSource
ifsrc
does not belong to this topology.IncompatibleTypes
ifsrc
is a normal/memory object andty
is an I/O or Misc object type, or vice versa.StringContainsNul
ifsubtype
orname_prefix
contains NUL chars.
source§impl Topology
impl Topology
§Modifying a loaded Topology
sourcepub fn edit<R>(
&mut self,
edit: impl UnwindSafe + FnOnce(&mut TopologyEditor<'_>) -> R
) -> R
Available on crate feature hwloc-2_3_0
only.
pub fn edit<R>( &mut self, edit: impl UnwindSafe + FnOnce(&mut TopologyEditor<'_>) -> R ) -> R
hwloc-2_3_0
only.Modify this topology
hwloc employs lazy caching patterns that do not interact well with
Rust’s shared XOR mutable aliasing model. This API lets you safely
modify the active Topology
through a TopologyEditor
proxy object,
with the guarantee that by the time Topology::edit()
returns, the
Topology
will be back in a state where it is safe to use &self
again.
In general, the hwlocality binding optimizes the ergonomics and performance of reading and using topologies at the expense of making them harder and slower to edit. If a strong need for easier or more efficient topology editing emerged, the right thing to do would probably be to set up an alternate hwloc Rust binding optimized for that, sharing as much code as possible with hwlocality.
source§impl Topology
impl Topology
§Exporting Topologies to Synthetic
sourcepub fn export_synthetic(
&self,
flags: SyntheticExportFlags
) -> Result<String, RawHwlocError>
pub fn export_synthetic( &self, flags: SyntheticExportFlags ) -> Result<String, RawHwlocError>
Export the topology as a synthetic string
This string may be loaded later using
TopologyBuilder::from_synthetic()
.
I/O and Misc children are ignored, the synthetic string only describes normal children.
By default, the exported topology is only meant to be compatible with
the latest hwloc version. You may want to set some of the flags
to be
compatible with older hwloc releases, at the cost of dropping support
for newer features.
§Errors
Synthetic topologies cannot express the full range of hardware topologies supported by hwloc, for example they don’t support asymmetric topologies. An error will be returned if the current topology cannot be expressed as a synthetic topology.
source§impl Topology
impl Topology
§Exporting Topologies to XML
sourcepub fn export_xml_file(
&self,
path: Option<&Path>,
flags: XMLExportFlags
) -> Result<(), HybridError<PathError>>
pub fn export_xml_file( &self, path: Option<&Path>, flags: XMLExportFlags ) -> Result<(), HybridError<PathError>>
Export the topology into an XML file at filesystem location path
If no path is given, the XML output is sent to standard output.
This file may be loaded later using TopologyBuilder::from_xml_file()
.
By default, the latest export format is used, which means older hwloc
releases (e.g. v1.x) will not be able to import it. Exporting to v1.x
specific XML format is possible using flag
XMLExportFlags::V1
but it may miss some details about the topology.
Also, note that this option will be removed from the (upcoming at the
time of writing) hwloc v3.0 release.
If there is any chance that the exported file may ever be imported back by a process using hwloc 1.x, one should consider detecting it at runtime and using the corresponding export format.
Only printable characters may be exported to XML string attributes. Any other character, especially any non-ASCII character, will be silently dropped.
§Errors
ContainsNul
ifpath
contains NUL chars.NotUnicode
ifpath
contains non-Unicode data
sourcepub fn export_xml(
&self,
flags: XMLExportFlags
) -> Result<XML<'_>, RawHwlocError>
pub fn export_xml( &self, flags: XMLExportFlags ) -> Result<XML<'_>, RawHwlocError>
Export the topology into an XML memory buffer
This memory buffer may be loaded later using
TopologyBuilder::from_xml()
.
By default, the latest export format is used, which means older hwloc
releases (e.g. v1.x) will not be able to import it. Exporting to v1.x
specific XML format is possible using flag
XMLExportFlags::V1
but it may miss some details about the topology.
Also, note that this option will be removed from the (upcoming at the
time of writing) hwloc v3.0 release.
If there is any chance that the exported file may ever be imported back by a process using hwloc 1.x, one should consider detecting it at runtime and using the corresponding export format.
Only printable characters may be exported to XML string attributes. Any other character, especially any non-ASCII character, will be silently dropped.
source§impl Topology
impl Topology
§Topology building
sourcepub fn new() -> Result<Self, RawHwlocError>
pub fn new() -> Result<Self, RawHwlocError>
Creates a new Topology.
If no customization of the build process is needed, this method is the main entry point to this crate. A topology is returned, which contains the logical representation of the physical hardware.
§Examples
let topology = Topology::new()?;
sourcepub fn builder() -> TopologyBuilder
pub fn builder() -> TopologyBuilder
Prepare to create a Topology with custom configuration
§Examples
let flags = BuildFlags::INCLUDE_DISALLOWED;
let topology = Topology::builder().with_flags(flags)?.build()?;
assert_eq!(topology.build_flags(), flags);
sourcepub fn is_abi_compatible(&self) -> bool
pub fn is_abi_compatible(&self) -> bool
Check that this topology is compatible with the current hwloc library
This is useful when using the same topology structure (in memory) in different libraries that may use different hwloc installations (for instance if one library embeds a specific version of hwloc, while another library uses a default system-wide hwloc installation).
If all libraries/programs use the same hwloc installation, this function
always returns true
.
§Examples
assert!(Topology::new()?.is_abi_compatible());
sourcepub fn build_flags(&self) -> BuildFlags
pub fn build_flags(&self) -> BuildFlags
Flags that were used to build this topology
§Examples
assert_eq!(Topology::new()?.build_flags(), BuildFlags::empty());
sourcepub fn is_this_system(&self) -> bool
pub fn is_this_system(&self) -> bool
Was the topology built using the system running this program?
It may not have been if, for instance, it was built using another file-system root or loaded from a synthetic or XML textual description.
§Examples
assert!(Topology::new()?.is_this_system());
sourcepub fn feature_support(&self) -> &FeatureSupport
pub fn feature_support(&self) -> &FeatureSupport
Supported hwloc features with this topology on this machine
This is the information that one gets via the hwloc-info --support
CLI.
The reported features are what the current topology supports on the
current machine. If the topology was exported to XML from another
machine and later imported here, support still describes what is
supported for this imported topology after import. By default, binding
will be reported as unsupported in this case (see
BuildFlags::ASSUME_THIS_SYSTEM
).
On hwloc 2.3+, BuildFlags::IMPORT_SUPPORT
may be used during topology building to
report the supported features of the original remote machine instead. If
it was successfully imported, MiscSupport::imported()
will be set.
§Examples
println!("{:?}", topology.feature_support());
sourcepub fn supports<Group>(
&self,
get_group: fn(_: &FeatureSupport) -> Option<&Group>,
check_feature: fn(_: &Group) -> bool
) -> bool
pub fn supports<Group>( &self, get_group: fn(_: &FeatureSupport) -> Option<&Group>, check_feature: fn(_: &Group) -> bool ) -> bool
Quickly check a support flag
§Examples
let topology = Topology::new()?;
assert!(topology.supports(
FeatureSupport::discovery,
DiscoverySupport::pu_count
));
sourcepub fn type_filter(&self, ty: ObjectType) -> Result<TypeFilter, RawHwlocError>
pub fn type_filter(&self, ty: ObjectType) -> Result<TypeFilter, RawHwlocError>
Filtering that was applied for the given object type
§Examples
// PUs, NUMANodes and Machine are always kept
let always_there = [ObjectType::PU,
ObjectType::NUMANode,
ObjectType::Machine];
for ty in always_there {
assert_eq!(topology.type_filter(ty)?, TypeFilter::KeepAll);
}
// Groups are only kept if they bring extra structure
assert_eq!(
topology.type_filter(ObjectType::Group)?,
TypeFilter::KeepStructure
);
source§impl Topology
impl Topology
§Distributing work items over a topology
sourcepub fn distribute_items(
&self,
roots: &[&TopologyObject],
num_items: usize,
max_depth: NormalDepth,
flags: DistributeFlags
) -> Result<Vec<CpuSet>, DistributeError>
pub fn distribute_items( &self, roots: &[&TopologyObject], num_items: usize, max_depth: NormalDepth, flags: DistributeFlags ) -> Result<Vec<CpuSet>, DistributeError>
Distribute num_items
work items over the topology under roots
Given a number of work items to be processed (which can be, for example, a set of threads to be spawned), this function will assign a cpuset to each of them according to a recursive linear distribution algorithm. Such an algorithm spreads work evenly across CPUs and ensures that work-items with neighboring indices in the output array are processed by neighbouring locations in the topology, which have a high chance of sharing resources like fast CPU caches.
The set of CPUs over which work items are distributed is designated by a
set of root TopologyObject
s with associated CPUs. The root objects
must be part of this Topology
, or the ForeignRoot
error will be
returned. Their CPU sets must also be disjoint, or the
OverlappingRoots
error will be returned. You can distribute items
across all CPUs in the topology by setting roots
to
&[topology.root_object()]
.
Since the purpose of roots
is to designate which CPUs items should be
allocated to, root objects should normally have a CPU set. If that is
not the case (e.g. if some roots designate NUMA nodes or I/O objects
like storage or GPUs), the algorithm will walk up affected roots’
ancestor chains to locate the first ancestor with CPUs in the topology,
which represents the CPUs closest to the object of interest. If none of
the CPUs of that ancestor is available for binding, that root will be
ignored, unless this is true of all roots in which case the
EmptyRoots
error is returned.
If there is no depth limit, which can be achieved by setting max_depth
to NormalDepth::MAX
, the distribution will be done down to the
granularity of individual CPUs, i.e. if there are more work items that
CPUs, each work item will be assigned one CPU. By setting the
max_depth
parameter to a lower limit, you can distribute work at a
coarser granularity, e.g. across L3 caches, giving the OS some leeway to
move tasks across CPUs sharing that cache.
By default, output cpusets follow the logical topology children order.
By setting flags
to DistributeFlags::REVERSE
, you can ask for them
to be provided in reverse order instead (from last child to first child).
§Errors
EmptyRoots
if there are no CPUs to distribute work to (the union of all root cpusets is empty).ForeignRoot
if some of the specified roots do not belong to this topology.OverlappingRoots
if some of the roots have overlapping CPU sets.
source§impl Topology
impl Topology
§CPU and node sets of entire topologies
sourcepub fn cpuset(&self) -> BitmapRef<'_, CpuSet>
pub fn cpuset(&self) -> BitmapRef<'_, CpuSet>
Topology CPU set
This is equivalent to calling TopologyObject::cpuset()
on
the topology’s root object.
§Example
println!("Visible CPUs in this topology: {}", topology.cpuset());
sourcepub fn complete_cpuset(&self) -> BitmapRef<'_, CpuSet>
pub fn complete_cpuset(&self) -> BitmapRef<'_, CpuSet>
Complete CPU set
This is equivalent to calling TopologyObject::complete_cpuset()
on
the topology’s root object.
§Example
println!(
"Overall CPUs in this topology: {}",
topology.complete_cpuset()
);
sourcepub fn allowed_cpuset(&self) -> BitmapRef<'_, CpuSet>
pub fn allowed_cpuset(&self) -> BitmapRef<'_, CpuSet>
Allowed CPU set
If BuildFlags::INCLUDE_DISALLOWED
was not set, this is identical to
Topology::cpuset()
: all visible PUs are allowed.
Otherwise, you can check whether a particular cpuset contains allowed
PUs by calling cpuset.intersects(topology.allowed_cpuset())
, and if so
you can get the set of allowed PUs with
cpuset & topology.allowed_cpuset()
.
§Example
println!(
"Allowed CPUs in this topology: {}",
topology.allowed_cpuset()
);
sourcepub fn nodeset(&self) -> BitmapRef<'_, NodeSet>
pub fn nodeset(&self) -> BitmapRef<'_, NodeSet>
Topology node set
This is equivalent to calling TopologyObject::nodeset()
on
the topology’s root object.
§Example
println!("Visible NUMA nodes in this topology: {}", topology.nodeset());
sourcepub fn complete_nodeset(&self) -> BitmapRef<'_, NodeSet>
pub fn complete_nodeset(&self) -> BitmapRef<'_, NodeSet>
Complete node set
This is equivalent to calling TopologyObject::complete_nodeset()
on
the topology’s root object.
§Example
println!(
"Overall NUMA nodes in this topology: {}",
topology.complete_nodeset()
);
sourcepub fn allowed_nodeset(&self) -> BitmapRef<'_, NodeSet>
pub fn allowed_nodeset(&self) -> BitmapRef<'_, NodeSet>
Allowed node set
If BuildFlags::INCLUDE_DISALLOWED
was not set, this is identical to
Topology::nodeset()
: all visible NUMA nodes are allowed.
Otherwise, you can check whether a particular nodeset contains allowed
NUMA nodes by calling nodeset.intersects(topology.allowed_nodeset())
,
and if so you can get the set of allowed NUMA nodes with
nodeset & topology.allowed_nodeset()
.
§Example
println!(
"Allowed NUMA nodes in this topology: {}",
topology.allowed_nodeset()
);