37namespace alpaka::onHost::internal::hwloc
40 constexpr uint32_t allDomains = std::numeric_limits<uint32_t>::max();
51 static TopologyCache& instance()
53 static TopologyCache topology;
57 hwloc_topology_t
get() const noexcept
65 if(hwloc_topology_init(&m_topology) != 0)
67 throw std::runtime_error(
"hwloc_topology_init failed");
69 if(hwloc_topology_load(m_topology) != 0)
71 hwloc_topology_destroy(m_topology);
72 throw std::runtime_error(
"hwloc_topology_load failed");
78 if(m_topology !=
nullptr)
80 hwloc_topology_destroy(m_topology);
84 TopologyCache(TopologyCache
const&) =
delete;
85 TopologyCache& operator=(TopologyCache
const&) =
delete;
86 TopologyCache(TopologyCache&&) =
delete;
87 TopologyCache& operator=(TopologyCache&&) =
delete;
90 hwloc_topology_t m_topology{};
93 [[noreturn]]
inline void throwErrno(
char const* what)
95 throw std::runtime_error(std::string(what) +
": " + std::strerror(errno));
99 inline hwloc_topology_t getTopology()
101 return TopologyCache::instance().get();
110 inline bool hasNonEmptyCpuSet(hwloc_obj_t obj)
112 return obj !=
nullptr && obj->cpuset !=
nullptr && !hwloc_bitmap_iszero(obj->cpuset);
121 inline bool isObjectInSubtree(hwloc_obj_t obj, hwloc_obj_t subtreeRoot)
123 for(hwloc_obj_t current = obj; current !=
nullptr; current = current->parent)
125 if(current == subtreeRoot)
136 inline std::vector<hwloc_obj_t> getGroupCpuDomains()
138 std::vector<hwloc_obj_t> domains;
139 hwloc_topology_t
const topology = getTopology();
141 hwloc_obj_t group =
nullptr;
142 while((group = hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_GROUP, group)) !=
nullptr)
145 if(!hasNonEmptyCpuSet(group))
150 bool containsNumaNode =
false;
151 hwloc_obj_t node =
nullptr;
152 while((node = hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_NUMANODE, node)) !=
nullptr)
154 if(isObjectInSubtree(node, group))
156 containsNumaNode =
true;
162 domains.push_back(group);
172 inline std::vector<hwloc_obj_t> getNumaCpuDomains()
174 std::vector<hwloc_obj_t> domains;
175 hwloc_topology_t
const topology = getTopology();
177 hwloc_obj_t node =
nullptr;
178 while((node = hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_NUMANODE, node)) !=
nullptr)
180 if(hasNonEmptyCpuSet(node))
181 domains.push_back(node);
191 inline std::vector<hwloc_obj_t> getCpuDomains()
193 std::vector<hwloc_obj_t> domains = getGroupCpuDomains();
197 return getNumaCpuDomains();
201 inline hwloc_obj_t getCpuDomainObj(uint32_t domainIdx)
203 std::vector<hwloc_obj_t>
const domains = getCpuDomains();
204 if(domainIdx >= domains.size())
206 throw std::out_of_range(
"CPU domain index out of range: " + std::to_string(domainIdx));
208 return domains[domainIdx];
216 inline std::vector<hwloc_obj_t> getMemoryNodes(hwloc_obj_t domainObj)
218 std::vector<hwloc_obj_t> nodes;
219 hwloc_topology_t
const topology = getTopology();
221 if(domainObj ==
nullptr)
224 if(domainObj->type == HWLOC_OBJ_NUMANODE)
226 nodes.push_back(domainObj);
230 hwloc_obj_t node =
nullptr;
231 while((node = hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_NUMANODE, node)) !=
nullptr)
233 if(isObjectInSubtree(node, domainObj))
234 nodes.push_back(node);
243 inline uint32_t getNumCpuDomains()
246 std::vector<hwloc_obj_t>
const domains = getCpuDomains();
247 return static_cast<uint32_t
>(domains.size());
261 inline std::optional<size_t> parseNodeMemInfoValueBytes(
unsigned osNodeIndex, std::string_view key)
263 std::ifstream in(
"/sys/devices/system/node/node" + std::to_string(osNodeIndex) +
"/meminfo");
270 while(std::getline(in, line))
272 if(line.find(std::string(key)) == std::string::npos)
279 std::istringstream iss(line);
280 std::string nodeWord;
281 unsigned nodeNumber = 0;
285 if(iss >> nodeWord >> nodeNumber >> field >> valueKB >> unit)
287 if(field == key && unit ==
"kB")
289 return valueKB * 1024ULL;
301 inline void setThreadAffinity(uint32_t cpuDomainIdx)
304 hwloc_cpuset_t cpuset =
nullptr;
306 if(cpuDomainIdx == allDomains)
308 hwloc_const_cpuset_t
const fullSet = hwloc_topology_get_complete_cpuset(getTopology());
309 if(fullSet ==
nullptr)
311 throw std::runtime_error(
"Topology has no complete cpuset");
314 cpuset = hwloc_bitmap_dup(fullSet);
318 hwloc_obj_t
const domain = getCpuDomainObj(cpuDomainIdx);
319 if(domain->cpuset ==
nullptr || hwloc_bitmap_iszero(domain->cpuset))
321 throw std::runtime_error(
"CPU domain has no cpuset");
324 cpuset = hwloc_bitmap_dup(domain->cpuset);
327 if(cpuset ==
nullptr)
329 throw std::bad_alloc();
332 int const rc = hwloc_set_cpubind(getTopology(), cpuset, HWLOC_CPUBIND_THREAD | HWLOC_CPUBIND_STRICT);
334 hwloc_bitmap_free(cpuset);
338 throwErrno(
"hwloc_set_cpubind failed");
341 alpaka::unused(cpuDomainIdx);
349 inline void pinPointerToNumaNode(T*
const ptr,
size_t bytes, hwloc_obj_t
const node)
351 if(ptr ==
nullptr || bytes == 0u)
355 throw std::runtime_error(
"NUMA node is null");
357 if(node->type != HWLOC_OBJ_NUMANODE)
358 throw std::runtime_error(
"Memory binding target is not a NUMA node");
360 if(node->nodeset ==
nullptr)
362 throw std::runtime_error(
"NUMA node has no nodeset");
365 hwloc_nodeset_t nodeset = hwloc_bitmap_dup(node->nodeset);
366 if(nodeset ==
nullptr)
368 throw std::bad_alloc();
371 int const rc = hwloc_set_area_membind(
377 HWLOC_MEMBIND_BYNODESET | HWLOC_MEMBIND_STRICT);
379 hwloc_bitmap_free(nodeset);
383# ifdef ALPAKA_HOST_MEM_PINNING_CAN_FAIL
385 bool const operationNotSupported = errno == EPERM;
387 bool const functionNotImplemented = errno == ENOSYS;
389 bool const operationNotAllowed = errno == EXDEV;
390 if(operationNotSupported || functionNotImplemented || operationNotAllowed)
395 throwErrno(
"hwloc_set_area_membind failed");
410 inline void pinPointer(T*
const ptr,
size_t bytes, uint32_t cpuDomainIdx)
413 if(cpuDomainIdx == allDomains)
416 if(ptr ==
nullptr || bytes == 0u)
419 std::vector<hwloc_obj_t>
const nodes = getMemoryNodes(getCpuDomainObj(cpuDomainIdx));
422 throw std::runtime_error(
"CPU domain has no associated NUMA memory node");
429 pinPointerToNumaNode(ptr, bytes, nodes.front());
431 alpaka::unused(ptr, bytes, cpuDomainIdx);
442 inline uint32_t getNumCores(uint32_t cpuDomainIdx)
445 if(cpuDomainIdx == allDomains)
446 return std::thread::hardware_concurrency();
448 hwloc_obj_t
const domain = getCpuDomainObj(cpuDomainIdx);
449 if(domain->cpuset ==
nullptr || hwloc_bitmap_iszero(domain->cpuset))
451 throw std::runtime_error(
"CPU domain has no cpuset");
454 int const numPUs = hwloc_bitmap_weight(domain->cpuset);
457 throw std::runtime_error(
"hwloc_bitmap_weight failed");
460 return static_cast<uint32_t
>(numPUs);
462 alpaka::unused(cpuDomainIdx);
463 return std::thread::hardware_concurrency();
474 inline size_t getMemCapacityBytes(uint32_t cpuDomainIdx)
477 if(cpuDomainIdx == allDomains)
481 for(hwloc_obj_t
const node : getMemoryNodes(getCpuDomainObj(cpuDomainIdx)))
483 if(node->attr ==
nullptr)
485 throw std::runtime_error(
"NUMA node has no attributes");
487 bytes +=
static_cast<size_t>(node->attr->numanode.local_memory);
492 alpaka::unused(cpuDomainIdx);
504 inline size_t getFreeGlobalMemBytes(uint32_t cpuDomainIdx)
507 if(cpuDomainIdx == allDomains)
511 for(hwloc_obj_t
const node : getMemoryNodes(getCpuDomainObj(cpuDomainIdx)))
513 auto const freeBytes = parseNodeMemInfoValueBytes(node->os_index,
"MemFree:");
514 if(!freeBytes.has_value())
516 throw std::runtime_error(
517 "Could not read per-node MemFree from /sys/devices/system/node/node"
518 + std::to_string(node->os_index) +
"/meminfo");
525 alpaka::unused(cpuDomainIdx);
auto getFreeGlobalMemBytes() -> std::size_t
auto getGlobalMemCapacityBytes() -> std::size_t
auto * toVoidPtr(T inPtr)
Cast a pointer that may or may not point to volatile memory to a (void*) or (void const*).
constexpr decltype(auto) get(concepts::SpecializationOf< Dict > auto &t) noexcept