2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
5 * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
6 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7 * and including many others, as listed in the AUTHORS file in the
8 * top-level source directory and at http://www.gromacs.org.
10 * GROMACS is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1
13 * of the License, or (at your option) any later version.
15 * GROMACS is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with GROMACS; if not, see
22 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * If you want to redistribute modifications to GROMACS, please
26 * consider that scientific software is very special. Version
27 * control is crucial - bugs must be traceable. We will be happy to
28 * consider code for inclusion in the official distribution, but
29 * derived work must not be called official GROMACS. Details are found
30 * in the README & COPYING files - if they are missing, get the
31 * official version at http://www.gromacs.org.
33 * To help us fund GROMACS development, we humbly ask that you cite
34 * the research papers on the package. Check out http://www.gromacs.org.
39 * Implements gmx::HardwareTopology.
41 * \author Erik Lindahl <erik.lindahl@gmail.com>
42 * \ingroup module_hardware
47 #include "hardwaretopology.h"
63 #include "gromacs/hardware/cpuinfo.h"
64 #include "gromacs/utility/gmxassert.h"
67 # include <unistd.h> // sysconf()
69 #if GMX_NATIVE_WINDOWS
70 # include <windows.h> // GetSystemInfo()
73 //! Convenience macro to help us avoid ifdefs each time we use sysconf
74 #if !defined(_SC_NPROCESSORS_ONLN) && defined(_SC_NPROC_ONLN)
75 # define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN
84 /*****************************************************************************
86 * Utility functions for extracting hardware topology from CpuInfo object *
88 *****************************************************************************/
90 /*! \brief Initialize machine data from basic information in cpuinfo
92 * \param machine Machine tree structure where information will be assigned
93 * if the cpuinfo object contains topology information.
94 * \param supportLevel If topology information is available in CpuInfo,
95 * this will be updated to reflect the amount of
96 * information written to the machine structure.
98 void parseCpuInfo(HardwareTopology::Machine* machine, HardwareTopology::SupportLevel* supportLevel)
100 CpuInfo cpuInfo(CpuInfo::detect());
102 if (!cpuInfo.logicalProcessors().empty())
108 // Copy the logical processor information from cpuinfo
109 for (auto& l : cpuInfo.logicalProcessors())
111 machine->logicalProcessors.push_back(
112 { l.socketRankInMachine, l.coreRankInSocket, l.hwThreadRankInCore, -1 });
113 nSockets = std::max(nSockets, l.socketRankInMachine);
114 nCores = std::max(nCores, l.coreRankInSocket);
115 nHwThreads = std::max(nHwThreads, l.hwThreadRankInCore);
118 // Fill info form sockets/cores/hwthreads
123 machine->sockets.resize(nSockets + 1);
124 for (auto& s : machine->sockets)
127 s.cores.resize(nCores + 1);
128 for (auto& c : s.cores)
131 c.numaNodeId = -1; // No numa information
132 c.hwThreads.resize(nHwThreads + 1);
133 for (auto& t : c.hwThreads)
136 t.logicalProcessorId = -1; // set as unassigned for now
141 // Fill the logical processor id in the right place
142 for (std::size_t i = 0; i < machine->logicalProcessors.size(); i++)
144 const HardwareTopology::LogicalProcessor& l = machine->logicalProcessors[i];
145 machine->sockets[l.socketRankInMachine]
146 .cores[l.coreRankInSocket]
147 .hwThreads[l.hwThreadRankInCore]
148 .logicalProcessorId = static_cast<int>(i);
150 machine->logicalProcessorCount = machine->logicalProcessors.size();
151 *supportLevel = HardwareTopology::SupportLevel::Basic;
155 *supportLevel = HardwareTopology::SupportLevel::None;
161 # if HWLOC_API_VERSION < 0x00010b00
162 # define HWLOC_OBJ_PACKAGE HWLOC_OBJ_SOCKET
163 # define HWLOC_OBJ_NUMANODE HWLOC_OBJ_NODE
166 // Preprocessor variable for if hwloc api is version 1.x.x or 2.x.x
167 # if HWLOC_API_VERSION >= 0x00020000
168 # define GMX_HWLOC_API_VERSION_IS_2XX 1
169 # if GMX_HWLOC_API_VERSION < 0x00020000
170 # error "HWLOC library major version set during configuration is 1, but currently using version 2 headers"
173 # define GMX_HWLOC_API_VERSION_IS_2XX 0
174 # if GMX_HWLOC_API_VERSION >= 0x00020000
175 # error "HWLOC library major version set during configuration is 2, but currently using version 1 headers"
179 /*****************************************************************************
181 * Utility functions for extracting hardware topology from hwloc library *
183 *****************************************************************************/
185 // Compatibility function for accessing hwloc_obj_t object memory with different API versions of hwloc
186 std::size_t getHwLocObjectMemory(const hwloc_obj* obj)
188 # if GMX_HWLOC_API_VERSION_IS_2XX
189 return obj->total_memory;
191 return obj->memory.total_memory;
195 /*! \brief Return vector of all descendants of a given type in hwloc topology
197 * \param topo hwloc topology handle that has been initialized and loaded
198 * \param obj Non-null hwloc object.
199 * \param type hwloc object type to find. The routine will only search
200 * on levels below obj.
202 * \return vector containing all the objects of given type that are
203 * descendants of the provided object. If no objects of this type
204 * were found, the vector will be empty.
206 std::vector<const hwloc_obj*> getHwLocDescendantsByType(const hwloc_topology* topo,
207 const hwloc_obj* obj,
208 const hwloc_obj_type_t type)
210 GMX_RELEASE_ASSERT(obj, "NULL hwloc object provided to getHwLocDescendantsByType()");
212 std::vector<const hwloc_obj*> v;
214 if (obj->type == type)
218 // Go through children; if this object has no children obj->arity is 0,
219 // and we'll return an empty vector.
220 hwloc_obj_t tempNode = nullptr;
221 while ((tempNode = hwloc_get_next_child(const_cast<hwloc_topology_t>(topo),
222 const_cast<hwloc_obj_t>(obj), tempNode))
225 std::vector<const hwloc_obj*> v2 = getHwLocDescendantsByType(topo, tempNode, type);
226 v.insert(v.end(), v2.begin(), v2.end());
231 /*! \brief Read information about sockets, cores and threads from hwloc topology
233 * \param topo hwloc topology handle that has been initialized and loaded
234 * \param machine Pointer to the machine structure in the HardwareTopology
235 * class, where the tree of sockets/cores/threads will be written.
237 * \return If all the data is found
239 bool parseHwLocSocketsCoresThreads(hwloc_topology_t topo, HardwareTopology::Machine* machine)
241 const hwloc_obj* root = hwloc_get_root_obj(topo);
242 std::vector<const hwloc_obj*> hwlocSockets = getHwLocDescendantsByType(topo, root, HWLOC_OBJ_PACKAGE);
244 machine->logicalProcessorCount = hwloc_get_nbobjs_by_type(topo, HWLOC_OBJ_PU);
245 machine->logicalProcessors.resize(machine->logicalProcessorCount);
246 machine->sockets.resize(hwlocSockets.size());
248 bool topologyOk = !hwlocSockets.empty(); // Fail if we have no sockets in machine
250 for (std::size_t i = 0; i < hwlocSockets.size() && topologyOk; i++)
252 // Assign information about this socket
253 machine->sockets[i].id = hwlocSockets[i]->logical_index;
255 // Get children (cores)
256 std::vector<const hwloc_obj*> hwlocCores =
257 getHwLocDescendantsByType(topo, hwlocSockets[i], HWLOC_OBJ_CORE);
258 machine->sockets[i].cores.resize(hwlocCores.size());
260 topologyOk = topologyOk && !hwlocCores.empty(); // Fail if we have no cores in socket
262 // Loop over child cores
263 for (std::size_t j = 0; j < hwlocCores.size() && topologyOk; j++)
265 // Assign information about this core
266 machine->sockets[i].cores[j].id = hwlocCores[j]->logical_index;
267 machine->sockets[i].cores[j].numaNodeId = -1;
269 // Get children (hwthreads)
270 std::vector<const hwloc_obj*> hwlocPUs =
271 getHwLocDescendantsByType(topo, hwlocCores[j], HWLOC_OBJ_PU);
272 machine->sockets[i].cores[j].hwThreads.resize(hwlocPUs.size());
274 topologyOk = topologyOk && !hwlocPUs.empty(); // Fail if we have no hwthreads in core
276 // Loop over child hwthreads
277 for (std::size_t k = 0; k < hwlocPUs.size() && topologyOk; k++)
279 // Assign information about this hwthread
280 std::size_t logicalProcessorId = hwlocPUs[k]->os_index;
281 machine->sockets[i].cores[j].hwThreads[k].id = hwlocPUs[k]->logical_index;
282 machine->sockets[i].cores[j].hwThreads[k].logicalProcessorId = logicalProcessorId;
284 if (logicalProcessorId < machine->logicalProcessors.size())
286 // Cross-assign data for this hwthread to the logicalprocess vector
287 machine->logicalProcessors[logicalProcessorId].socketRankInMachine =
289 machine->logicalProcessors[logicalProcessorId].coreRankInSocket =
291 machine->logicalProcessors[logicalProcessorId].hwThreadRankInCore =
293 machine->logicalProcessors[logicalProcessorId].numaNodeId = -1;
305 machine->logicalProcessors.clear();
306 machine->sockets.clear();
311 /*! \brief Read cache information from hwloc topology
313 * \param topo hwloc topology handle that has been initialized and loaded
314 * \param machine Pointer to the machine structure in the HardwareTopology
315 * class, where cache data will be filled.
317 * \return If any cache data is found
319 bool parseHwLocCache(hwloc_topology_t topo, HardwareTopology::Machine* machine)
321 // Parse caches up to L5
322 for (int cachelevel : { 1, 2, 3, 4, 5 })
324 int depth = hwloc_get_cache_type_depth(topo, cachelevel, HWLOC_OBJ_CACHE_DATA);
328 hwloc_obj_t cache = hwloc_get_next_obj_by_depth(topo, depth, nullptr);
329 if (cache != nullptr)
331 std::vector<const hwloc_obj*> hwThreads =
332 getHwLocDescendantsByType(topo, cache, HWLOC_OBJ_PU);
334 machine->caches.push_back({ static_cast<int>(cache->attr->cache.depth),
335 static_cast<std::size_t>(cache->attr->cache.size),
336 static_cast<int>(cache->attr->cache.linesize),
337 static_cast<int>(cache->attr->cache.associativity),
338 std::max<int>(hwThreads.size(), 1) });
342 return !machine->caches.empty();
346 /*! \brief Read numa information from hwloc topology
348 * \param topo hwloc topology handle that has been initialized and loaded
349 * \param machine Pointer to the machine structure in the HardwareTopology
350 * class, where numa information will be filled.
352 * Hwloc should virtually always be able to detect numa information, but if
353 * there is only a single numa node in the system it is not reported at all.
354 * In this case we create a single numa node covering all cores.
356 * This function uses the basic socket/core/thread information detected by
357 * parseHwLocSocketsCoresThreads(), which means that routine must have
358 * completed successfully before calling this one. If this is not the case,
359 * you will get an error return code.
361 * \return If the data found makes sense (either in the numa node or the
364 bool parseHwLocNuma(hwloc_topology_t topo, HardwareTopology::Machine* machine)
366 const hwloc_obj* root = hwloc_get_root_obj(topo);
367 std::vector<const hwloc_obj*> hwlocNumaNodes =
368 getHwLocDescendantsByType(topo, root, HWLOC_OBJ_NUMANODE);
369 bool topologyOk = true;
371 if (!hwlocNumaNodes.empty())
373 machine->numa.nodes.resize(hwlocNumaNodes.size());
375 for (std::size_t i = 0; i < hwlocNumaNodes.size(); i++)
377 machine->numa.nodes[i].id = hwlocNumaNodes[i]->logical_index;
378 machine->numa.nodes[i].memory = getHwLocObjectMemory(hwlocNumaNodes[i]);
380 machine->numa.nodes[i].logicalProcessorId.clear();
382 // Get list of PUs in this numa node. Get from numa node if v1.x.x, get from numa node's parent if 2.x.x
383 # if GMX_HWLOC_API_VERSION_IS_2XX
384 std::vector<const hwloc_obj*> hwlocPUs =
385 getHwLocDescendantsByType(topo, hwlocNumaNodes[i]->parent, HWLOC_OBJ_PU);
387 std::vector<const hwloc_obj*> hwlocPUs =
388 getHwLocDescendantsByType(topo, hwlocNumaNodes[i], HWLOC_OBJ_PU);
390 for (auto& p : hwlocPUs)
392 machine->numa.nodes[i].logicalProcessorId.push_back(p->os_index);
394 GMX_RELEASE_ASSERT(p->os_index < machine->logicalProcessors.size(),
395 "OS index of PU in hwloc larger than processor count");
397 machine->logicalProcessors[p->os_index].numaNodeId = static_cast<int>(i);
398 std::size_t s = machine->logicalProcessors[p->os_index].socketRankInMachine;
399 std::size_t c = machine->logicalProcessors[p->os_index].coreRankInSocket;
401 GMX_RELEASE_ASSERT(s < machine->sockets.size(),
402 "Socket index in logicalProcessors larger than socket count");
403 GMX_RELEASE_ASSERT(c < machine->sockets[s].cores.size(),
404 "Core index in logicalProcessors larger than core count");
405 // Set numaNodeId in core too
406 machine->sockets[s].cores[c].numaNodeId = i;
409 // Getting the distance matrix
410 # if GMX_HWLOC_API_VERSION_IS_2XX
411 // with hwloc api v. 2.x.x, distances are no longer directly accessible. Need to retrieve and release hwloc_distances_s object
412 // In addition, there can now be multiple types of distances, ie latency, bandwidth. We look only for latency, but have to check
413 // if multiple distance matrices are returned.
415 // If only 1 numa node exists, the v2.x.x hwloc api won't have a distances matrix, set manually
416 if (hwlocNumaNodes.size() == 1)
418 machine->numa.relativeLatency = { { 1.0 } };
422 hwloc_distances_s* dist;
423 // Set the number of distance matrices to return (1 in our case, but hwloc 2.x.x allows
424 // for multiple distances types and therefore multiple distance matrices)
426 hwloc_distances_get(topo, &nr, &dist, HWLOC_DISTANCES_KIND_MEANS_LATENCY, 0);
427 // If no distances were found, nr will be 0, otherwise distances will be populated with
428 // 1 hwloc_distances_s object
429 if (nr > 0 && dist->nbobjs == hwlocNumaNodes.size())
432 machine->numa.relativeLatency.resize(dist->nbobjs);
433 for (std::size_t i = 0; i < dist->nbobjs; i++)
435 machine->numa.relativeLatency[i].resize(dist->nbobjs);
436 for (std::size_t j = 0; j < dist->nbobjs; j++)
438 machine->numa.relativeLatency[i][j] = dist->values[i * dist->nbobjs + j];
446 hwloc_distances_release(topo, dist);
449 // hwloc-2.x provides latencies as integers, but to make things more similar to the case of
450 // a single numa node as well as hwloc-1.x, we rescale to relative floating-point values and
451 // also set the largest relative latency value.
453 // find smallest value in matrix
454 float minLatency = std::numeric_limits<float>::max(); // large number
455 float maxLatency = std::numeric_limits<float>::min(); // 0.0
456 for (const auto& v : machine->numa.relativeLatency)
458 auto result = std::minmax_element(v.begin(), v.end());
459 minLatency = std::min(minLatency, *result.first);
460 maxLatency = std::max(maxLatency, *result.second);
464 for (auto& v : machine->numa.relativeLatency)
466 std::transform(v.begin(), v.end(), v.begin(),
467 std::bind(std::multiplies<float>(), std::placeholders::_1, 1.0 / minLatency));
469 machine->numa.baseLatency = 1.0; // latencies still do not have any units in hwloc-2.x
470 machine->numa.maxRelativeLatency = maxLatency / minLatency;
472 # else // GMX_HWLOC_API_VERSION_IS_2XX == false, hwloc api is 1.x.x
473 int depth = hwloc_get_type_depth(topo, HWLOC_OBJ_NUMANODE);
474 const struct hwloc_distances_s* dist = hwloc_get_whole_distance_matrix_by_depth(topo, depth);
475 if (dist != nullptr && dist->nbobjs == hwlocNumaNodes.size())
477 machine->numa.baseLatency = dist->latency_base;
478 machine->numa.maxRelativeLatency = dist->latency_max;
479 machine->numa.relativeLatency.resize(dist->nbobjs);
480 for (std::size_t i = 0; i < dist->nbobjs; i++)
482 machine->numa.relativeLatency[i].resize(dist->nbobjs);
483 for (std::size_t j = 0; j < dist->nbobjs; j++)
485 machine->numa.relativeLatency[i][j] = dist->latency[i * dist->nbobjs + j];
493 # endif // end GMX_HWLOC_API_VERSION_IS_2XX == false
496 // Deals with the case of no numa nodes found.
497 # if GMX_HWLOC_API_VERSION_IS_2XX
498 // If the hwloc version is 2.x.x, and there is no numa node, something went wrong
504 // No numa nodes found. Use the entire machine as a numa node.
505 // Note that this should only be the case with hwloc api v 1.x.x,
506 // a numa node is assigned to the machine by default in v 2.x.x
507 const hwloc_obj* const hwlocMachine = hwloc_get_next_obj_by_type(topo, HWLOC_OBJ_MACHINE, nullptr);
509 if (hwlocMachine != nullptr)
511 machine->numa.nodes.resize(1);
512 machine->numa.nodes[0].id = 0;
513 machine->numa.nodes[0].memory = hwlocMachine->memory.total_memory;
514 machine->numa.baseLatency = 10;
515 machine->numa.maxRelativeLatency = 1;
516 machine->numa.relativeLatency = { { 1.0 } };
518 for (int i = 0; i < machine->logicalProcessorCount; i++)
520 machine->numa.nodes[0].logicalProcessorId.push_back(i);
522 for (auto& l : machine->logicalProcessors)
526 for (auto& s : machine->sockets)
528 for (auto& c : s.cores)
539 # endif // end if not GMX_HWLOC_API_VERSION_IS_2XX
542 machine->numa.nodes.clear();
547 /*! \brief Read PCI device information from hwloc topology
549 * \param topo hwloc topology handle that has been initialized and loaded
550 * \param machine Pointer to the machine structure in the HardwareTopology
551 * class, where PCI device information will be filled.
553 * \return If any devices were found
555 bool parseHwLocDevices(hwloc_topology_t topo, HardwareTopology::Machine* machine)
557 const hwloc_obj* root = hwloc_get_root_obj(topo);
558 std::vector<const hwloc_obj*> pcidevs = getHwLocDescendantsByType(topo, root, HWLOC_OBJ_PCI_DEVICE);
560 for (auto& p : pcidevs)
562 # if GMX_HWLOC_API_VERSION_IS_2XX
563 const hwloc_obj* ancestor = nullptr;
564 // Numa nodes not directly part of tree. Walk up the tree until we find an ancestor with a numa node
565 hwloc_obj_t parent = p->parent;
566 while (parent && !parent->memory_arity)
568 parent = parent->parent;
572 ancestor = parent->memory_first_child;
574 # else // GMX_HWLOC_API_VERSION_IS_2XX = false, api v 1.x.x
575 // numa nodes are normal part of tree, can use hwloc ancestor function
576 const hwloc_obj* const ancestor =
577 hwloc_get_ancestor_obj_by_type(topo, HWLOC_OBJ_NUMANODE, const_cast<hwloc_obj_t>(p));
578 # endif // end if GMX_HWLOC_API_VERSION_IS_2XX
580 if (ancestor != nullptr)
582 numaId = ancestor->logical_index;
586 // If we only have a single numa node we belong to it, otherwise set it to -1 (unknown)
587 numaId = (machine->numa.nodes.size() == 1) ? 0 : -1;
590 GMX_RELEASE_ASSERT(p->attr, "Attributes should not be NULL for hwloc PCI object");
592 machine->devices.push_back({ p->attr->pcidev.vendor_id, p->attr->pcidev.device_id,
593 p->attr->pcidev.class_id, p->attr->pcidev.domain,
594 p->attr->pcidev.bus, p->attr->pcidev.dev, p->attr->pcidev.func,
597 return !pcidevs.empty();
600 void parseHwLoc(HardwareTopology::Machine* machine, HardwareTopology::SupportLevel* supportLevel, bool* isThisSystem)
602 hwloc_topology_t topo;
604 // Initialize a hwloc object, set flags to request IO device information too,
605 // try to load the topology, and get the root object. If either step fails,
606 // return that we do not have any support at all from hwloc.
607 if (hwloc_topology_init(&topo) != 0)
609 hwloc_topology_destroy(topo);
610 return; // SupportLevel::None.
613 // Flags to look for io devices
614 # if GMX_HWLOC_API_VERSION_IS_2XX
616 (hwloc_get_api_version() >= 0x20000),
617 "Mismatch between hwloc headers and library, using v2 headers with v1 library");
618 hwloc_topology_set_io_types_filter(topo, HWLOC_TYPE_FILTER_KEEP_IMPORTANT);
621 (hwloc_get_api_version() < 0x20000),
622 "Mismatch between hwloc headers and library, using v1 headers with v2 library");
623 hwloc_topology_set_flags(topo, HWLOC_TOPOLOGY_FLAG_IO_DEVICES);
626 if (hwloc_topology_load(topo) != 0 || hwloc_get_root_obj(topo) == nullptr)
628 hwloc_topology_destroy(topo);
629 return; // SupportLevel::None.
632 // If we get here, we can get a valid root object for the topology
633 *isThisSystem = hwloc_topology_is_thissystem(topo) != 0;
635 // Parse basic information about sockets, cores, and hardware threads
636 if (parseHwLocSocketsCoresThreads(topo, machine))
638 *supportLevel = HardwareTopology::SupportLevel::Basic;
642 hwloc_topology_destroy(topo);
643 return; // SupportLevel::None.
646 // Get information about cache and numa nodes
647 if (parseHwLocCache(topo, machine) && parseHwLocNuma(topo, machine))
649 *supportLevel = HardwareTopology::SupportLevel::Full;
653 hwloc_topology_destroy(topo);
654 return; // SupportLevel::Basic.
658 if (parseHwLocDevices(topo, machine))
660 *supportLevel = HardwareTopology::SupportLevel::FullWithDevices;
663 hwloc_topology_destroy(topo);
664 // SupportLevel::Full or SupportLevel::FullWithDevices.
669 /*! \brief Try to detect the number of logical processors.
671 * \return The number of hardware processing units, or 0 if it fails.
673 int detectLogicalProcessorCount()
678 #if GMX_NATIVE_WINDOWS
681 GetSystemInfo(&sysinfo);
682 count = sysinfo.dwNumberOfProcessors;
683 #elif defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
684 // We are probably on Unix. Check if we have the argument to use before executing any calls
685 count = sysconf(_SC_NPROCESSORS_ONLN);
687 count = 0; // Neither windows nor Unix.
697 HardwareTopology HardwareTopology::detect()
699 HardwareTopology result;
702 parseHwLoc(&result.machine_, &result.supportLevel_, &result.isThisSystem_);
705 // If something went wrong in hwloc (or if it was not present) we might
706 // have more information in cpuInfo
707 if (result.supportLevel_ < SupportLevel::Basic)
709 // There might be topology information in cpuInfo
710 parseCpuInfo(&result.machine_, &result.supportLevel_);
712 // If we did not manage to get anything from either hwloc or cpuInfo, find the cpu count at least
713 if (result.supportLevel_ == SupportLevel::None)
715 // No topology information; try to detect the number of logical processors at least
716 result.machine_.logicalProcessorCount = detectLogicalProcessorCount();
717 if (result.machine_.logicalProcessorCount > 0)
719 result.supportLevel_ = SupportLevel::LogicalProcessorCount;
725 HardwareTopology::Machine::Machine()
727 logicalProcessorCount = 0;
728 numa.baseLatency = 0.0;
729 numa.maxRelativeLatency = 0.0;
733 HardwareTopology::HardwareTopology() :
734 supportLevel_(SupportLevel::None),
740 HardwareTopology::HardwareTopology(int logicalProcessorCount) :
741 supportLevel_(SupportLevel::None),
745 if (logicalProcessorCount > 0)
747 machine_.logicalProcessorCount = logicalProcessorCount;
748 supportLevel_ = SupportLevel::LogicalProcessorCount;
752 int HardwareTopology::numberOfCores() const
754 if (supportLevel() >= SupportLevel::Basic)
756 // We assume all sockets have the same number of cores as socket 0.
757 // Since topology information is present, we can assume there is at least one socket.
758 return machine().sockets.size() * machine().sockets[0].cores.size();
760 else if (supportLevel() >= SupportLevel::LogicalProcessorCount)
762 return machine().logicalProcessorCount;