Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:01:16

0001 /*
0002  * Copyright © 2019-2023 Inria.  All rights reserved.
0003  * See COPYING in top-level directory.
0004  */
0005 
0006 /** \file
0007  * \brief Memory node attributes.
0008  */
0009 
0010 #ifndef HWLOC_MEMATTR_H
0011 #define HWLOC_MEMATTR_H
0012 
0013 #include "hwloc.h"
0014 
0015 #ifdef __cplusplus
0016 extern "C" {
0017 #elif 0
0018 }
0019 #endif
0020 
0021 /** \defgroup hwlocality_memattrs Comparing memory node attributes for finding where to allocate on
0022  *
0023  * Platforms with heterogeneous memory require ways to decide whether
0024  * a buffer should be allocated on "fast" memory (such as HBM),
0025  * "normal" memory (DDR) or even "slow" but large-capacity memory
0026  * (non-volatile memory).
0027  * These memory nodes are called "Targets" while the CPU accessing them
0028  * is called the "Initiator". Access performance depends on their
0029  * locality (NUMA platforms) as well as the intrinsic performance
0030  * of the targets (heterogeneous platforms).
0031  *
0032  * The following attributes describe the performance of memory accesses
0033  * from an Initiator to a memory Target, for instance their latency
0034  * or bandwidth.
0035  * Initiators performing these memory accesses are usually some PUs or Cores
0036  * (described as a CPU set).
0037  * Hence a Core may choose where to allocate a memory buffer by comparing
0038  * the attributes of different target memory nodes nearby.
0039  *
0040  * There are also some attributes that are system-wide.
0041  * Their value does not depend on a specific initiator performing
0042  * an access.
0043  * The memory node Capacity is an example of such attribute without
0044  * initiator.
0045  *
0046  * One way to use this API is to start with a cpuset describing the Cores where
0047  * a program is bound. The best target NUMA node for allocating memory in this
0048  * program on these Cores may be obtained by passing this cpuset as an initiator
0049  * to hwloc_memattr_get_best_target() with the relevant memory attribute.
0050  * For instance, if the code is latency limited, use the Latency attribute.
0051  *
0052  * A more flexible approach consists in getting the list of local NUMA nodes
0053  * by passing this cpuset to hwloc_get_local_numanode_objs().
0054  * Attribute values for these nodes, if any, may then be obtained with
0055  * hwloc_memattr_get_value() and manually compared with the desired criteria.
0056  *
0057  * Memory attributes are also used internally to build Memory Tiers which provide
0058  * an easy way to distinguish NUMA nodes of different kinds, as explained
0059  * in \ref heteromem.
0060  *
0061  * \sa An example is available in doc/examples/memory-attributes.c in the source tree.
0062  *
0063  * \note The API also supports specific objects as initiator,
0064  * but it is currently not used internally by hwloc.
0065  * Users may for instance use it to provide custom performance
0066  * values for host memory accesses performed by GPUs.
0067  *
0068  * \note The interface actually also accepts targets that are not NUMA nodes.
0069  * @{
0070  */
0071 
0072 /** \brief Memory node attributes. */
0073 enum hwloc_memattr_id_e {
0074   /** \brief
0075    * The \"Capacity\" is returned in bytes (local_memory attribute in objects).
0076    *
0077    * Best capacity nodes are nodes with <b>higher capacity</b>.
0078    *
0079    * No initiator is involved when looking at this attribute.
0080    * The corresponding attribute flags are ::HWLOC_MEMATTR_FLAG_HIGHER_FIRST.
0081    * \hideinitializer
0082    */
0083   HWLOC_MEMATTR_ID_CAPACITY = 0,
0084 
0085   /** \brief
0086    * The \"Locality\" is returned as the number of PUs in that locality
0087    * (e.g. the weight of its cpuset).
0088    *
0089    * Best locality nodes are nodes with <b>smaller locality</b>
0090    * (nodes that are local to very few PUs).
0091    * Poor locality nodes are nodes with larger locality
0092    * (nodes that are local to the entire machine).
0093    *
0094    * No initiator is involved when looking at this attribute.
0095    * The corresponding attribute flags are ::HWLOC_MEMATTR_FLAG_HIGHER_FIRST.
0096    * \hideinitializer
0097    */
0098   HWLOC_MEMATTR_ID_LOCALITY = 1,
0099 
0100   /** \brief
0101    * The \"Bandwidth\" is returned in MiB/s, as seen from the given initiator location.
0102    *
0103    * Best bandwidth nodes are nodes with <b>higher bandwidth</b>.
0104    *
0105    * The corresponding attribute flags are ::HWLOC_MEMATTR_FLAG_HIGHER_FIRST
0106    * and ::HWLOC_MEMATTR_FLAG_NEED_INITIATOR.
0107    *
0108    * This is the average bandwidth for read and write accesses. If the platform
0109    * provides individual read and write bandwidths but no explicit average value,
0110    * hwloc computes and returns the average.
0111    * \hideinitializer
0112    */
0113   HWLOC_MEMATTR_ID_BANDWIDTH = 2,
0114 
0115   /** \brief
0116    * The \"ReadBandwidth\" is returned in MiB/s, as seen from the given initiator location.
0117    *
0118    * Best bandwidth nodes are nodes with <b>higher bandwidth</b>.
0119    *
0120    * The corresponding attribute flags are ::HWLOC_MEMATTR_FLAG_HIGHER_FIRST
0121    * and ::HWLOC_MEMATTR_FLAG_NEED_INITIATOR.
0122    * \hideinitializer
0123    */
0124   HWLOC_MEMATTR_ID_READ_BANDWIDTH = 4,
0125 
0126   /** \brief
0127    * The \"WriteBandwidth\" is returned in MiB/s, as seen from the given initiator location.
0128    *
0129    * Best bandwidth nodes are nodes with <b>higher bandwidth</b>.
0130    *
0131    * The corresponding attribute flags are ::HWLOC_MEMATTR_FLAG_HIGHER_FIRST
0132    * and ::HWLOC_MEMATTR_FLAG_NEED_INITIATOR.
0133    * \hideinitializer
0134    */
0135   HWLOC_MEMATTR_ID_WRITE_BANDWIDTH = 5,
0136 
0137   /** \brief
0138    * The \"Latency\" is returned as nanoseconds, as seen from the given initiator location.
0139    *
0140    * Best latency nodes are nodes with <b>smaller latency</b>.
0141    *
0142    * The corresponding attribute flags are ::HWLOC_MEMATTR_FLAG_LOWER_FIRST
0143    * and ::HWLOC_MEMATTR_FLAG_NEED_INITIATOR.
0144    *
0145    * This is the average latency for read and write accesses. If the platform
0146    * provides individual read and write latencies but no explicit average value,
0147    * hwloc computes and returns the average.
0148    * \hideinitializer
0149    */
0150   HWLOC_MEMATTR_ID_LATENCY = 3,
0151 
0152   /** \brief
0153    * The \"ReadLatency\" is returned as nanoseconds, as seen from the given initiator location.
0154    *
0155    * Best latency nodes are nodes with <b>smaller latency</b>.
0156    *
0157    * The corresponding attribute flags are ::HWLOC_MEMATTR_FLAG_LOWER_FIRST
0158    * and ::HWLOC_MEMATTR_FLAG_NEED_INITIATOR.
0159    * \hideinitializer
0160    */
0161   HWLOC_MEMATTR_ID_READ_LATENCY = 6,
0162 
0163   /** \brief
0164    * The \"WriteLatency\" is returned as nanoseconds, as seen from the given initiator location.
0165    *
0166    * Best latency nodes are nodes with <b>smaller latency</b>.
0167    *
0168    * The corresponding attribute flags are ::HWLOC_MEMATTR_FLAG_LOWER_FIRST
0169    * and ::HWLOC_MEMATTR_FLAG_NEED_INITIATOR.
0170    * \hideinitializer
0171    */
0172   HWLOC_MEMATTR_ID_WRITE_LATENCY = 7,
0173 
0174   /* TODO persistence? */
0175 
0176   HWLOC_MEMATTR_ID_MAX /**< \private Sentinel value */
0177 };
0178 
0179 /** \brief A memory attribute identifier.
0180  * May be either one of ::hwloc_memattr_id_e or a new id returned by hwloc_memattr_register().
0181  */
0182 typedef unsigned hwloc_memattr_id_t;
0183 
0184 /** \brief Return the identifier of the memory attribute with the given name.
0185  *
0186  * \return 0 on success.
0187  * \return -1 with errno set to \c EINVAL if no such attribute exists.
0188  */
0189 HWLOC_DECLSPEC int
0190 hwloc_memattr_get_by_name(hwloc_topology_t topology,
0191                           const char *name,
0192                           hwloc_memattr_id_t *id);
0193 
0194 
0195 /** \brief Type of location. */
0196 enum hwloc_location_type_e {
0197   /** \brief Location is given as a cpuset, in the location cpuset union field. \hideinitializer */
0198   HWLOC_LOCATION_TYPE_CPUSET = 1,
0199   /** \brief Location is given as an object, in the location object union field. \hideinitializer */
0200   HWLOC_LOCATION_TYPE_OBJECT = 0
0201 };
0202 
0203 /** \brief Where to measure attributes from. */
0204 struct hwloc_location {
0205   /** \brief Type of location. */
0206   enum hwloc_location_type_e type;
0207   /** \brief Actual location. */
0208   union hwloc_location_u {
0209     /** \brief Location as a cpuset, when the location type is ::HWLOC_LOCATION_TYPE_CPUSET. */
0210     hwloc_cpuset_t cpuset;
0211     /** \brief Location as an object, when the location type is ::HWLOC_LOCATION_TYPE_OBJECT. */
0212     hwloc_obj_t object;
0213   } location;
0214 };
0215 
0216 
0217 /** \brief Flags for selecting target NUMA nodes. */
0218 enum hwloc_local_numanode_flag_e {
0219   /** \brief Select NUMA nodes whose locality is larger than the given cpuset.
0220    * For instance, if a single PU (or its cpuset) is given in \p initiator,
0221    * select all nodes close to the package that contains this PU.
0222    * \hideinitializer
0223    */
0224   HWLOC_LOCAL_NUMANODE_FLAG_LARGER_LOCALITY = (1UL<<0),
0225 
0226   /** \brief Select NUMA nodes whose locality is smaller than the given cpuset.
0227    * For instance, if a package (or its cpuset) is given in \p initiator,
0228    * also select nodes that are attached to only a half of that package.
0229    * \hideinitializer
0230    */
0231   HWLOC_LOCAL_NUMANODE_FLAG_SMALLER_LOCALITY = (1UL<<1),
0232 
0233   /** \brief Select all NUMA nodes in the topology.
0234    * The initiator \p initiator is ignored.
0235    * \hideinitializer
0236    */
0237   HWLOC_LOCAL_NUMANODE_FLAG_ALL = (1UL<<2)
0238 };
0239 
0240 /** \brief Return an array of local NUMA nodes.
0241  *
0242  * By default only select the NUMA nodes whose locality is exactly
0243  * the given \p location. More nodes may be selected if additional flags
0244  * are given as a OR'ed set of ::hwloc_local_numanode_flag_e.
0245  *
0246  * If \p location is given as an explicit object, its CPU set is used
0247  * to find NUMA nodes with the corresponding locality.
0248  * If the object does not have a CPU set (e.g. I/O object), the CPU
0249  * parent (where the I/O object is attached) is used.
0250  *
0251  * On input, \p nr points to the number of nodes that may be stored
0252  * in the \p nodes array.
0253  * On output, \p nr will be changed to the number of stored nodes,
0254  * or the number of nodes that would have been stored if there were
0255  * enough room.
0256  *
0257  * \return 0 on success or -1 on error.
0258  *
0259  * \note Some of these NUMA nodes may not have any memory attribute
0260  * values and hence not be reported as actual targets in other functions.
0261  *
0262  * \note The number of NUMA nodes in the topology (obtained by
0263  * hwloc_bitmap_weight() on the root object nodeset) may be used
0264  * to allocate the \p nodes array.
0265  *
0266  * \note When an object CPU set is given as locality, for instance a Package,
0267  * and when flags contain both ::HWLOC_LOCAL_NUMANODE_FLAG_LARGER_LOCALITY
0268  * and ::HWLOC_LOCAL_NUMANODE_FLAG_SMALLER_LOCALITY,
0269  * the returned array corresponds to the nodeset of that object.
0270  */
0271 HWLOC_DECLSPEC int
0272 hwloc_get_local_numanode_objs(hwloc_topology_t topology,
0273                               struct hwloc_location *location,
0274                               unsigned *nr,
0275                               hwloc_obj_t *nodes,
0276                               unsigned long flags);
0277 
0278 
0279 
0280 /** \brief Return an attribute value for a specific target NUMA node.
0281  *
0282  * If the attribute does not relate to a specific initiator
0283  * (it does not have the flag ::HWLOC_MEMATTR_FLAG_NEED_INITIATOR),
0284  * location \p initiator is ignored and may be \c NULL.
0285  *
0286  * \p flags must be \c 0 for now.
0287  *
0288  * \return 0 on success.
0289  * \return -1 on error, for instance with errno set to \c EINVAL if flags
0290  * are invalid or no such attribute exists.
0291  *
0292  * \note The initiator \p initiator should be of type ::HWLOC_LOCATION_TYPE_CPUSET
0293  * when refering to accesses performed by CPU cores.
0294  * ::HWLOC_LOCATION_TYPE_OBJECT is currently unused internally by hwloc,
0295  * but users may for instance use it to provide custom information about
0296  * host memory accesses performed by GPUs.
0297  */
0298 HWLOC_DECLSPEC int
0299 hwloc_memattr_get_value(hwloc_topology_t topology,
0300                         hwloc_memattr_id_t attribute,
0301                         hwloc_obj_t target_node,
0302                         struct hwloc_location *initiator,
0303                         unsigned long flags,
0304                         hwloc_uint64_t *value);
0305 
0306 /** \brief Return the best target NUMA node for the given attribute and initiator.
0307  *
0308  * If the attribute does not relate to a specific initiator
0309  * (it does not have the flag ::HWLOC_MEMATTR_FLAG_NEED_INITIATOR),
0310  * location \p initiator is ignored and may be \c NULL.
0311  *
0312  * If \p value is non \c NULL, the corresponding value is returned there.
0313  *
0314  * If multiple targets have the same attribute values, only one is
0315  * returned (and there is no way to clarify how that one is chosen).
0316  * Applications that want to detect targets with identical/similar
0317  * values, or that want to look at values for multiple attributes,
0318  * should rather get all values using hwloc_memattr_get_value()
0319  * and manually select the target they consider the best.
0320  *
0321  * \p flags must be \c 0 for now.
0322  *
0323  * \return 0 on success.
0324  * \return -1 with errno set to \c ENOENT if there are no matching targets.
0325  * \return -1 with errno set to \c EINVAL if flags are invalid,
0326  * or no such attribute exists.
0327  *
0328  * \note The initiator \p initiator should be of type ::HWLOC_LOCATION_TYPE_CPUSET
0329  * when refering to accesses performed by CPU cores.
0330  * ::HWLOC_LOCATION_TYPE_OBJECT is currently unused internally by hwloc,
0331  * but users may for instance use it to provide custom information about
0332  * host memory accesses performed by GPUs.
0333  */
0334 HWLOC_DECLSPEC int
0335 hwloc_memattr_get_best_target(hwloc_topology_t topology,
0336                               hwloc_memattr_id_t attribute,
0337                               struct hwloc_location *initiator,
0338                               unsigned long flags,
0339                               hwloc_obj_t *best_target, hwloc_uint64_t *value);
0340 
0341 /** \brief Return the best initiator for the given attribute and target NUMA node.
0342  *
0343  * If \p value is non \c NULL, the corresponding value is returned there.
0344  *
0345  * If multiple initiators have the same attribute values, only one is
0346  * returned (and there is no way to clarify how that one is chosen).
0347  * Applications that want to detect initiators with identical/similar
0348  * values, or that want to look at values for multiple attributes,
0349  * should rather get all values using hwloc_memattr_get_value()
0350  * and manually select the initiator they consider the best.
0351  *
0352  * The returned initiator should not be modified or freed,
0353  * it belongs to the topology.
0354  *
0355  * \p flags must be \c 0 for now.
0356  *
0357  * \return 0 on success.
0358  * \return -1 with errno set to \c ENOENT if there are no matching initiators.
0359  * \return -1 with errno set to \c EINVAL if the attribute does not relate to a specific initiator
0360  * (it does not have the flag ::HWLOC_MEMATTR_FLAG_NEED_INITIATOR).
0361  */
0362 HWLOC_DECLSPEC int
0363 hwloc_memattr_get_best_initiator(hwloc_topology_t topology,
0364                                  hwloc_memattr_id_t attribute,
0365                                  hwloc_obj_t target,
0366                                  unsigned long flags,
0367                                  struct hwloc_location *best_initiator, hwloc_uint64_t *value);
0368 
0369 /** @} */
0370 
0371 
0372 /** \defgroup hwlocality_memattrs_manage Managing memory attributes
0373  * @{
0374  */
0375 
0376 /** \brief Return the name of a memory attribute.
0377  *
0378  * \return 0 on success.
0379  * \return -1 with errno set to \c EINVAL if the attribute does not exist.
0380  */
0381 HWLOC_DECLSPEC int
0382 hwloc_memattr_get_name(hwloc_topology_t topology,
0383                        hwloc_memattr_id_t attribute,
0384                        const char **name);
0385 
0386 /** \brief Return the flags of the given attribute.
0387  *
0388  * Flags are a OR'ed set of ::hwloc_memattr_flag_e.
0389  *
0390  * \return 0 on success.
0391  * \return -1 with errno set to \c EINVAL if the attribute does not exist.
0392  */
0393 HWLOC_DECLSPEC int
0394 hwloc_memattr_get_flags(hwloc_topology_t topology,
0395                         hwloc_memattr_id_t attribute,
0396                         unsigned long *flags);
0397 
0398 /** \brief Memory attribute flags.
0399  * Given to hwloc_memattr_register() and returned by hwloc_memattr_get_flags().
0400  */
0401 enum hwloc_memattr_flag_e {
0402   /** \brief The best nodes for this memory attribute are those with the higher values.
0403    * For instance Bandwidth.
0404    */
0405   HWLOC_MEMATTR_FLAG_HIGHER_FIRST = (1UL<<0),
0406   /** \brief The best nodes for this memory attribute are those with the lower values.
0407    * For instance Latency.
0408    */
0409   HWLOC_MEMATTR_FLAG_LOWER_FIRST = (1UL<<1),
0410   /** \brief The value returned for this memory attribute depends on the given initiator.
0411    * For instance Bandwidth and Latency, but not Capacity.
0412    */
0413   HWLOC_MEMATTR_FLAG_NEED_INITIATOR = (1UL<<2)
0414 };
0415 
0416 /** \brief Register a new memory attribute.
0417  *
0418  * Add a specific memory attribute that is not defined in ::hwloc_memattr_id_e.
0419  * Flags are a OR'ed set of ::hwloc_memattr_flag_e. It must contain at least
0420  * one of ::HWLOC_MEMATTR_FLAG_HIGHER_FIRST or ::HWLOC_MEMATTR_FLAG_LOWER_FIRST.
0421  *
0422  * \return 0 on success.
0423  * \return -1 with errno set to \c EBUSY if another attribute already uses this name.
0424  */
0425 HWLOC_DECLSPEC int
0426 hwloc_memattr_register(hwloc_topology_t topology,
0427                        const char *name,
0428                        unsigned long flags,
0429                        hwloc_memattr_id_t *id);
0430 
0431 /** \brief Set an attribute value for a specific target NUMA node.
0432  *
0433  * If the attribute does not relate to a specific initiator
0434  * (it does not have the flag ::HWLOC_MEMATTR_FLAG_NEED_INITIATOR),
0435  * location \p initiator is ignored and may be \c NULL.
0436  *
0437  * The initiator will be copied into the topology,
0438  * the caller should free anything allocated to store the initiator,
0439  * for instance the cpuset.
0440  *
0441  * \p flags must be \c 0 for now.
0442  *
0443  * \note The initiator \p initiator should be of type ::HWLOC_LOCATION_TYPE_CPUSET
0444  * when referring to accesses performed by CPU cores.
0445  * ::HWLOC_LOCATION_TYPE_OBJECT is currently unused internally by hwloc,
0446  * but users may for instance use it to provide custom information about
0447  * host memory accesses performed by GPUs.
0448  *
0449  * \return 0 on success or -1 on error.
0450  */
0451 HWLOC_DECLSPEC int
0452 hwloc_memattr_set_value(hwloc_topology_t topology,
0453                         hwloc_memattr_id_t attribute,
0454                         hwloc_obj_t target_node,
0455                         struct hwloc_location *initiator,
0456                         unsigned long flags,
0457                         hwloc_uint64_t value);
0458 
0459 /** \brief Return the target NUMA nodes that have some values for a given attribute.
0460  *
0461  * Return targets for the given attribute in the \p targets array
0462  * (for the given initiator if any).
0463  * If \p values is not \c NULL, the corresponding attribute values
0464  * are stored in the array it points to.
0465  *
0466  * On input, \p nr points to the number of targets that may be stored
0467  * in the array \p targets (and \p values).
0468  * On output, \p nr points to the number of targets (and values) that
0469  * were actually found, even if some of them couldn't be stored in the array.
0470  * Targets that couldn't be stored are ignored, but the function still
0471  * returns success (\c 0). The caller may find out by comparing the value pointed
0472  * by \p nr before and after the function call.
0473  *
0474  * The returned targets should not be modified or freed,
0475  * they belong to the topology.
0476  *
0477  * Argument \p initiator is ignored if the attribute does not relate to a specific
0478  * initiator (it does not have the flag ::HWLOC_MEMATTR_FLAG_NEED_INITIATOR).
0479  * Otherwise \p initiator may be non \c NULL to report only targets
0480  * that have a value for that initiator.
0481  *
0482  * \p flags must be \c 0 for now.
0483  *
0484  * \note This function is meant for tools and debugging (listing internal information)
0485  * rather than for application queries. Applications should rather select useful
0486  * NUMA nodes with hwloc_get_local_numanode_objs() and then look at their attribute
0487  * values.
0488  *
0489  * \return 0 on success or -1 on error.
0490  *
0491  * \note The initiator \p initiator should be of type ::HWLOC_LOCATION_TYPE_CPUSET
0492  * when referring to accesses performed by CPU cores.
0493  * ::HWLOC_LOCATION_TYPE_OBJECT is currently unused internally by hwloc,
0494  * but users may for instance use it to provide custom information about
0495  * host memory accesses performed by GPUs.
0496  */
0497 HWLOC_DECLSPEC int
0498 hwloc_memattr_get_targets(hwloc_topology_t topology,
0499                           hwloc_memattr_id_t attribute,
0500                           struct hwloc_location *initiator,
0501                           unsigned long flags,
0502                           unsigned *nr, hwloc_obj_t *targets, hwloc_uint64_t *values);
0503 
0504 /** \brief Return the initiators that have values for a given attribute for a specific target NUMA node.
0505  *
0506  * Return initiators for the given attribute and target node in the
0507  * \p initiators array.
0508  * If \p values is not \c NULL, the corresponding attribute values
0509  * are stored in the array it points to.
0510  *
0511  * On input, \p nr points to the number of initiators that may be stored
0512  * in the array \p initiators (and \p values).
0513  * On output, \p nr points to the number of initiators (and values) that
0514  * were actually found, even if some of them couldn't be stored in the array.
0515  * Initiators that couldn't be stored are ignored, but the function still
0516  * returns success (\c 0). The caller may find out by comparing the value pointed
0517  * by \p nr before and after the function call.
0518  *
0519  * The returned initiators should not be modified or freed,
0520  * they belong to the topology.
0521  *
0522  * \p flags must be \c 0 for now.
0523  *
0524  * If the attribute does not relate to a specific initiator
0525  * (it does not have the flag ::HWLOC_MEMATTR_FLAG_NEED_INITIATOR),
0526  * no initiator is returned.
0527  *
0528  * \return 0 on success or -1 on error.
0529  *
0530  * \note This function is meant for tools and debugging (listing internal information)
0531  * rather than for application queries. Applications should rather select useful
0532  * NUMA nodes with hwloc_get_local_numanode_objs() and then look at their attribute
0533  * values for some relevant initiators.
0534  */
0535 HWLOC_DECLSPEC int
0536 hwloc_memattr_get_initiators(hwloc_topology_t topology,
0537                              hwloc_memattr_id_t attribute,
0538                              hwloc_obj_t target_node,
0539                              unsigned long flags,
0540                              unsigned *nr, struct hwloc_location *initiators, hwloc_uint64_t *values);
0541 /** @} */
0542 
0543 #ifdef __cplusplus
0544 } /* extern "C" */
0545 #endif
0546 
0547 
0548 #endif /* HWLOC_MEMATTR_H */