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:

Implementations§

source§

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.

source

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 Cores or Packages or even random sets of individual PUs. 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, specifically
  • BadFlags if the number of specified binding target flags is not exactly one
  • BadObject(ThisProgram) if it is not possible to bind the current process/thread to CPUs, generally speaking
source

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
source

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, specifically
  • BadFlags if flag THREAD was specified on an operating system other than Linux, or if any other binding target flag was specified
  • BadObject(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.

source

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
§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

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, specifically
  • BadFlags if a binding target flag was specified
  • BadObject(Thread) if it is not possible to bind the target thread to CPUs, generally speaking
source

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
source

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
source

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
§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

§CPU cache statistics

source

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

§Finding objects inside a CPU set

source

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().

source

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
source

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
where DepthLike: TryInto<Depth>, <DepthLike as TryInto<Depth>>::Error: Debug,

Enumerate objects included in the given cpuset set at a certain depth

Accepted operand types are as follows:

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.

source

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.

source

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

§Finding objects covering at least a CPU set

source

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.

source

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>.

source

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
where DepthLike: TryInto<Depth>, <DepthLike as TryInto<Depth>>::Error: Debug,

Enumerate objects covering the given cpuset set at a certain depth

Accepted operand types are as follows:

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

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

§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().

source

pub fn num_cpu_kinds(&self) -> Result<NonZeroUsize, NoData>

Available on crate feature hwloc-2_4_0 only.

Number of different kinds of CPU cores in the topology

§Errors
  • NoData if no information about CPU kinds was found
source

pub fn cpu_kinds( &self ) -> Result<impl DoubleEndedIterator<Item = CpuKind<'_>> + Clone + ExactSizeIterator + FusedIterator, NoData>

Available on crate feature 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
source

pub fn cpu_kind_from_set( &self, set: impl Deref<Target = CpuSet> ) -> Result<CpuKind<'_>, FromSetError>

Available on crate feature 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 if set is only partially included in some kind (i.e. some CPUs in the set belong to a kind, others to other kind(s))
  • NotIncluded if set 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

§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.

source

pub fn bind_tid_cpu( &self, tid: pid_t, set: impl Deref<Target = CpuSet> ) -> Result<(), RawHwlocError>

Available on Linux only.

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.

source

pub fn tid_cpu_binding(&self, tid: pid_t) -> Result<CpuSet, RawHwlocError>

Available on Linux only.

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.

source

pub fn tid_last_cpu_location(&self, tid: pid_t) -> Result<CpuSet, RawHwlocError>

Available on Linux only.

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.

source

pub fn read_path_as_cpumask( &self, path: impl AsRef<Path> ) -> Result<CpuSet, HybridError<PathError>>

Available on Linux only.

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
§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

§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.

source

pub fn num_processor_groups(&self) -> Result<NonZeroUsize, RawHwlocError>

Available on crate feature 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).

source

pub 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.

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

§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.

source

pub fn memory_attribute_named( &self, name: &str ) -> Result<Option<MemoryAttribute<'_>>, NulError>

Available on crate feature 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
source

pub 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.

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
source§

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.

source

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
source

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 failed
  • BadFlags if binding target flags were specified
  • BadSet if the system can’t bind memory to that CPU/node set
  • Unsupported if the system cannot allocate bound memory with the requested policy
source

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 failed
  • BadFlags if the number of specified binding target flags is not exactly one
  • BadSet if the system can’t bind memory to that CPU/node set
  • Unsupported if the system can neither allocate bound memory nor rebind the current thread/process with the requested policy
source

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 one
  • BadSet if the system can’t bind memory to that CPU/node set
  • Unsupported if the system cannot bind the current thread/process with the requested policy
source

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 flags STRICT and MIGRATE was specified, or if the number of specified binding target flags is not exactly one
  • Unsupported if the system cannot unbind the current thread/process
source

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
source

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 flag THREAD was specified, or if the number of specified binding target flags is not exactly one
  • BadSet if the system can’t bind memory to that CPU/node set
  • Unsupported 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.

source

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 flags MIGRATE, STRICT and THREAD was specified, or if the number of specified binding target flags is not exactly one
  • Unsupported 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.

source

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
§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

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 specified
  • BadSet if the system can’t bind memory to that CPU/node set
  • BadTarget if target is a zero-sized object
  • Unsupported if the system cannot bind the specified memory area with the requested policy
source

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 flags MIGRATE and STRICT was specified, or if a binding target flag was specified.
  • BadTarget if target is a zero-sized object
  • Unsupported if the system cannot unbind the specified memory area
