/home/runner/work/HiCR/HiCR/include/hicr/backends/hwloc/computeResource.hpp Source File

HiCR: /home/runner/work/HiCR/HiCR/include/hicr/backends/hwloc/computeResource.hpp Source File
HiCR
computeResource.hpp
Go to the documentation of this file.
1/*
2 * Copyright 2025 Huawei Technologies Co., Ltd.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
24#pragma once
25
26#include <unordered_set>
27#include <hwloc.h>
29#include <hicr/core/definitions.hpp>
32
33namespace HiCR::backend::hwloc
34{
35
41{
42 public:
43
47 using hwlocObjectIndex_t = unsigned int;
48
52 using logicalProcessorId_t = unsigned int;
53
57 using physicalProcessorId_t = unsigned int;
58
62 using numaAffinity_t = unsigned int;
63
69 ComputeResource(hwloc_topology_t topology, const hwlocObjectIndex_t hwlocObjectIndex)
70 : HiCR::ComputeResource(),
71 _hwlocObjectIndex(hwlocObjectIndex),
72 _logicalProcessorId(detectLogicalProcessorId(topology, hwlocObjectIndex)),
73 _physicalProcessorId(detectPhysicalProcessorId(topology, hwlocObjectIndex)),
74 _numaAffinity(detectCoreNUMAffinity(topology, hwlocObjectIndex)),
75 _caches(detectCpuCaches(topology, hwlocObjectIndex))
76 {
77 _type = "Processing Unit";
78 };
79
88 ComputeResource(const hwlocObjectIndex_t hwlocObjectIndex,
89 const logicalProcessorId_t logicalProcessorId,
90 const physicalProcessorId_t physicalProcessorId,
91 const numaAffinity_t numaAffinity,
92 std::unordered_set<std::shared_ptr<backend::hwloc::Cache>> caches)
93 : HiCR::ComputeResource(),
94 _hwlocObjectIndex(hwlocObjectIndex),
95 _logicalProcessorId(logicalProcessorId),
96 _physicalProcessorId(physicalProcessorId),
97 _numaAffinity(numaAffinity),
98 _caches(caches)
99 {
100 _type = "Processing Unit";
101 };
102
103 ~ComputeResource() override = default;
104
108 ComputeResource() { _type = "Processing Unit"; };
109
115 __INLINE__ logicalProcessorId_t getProcessorId() const { return _logicalProcessorId; }
116
123 __INLINE__ physicalProcessorId_t getPhysicalProcessorId() const { return _physicalProcessorId; }
124
133 __INLINE__ static void detectThreadPUs(hwloc_topology_t topology, hwloc_obj_t obj, int depth, std::vector<logicalProcessorId_t> &threadPUs)
134 {
135 if (obj->arity == 0) threadPUs.push_back(obj->logical_index);
136 for (unsigned int i = 0; i < obj->arity; i++) detectThreadPUs(topology, obj->children[i], depth + 1, threadPUs);
137 }
138
146 __INLINE__ static logicalProcessorId_t detectLogicalProcessorId(hwloc_topology_t topology, const hwlocObjectIndex_t objectId)
147 {
148 hwloc_obj_t obj = hwloc_get_obj_by_type(topology, HWLOC_OBJ_PU, objectId);
149 if (!obj) HICR_THROW_RUNTIME("Attempting to access a compute resource that does not exist (%u) in this backend", objectId);
150
151 return obj->logical_index;
152 }
153
161 __INLINE__ static physicalProcessorId_t detectPhysicalProcessorId(hwloc_topology_t topology, const hwlocObjectIndex_t objectId)
162 {
163 hwloc_obj_t obj = hwloc_get_obj_by_type(topology, HWLOC_OBJ_PU, objectId);
164 if (!obj) HICR_THROW_RUNTIME("Attempting to access a compute resource that does not exist (%u) in this backend", objectId);
165
166 return obj->os_index;
167 }
168
176 __INLINE__ static numaAffinity_t detectCoreNUMAffinity(hwloc_topology_t topology, const logicalProcessorId_t logicalProcessorId)
177 {
178 // Sanitize input? So far we only call it internally so assume ID given is safe?
179 hwloc_obj_t obj = hwloc_get_obj_by_type(topology, HWLOC_OBJ_PU, logicalProcessorId);
180
181 if (!obj) HICR_THROW_RUNTIME("Attempting to access a compute resource that does not exist (%lu) in this backend", logicalProcessorId);
182
183 size_t ret = 0;
184
185 // obj is a leaf/PU; get to its parents to discover the hwloc memory space it belongs to
186 hwloc_obj_t ancestor = obj->parent;
187 hwloc_obj_t nodeNUMA = nullptr;
188 bool found = false;
189
190 // iterate over parents until we find a memory node
191 while (ancestor && !ancestor->memory_arity) ancestor = ancestor->parent;
192
193 // iterate over potential sibling nodes (the likely behavior though is to run only once)
194 for (size_t memChild = 0; memChild < ancestor->memory_arity; memChild++)
195 {
196 if (memChild == 0)
197 nodeNUMA = ancestor->memory_first_child;
198 else if (nodeNUMA)
199 nodeNUMA = nodeNUMA->next_sibling;
200
201 if (hwloc_obj_type_is_memory(nodeNUMA->type) && hwloc_bitmap_isset(obj->nodeset, nodeNUMA->os_index))
202 {
203 found = true;
204 ret = nodeNUMA->logical_index;
205 break;
206 }
207 }
208
209 if (!found) HICR_THROW_RUNTIME("NUMA Domain not detected for compute resource (%lu)", logicalProcessorId);
210
211 return ret;
212 }
213
228 __INLINE__ static std::unordered_set<std::shared_ptr<backend::hwloc::Cache>> detectCpuCaches(hwloc_topology_t topology, const logicalProcessorId_t logicalProcessorId)
229 {
230 // Sanitize input? So far we only call it internally so assume ID given is safe?
231 hwloc_obj_t obj = hwloc_get_obj_by_type(topology, HWLOC_OBJ_PU, logicalProcessorId);
232
233 if (!obj) HICR_THROW_RUNTIME("Attempting to access a compute resource that does not exist (%lu) in this backend", logicalProcessorId);
234
235 std::unordered_set<std::shared_ptr<backend::hwloc::Cache>> ret;
236
237 // Start from 1 level above our leaf/PU
238 hwloc_obj_t cache = obj->parent;
239 while (cache)
240 {
242 std::string type;
243
244 // Check if the current object is a cache-type object
245 if (cache->type == HWLOC_OBJ_L1CACHE || cache->type == HWLOC_OBJ_L2CACHE || cache->type == HWLOC_OBJ_L3CACHE || cache->type == HWLOC_OBJ_L4CACHE ||
246 cache->type == HWLOC_OBJ_L5CACHE || cache->type == HWLOC_OBJ_L1ICACHE || cache->type == HWLOC_OBJ_L2ICACHE || cache->type == HWLOC_OBJ_L3ICACHE)
247 {
248 // In case it is a cache, deduce the level from the types HWloc supports
249 switch (cache->type)
250 {
251 case HWLOC_OBJ_L1CACHE:
252 case HWLOC_OBJ_L1ICACHE: level = Cache::cacheLevel_t::L1; break;
253 case HWLOC_OBJ_L2CACHE:
254 case HWLOC_OBJ_L2ICACHE: level = Cache::cacheLevel_t::L2; break;
255 case HWLOC_OBJ_L3CACHE:
256 case HWLOC_OBJ_L3ICACHE: level = Cache::cacheLevel_t::L3; break;
257 case HWLOC_OBJ_L4CACHE: level = Cache::cacheLevel_t::L4; break;
258 case HWLOC_OBJ_L5CACHE: level = Cache::cacheLevel_t::L5; break;
259 // We never expect to get here; this is for compiler warning suppresion
260 default: HICR_THROW_RUNTIME("Unsupported Cache level detected (%lu)", cache->type);
261 }
262
263 // Storage for cache type
264 std::string type = "Unknown";
265
266 // Discover the type: Instruction, Data or Unified
267 switch (cache->attr->cache.type)
268 {
269 case HWLOC_OBJ_CACHE_UNIFIED: type = "Unified"; break;
270 case HWLOC_OBJ_CACHE_INSTRUCTION: type = "Instruction"; break;
271 case HWLOC_OBJ_CACHE_DATA: type = "Data"; break;
272 }
273
274 // Storage for more cache information
275 const bool shared = cache->arity > 1;
276 const auto size = cache->attr->cache.size;
277 const auto lineSize = cache->attr->cache.linesize;
278
279 // Insert element to our return container
280 ret.insert(std::make_shared<backend::hwloc::Cache>(level, type, size, lineSize, shared));
281 }
282
283 // Repeat the search 1 level above
284 cache = cache->parent;
285 }
286
287 return ret;
288 }
289
297 __INLINE__ static numaAffinity_t getCpuNumaAffinity(hwloc_topology_t topology, const logicalProcessorId_t logicalProcessorId)
298 {
299 // Sanitize input? So far we only call it internally so assume ID given is safe?
300 hwloc_obj_t obj = hwloc_get_obj_by_type(topology, HWLOC_OBJ_PU, logicalProcessorId);
301
302 if (!obj) HICR_THROW_RUNTIME("Attempting to access a compute resource that does not exist (%lu) in this backend", logicalProcessorId);
303
304 numaAffinity_t ret = 0;
305
306 // obj is a leaf/PU; get to its parents to discover the hwloc memory space it belongs to
307 hwloc_obj_t ancestor = obj->parent;
308 hwloc_obj_t nodeNUMA = nullptr;
309 bool found = false;
310
311 // iterate over parents until we find a memory node
312 while (ancestor && !ancestor->memory_arity) ancestor = ancestor->parent;
313
314 // iterate over potential sibling nodes (the likely behavior though is to run only once)
315 for (size_t memChild = 0; memChild < ancestor->memory_arity; memChild++)
316 {
317 if (memChild == 0)
318 nodeNUMA = ancestor->memory_first_child;
319 else if (nodeNUMA)
320 nodeNUMA = nodeNUMA->next_sibling;
321
322 if (hwloc_obj_type_is_memory(nodeNUMA->type) && hwloc_bitmap_isset(obj->nodeset, nodeNUMA->os_index))
323 {
324 found = true;
325 ret = (numaAffinity_t)nodeNUMA->logical_index;
326 break;
327 }
328 }
329
330 if (found == false) HICR_THROW_RUNTIME("NUMA Domain not detected for compute resource (%lu)", logicalProcessorId);
331
332 return ret;
333 }
334
335 protected:
336
337 __INLINE__ void serializeImpl(nlohmann::json &output) const override
338 {
339 // Writing core's information into the serialized object
340 output["Logical Processor Id"] = _logicalProcessorId;
341 output["Physical Processor Id"] = _physicalProcessorId;
342 output["NUMA Affinity"] = _numaAffinity;
343
344 // Writing Cache information
345 std::string cachesKey = "Caches";
346 output[cachesKey] = std::vector<nlohmann::json>();
347 for (const auto &cache : _caches) output[cachesKey] += cache->serialize();
348 }
349
350 __INLINE__ void deserializeImpl(const nlohmann::json &input) override
351 {
352 // Checking whether the type is correct
353 if (_type != "Processing Unit") HICR_THROW_LOGIC("The passed compute resource type '%s' is not compatible with this topology manager", _type.c_str());
354
355 std::string key = "Logical Processor Id";
356 if (input.contains(key) == false) HICR_THROW_LOGIC("The serialized object contains no '%s' key", key.c_str());
357 if (input[key].is_number() == false) HICR_THROW_LOGIC("The '%s' entry is not a number", key.c_str());
358 _logicalProcessorId = input[key].get<logicalProcessorId_t>();
359
360 key = "Physical Processor Id";
361 if (input.contains(key) == false) HICR_THROW_LOGIC("The serialized object contains no '%s' key", key.c_str());
362 if (input[key].is_number() == false) HICR_THROW_LOGIC("The '%s' entry is not a number", key.c_str());
363 _physicalProcessorId = input[key].get<physicalProcessorId_t>();
364
365 key = "NUMA Affinity";
366 if (input.contains(key) == false) HICR_THROW_LOGIC("The serialized object contains no '%s' key", key.c_str());
367 if (input[key].is_number() == false) HICR_THROW_LOGIC("The '%s' entry is not a number", key.c_str());
368 _numaAffinity = input[key].get<numaAffinity_t>();
369
370 key = "Caches";
371 if (input.contains(key) == false) HICR_THROW_LOGIC("The serialized object contains no '%s' key", key.c_str());
372 if (input[key].is_array() == false) HICR_THROW_LOGIC("The '%s' entry is not an array", key.c_str());
373
374 _caches.clear();
375 for (const auto &c : input[key])
376 {
377 // Deserializing cache
378 auto cache = std::make_shared<backend::hwloc::Cache>(c);
379
380 // Adding it to the list
381 _caches.insert(cache);
382 }
383 }
384
385 private:
386
390 hwlocObjectIndex_t _hwlocObjectIndex;
391
395 logicalProcessorId_t _logicalProcessorId;
396
402 physicalProcessorId_t _physicalProcessorId;
403
407 numaAffinity_t _numaAffinity;
408
413 std::unordered_set<std::shared_ptr<backend::hwloc::Cache>> _caches;
414};
415
416} // namespace HiCR::backend::hwloc
Defines the Cache class for interacting with the host (CPUs) device type.
Definition computeResource.hpp:40
std::string _type
Definition computeResource.hpp:109
cacheLevel_t
Definition cache.hpp:46
@ L1
Cache Level L1.
Definition cache.hpp:48
@ L3
Cache Level L3.
Definition cache.hpp:54
@ L2
Cache Level L2.
Definition cache.hpp:51
@ L4
Cache Level L4.
Definition cache.hpp:57
@ L5
Cache Level L5.
Definition cache.hpp:60
Definition computeResource.hpp:41
__INLINE__ void serializeImpl(nlohmann::json &output) const override
Definition computeResource.hpp:337
static __INLINE__ logicalProcessorId_t detectLogicalProcessorId(hwloc_topology_t topology, const hwlocObjectIndex_t objectId)
Definition computeResource.hpp:146
unsigned int hwlocObjectIndex_t
Definition computeResource.hpp:47
__INLINE__ void deserializeImpl(const nlohmann::json &input) override
Definition computeResource.hpp:350
ComputeResource(const hwlocObjectIndex_t hwlocObjectIndex, const logicalProcessorId_t logicalProcessorId, const physicalProcessorId_t physicalProcessorId, const numaAffinity_t numaAffinity, std::unordered_set< std::shared_ptr< backend::hwloc::Cache > > caches)
Definition computeResource.hpp:88
unsigned int logicalProcessorId_t
Definition computeResource.hpp:52
static __INLINE__ std::unordered_set< std::shared_ptr< backend::hwloc::Cache > > detectCpuCaches(hwloc_topology_t topology, const logicalProcessorId_t logicalProcessorId)
Definition computeResource.hpp:228
unsigned int numaAffinity_t
Definition computeResource.hpp:62
ComputeResource()
Definition computeResource.hpp:108
ComputeResource(hwloc_topology_t topology, const hwlocObjectIndex_t hwlocObjectIndex)
Definition computeResource.hpp:69
unsigned int physicalProcessorId_t
Definition computeResource.hpp:57
static __INLINE__ physicalProcessorId_t detectPhysicalProcessorId(hwloc_topology_t topology, const hwlocObjectIndex_t objectId)
Definition computeResource.hpp:161
static __INLINE__ numaAffinity_t detectCoreNUMAffinity(hwloc_topology_t topology, const logicalProcessorId_t logicalProcessorId)
Definition computeResource.hpp:176
__INLINE__ logicalProcessorId_t getProcessorId() const
Definition computeResource.hpp:115
__INLINE__ physicalProcessorId_t getPhysicalProcessorId() const
Definition computeResource.hpp:123
static __INLINE__ numaAffinity_t getCpuNumaAffinity(hwloc_topology_t topology, const logicalProcessorId_t logicalProcessorId)
Definition computeResource.hpp:297
static __INLINE__ void detectThreadPUs(hwloc_topology_t topology, hwloc_obj_t obj, int depth, std::vector< logicalProcessorId_t > &threadPUs)
Definition computeResource.hpp:133
Provides a base definition for a HiCR ComputeResource class.
Provides a failure model and corresponding exception classes.
#define HICR_THROW_RUNTIME(...)
Definition exceptions.hpp:74
#define HICR_THROW_LOGIC(...)
Definition exceptions.hpp:67