source

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 flags MIGRATE and NO_CPU_BINDING was specified, or if a binding target flag was specified.
  • BadTarget if target is a zero-sized object
  • MixedResults if flag STRICT was specified and memory binding is inhomogeneous across target memory pages
  • Unsupported if the system cannot query the specified memory area’s binding
source

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 flags MIGRATE and NO_CPU_BINDING was specified, or if a binding target flag was specified.
  • BadTarget if target is a zero-sized object
  • MixedResults if flag STRICT was specified and memory binding is inhomogeneous across target memory pages
  • Unsupported if the system cannot query the specified memory area’s location
source§

impl Topology

§Retrieve distances between objects

source

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.

source

pub fn distances_at_depth<DepthLike>( &self, kind: Option<DistancesKind>, depth: DepthLike ) -> Result<Vec<Distances<'_>>, RawHwlocError>
where DepthLike: TryInto<Depth>, <DepthLike as TryInto<Depth>>::Error: Debug,

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.

source

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.

source

pub fn distances_with_name( &self, name: &str ) -> Result<Vec<Distances<'_>>, HybridError<NulError>>

Available on crate feature 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
source§

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.

source

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
);
source

pub fn memory_parents_depth(&self) -> Result<NormalDepth, TypeToDepthError>

Depth of normal parents where memory objects are attached

§Errors
§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);
}
source

pub fn depth_for_type( &self, object_type: ObjectType ) -> Result<Depth, TypeToDepthError>

Depth for the given ObjectType

§Errors
§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());
source

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());
source

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());
source

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
§Examples
let l1d_depth = topology.depth_for_cache(1, Some(CacheType::Data));
assert!(l1d_depth.is_ok());
source

pub fn type_at_depth<DepthLike>(&self, depth: DepthLike) -> Option<ObjectType>
where DepthLike: TryInto<Depth>, <DepthLike as TryInto<Depth>>::Error: Debug,

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));
source

pub fn num_objects_at_depth<DepthLike>(&self, depth: DepthLike) -> usize
where DepthLike: TryInto<Depth>, <DepthLike as TryInto<Depth>>::Error: Debug,

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);
source

pub fn objects_at_depth<DepthLike>( &self, depth: DepthLike ) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + ExactSizeIterator + FusedIterator
where DepthLike: TryInto<Depth>, <DepthLike as TryInto<Depth>>::Error: Debug,

TopologyObjects 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);
}
source

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:#}");
source

pub fn objects_with_type( &self, object_type: ObjectType ) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + ExactSizeIterator + FusedIterator

TopologyObjects 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

§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

source

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

source

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

source

pub fn virtual_objects( &self ) -> impl FusedIterator<Item = &TopologyObject> + Clone

Full list of virtual objects in the topology, ordered by type

source

pub fn memory_objects( &self ) -> impl FusedIterator<Item = &TopologyObject> + Clone

Full list of memory objects in the topology, ordered by type

source

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

§Finding I/O objects

source

pub fn pci_devices( &self ) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + ExactSizeIterator + FusedIterator

Enumerate PCI devices in the system

source

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

source

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
source

pub fn os_devices( &self ) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + ExactSizeIterator + FusedIterator

Enumerate OS devices in the system

source

pub fn bridges( &self ) -> impl DoubleEndedIterator<Item = &TopologyObject> + Clone + ExactSizeIterator + FusedIterator

Enumerate bridges in the system

source§

impl Topology

§Finding other objects

source

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().

source

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.

source

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().

source

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.

source

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
source

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.
source

pub 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.

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
source§

impl Topology

§Modifying a loaded Topology

source

pub fn edit<R>( &mut self, edit: impl UnwindSafe + FnOnce(&mut TopologyEditor<'_>) -> R ) -> R

Available on crate feature 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

§Exporting Topologies to Synthetic

source

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

§Exporting Topologies to XML

source

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
source

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

§Topology building

source

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()?;
source

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);
source

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());
source

pub fn build_flags(&self) -> BuildFlags

Flags that were used to build this topology

§Examples
assert_eq!(Topology::new()?.build_flags(), BuildFlags::empty());
source

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());
source

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());
source

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
));
source

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

§Distributing work items over a topology

source

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 TopologyObjects 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

§CPU and node sets of entire topologies

source

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());
source

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()
);
source

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()
);
source

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());
source

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()
);
source

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()
);

Trait Implementations§

source§

impl Clone for Topology

source§

fn clone(&self) -> Self

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Topology

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Drop for Topology

source§

fn drop(&mut self)

Executes the destructor for this type. Read more
source§

impl PartialEq for Topology

source§

fn eq(&self, other: &Self) -> bool

This method tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
source§

impl Pointer for Topology

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter.
source§

impl Send for Topology

source§

impl Sync for Topology

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V