2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
38 * Implements gmx::HardwareTopology.
40 * \author Erik Lindahl <erik.lindahl@gmail.com>
41 * \ingroup module_hardware
46 #include "hardwaretopology.h"
62 #include "gromacs/hardware/cpuinfo.h"
63 #include "gromacs/utility/gmxassert.h"
66 # include <unistd.h> // sysconf()
68 #if GMX_NATIVE_WINDOWS
69 # include <windows.h> // GetSystemInfo()
72 //! Convenience macro to help us avoid ifdefs each time we use sysconf
73 #if !defined(_SC_NPROCESSORS_ONLN) && defined(_SC_NPROC_ONLN)
74 # define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN
83 /*****************************************************************************
85 * Utility functions for extracting hardware topology from CpuInfo object *
87 *****************************************************************************/
89 /*! \brief Initialize machine data from basic information in cpuinfo
91 * \param machine Machine tree structure where information will be assigned
92 * if the cpuinfo object contains topology information.
93 * \param supportLevel If topology information is available in CpuInfo,
94 * this will be updated to reflect the amount of
95 * information written to the machine structure.
97 void parseCpuInfo(HardwareTopology::Machine* machine, HardwareTopology::SupportLevel* supportLevel)
99 CpuInfo cpuInfo(CpuInfo::detect());
101 if (!cpuInfo.logicalProcessors().empty())
107 // Copy the logical processor information from cpuinfo
108 for (auto& l : cpuInfo.logicalProcessors())
110 machine->logicalProcessors.push_back(
111 { l.socketRankInMachine, l.coreRankInSocket, l.hwThreadRankInCore, -1 });
112 nSockets = std::max(nSockets, l.socketRankInMachine);
113 nCores = std::max(nCores, l.coreRankInSocket);
114 nHwThreads = std::max(nHwThreads, l.hwThreadRankInCore);
117 // Fill info form sockets/cores/hwthreads
122 machine->sockets.resize(nSockets + 1);
123 for (auto& s : machine->sockets)
126 s.cores.resize(nCores + 1);
127 for (auto& c : s.cores)
130 c.numaNodeId = -1; // No numa information
131 c.hwThreads.resize(nHwThreads + 1);
132 for (auto& t : c.hwThreads)
135 t.logicalProcessorId = -1; // set as unassigned for now
140 // Fill the logical processor id in the right place
141 for (std::size_t i = 0; i < machine->logicalProcessors.size(); i++)
143 const HardwareTopology::LogicalProcessor& l = machine->logicalProcessors[i];
144 machine->sockets[l.socketRankInMachine]
145 .cores[l.coreRankInSocket]
146 .hwThreads[l.hwThreadRankInCore]
147 .logicalProcessorId = static_cast<int>(i);
149 machine->logicalProcessorCount = machine->logicalProcessors.size();
150 *supportLevel = HardwareTopology::SupportLevel::Basic;
154 *supportLevel = HardwareTopology::SupportLevel::None;
160 # if HWLOC_API_VERSION < 0x00010b00
161 # define HWLOC_OBJ_PACKAGE HWLOC_OBJ_SOCKET
162 # define HWLOC_OBJ_NUMANODE HWLOC_OBJ_NODE
165 // Preprocessor variable for if hwloc api is version 1.x.x or 2.x.x
166 # if HWLOC_API_VERSION >= 0x00020000
167 # define GMX_HWLOC_API_VERSION_IS_2XX 1
168 # if GMX_HWLOC_API_VERSION < 0x00020000
169 # error "HWLOC library major version set during configuration is 1, but currently using version 2 headers"
172 # define GMX_HWLOC_API_VERSION_IS_2XX 0
173 # if GMX_HWLOC_API_VERSION >= 0x00020000
174 # error "HWLOC library major version set during configuration is 2, but currently using version 1 headers"
178 /*****************************************************************************
180 * Utility functions for extracting hardware topology from hwloc library *
182 *****************************************************************************/
184 // Compatibility function for accessing hwloc_obj_t object memory with different API versions of hwloc
185 std::size_t getHwLocObjectMemory(const hwloc_obj* obj)
187 # if GMX_HWLOC_API_VERSION_IS_2XX
188 return obj->total_memory;
190 return obj->memory.total_memory;
194 /*! \brief Return vector of all descendants of a given type in hwloc topology
196 * \param topo hwloc topology handle that has been initialized and loaded
197 * \param obj Non-null hwloc object.
198 * \param type hwloc object type to find. The routine will only search
199 * on levels below obj.
201 * \return vector containing all the objects of given type that are
202 * descendants of the provided object. If no objects of this type
203 * were found, the vector will be empty.
205 std::vector<const hwloc_obj*> getHwLocDescendantsByType(const hwloc_topology* topo,
206 const hwloc_obj* obj,
207 const hwloc_obj_type_t type)
209 GMX_RELEASE_ASSERT(obj, "NULL hwloc object provided to getHwLocDescendantsByType()");
211 std::vector<const hwloc_obj*> v;
213 if (obj->type == type)
217 // Go through children; if this object has no children obj->arity is 0,
218 // and we'll return an empty vector.
219 hwloc_obj_t tempNode = nullptr;
220 while ((tempNode = hwloc_get_next_child(const_cast<hwloc_topology_t>(topo),
221 const_cast<hwloc_obj_t>(obj), tempNode))
224 std::vector<const hwloc_obj*> v2 = getHwLocDescendantsByType(topo, tempNode, type);
225 v.insert(v.end(), v2.begin(), v2.end());
230 /*! \brief Read information about sockets, cores and threads from hwloc topology
232 * \param topo hwloc topology handle that has been initialized and loaded
233 * \param machine Pointer to the machine structure in the HardwareTopology
234 * class, where the tree of sockets/cores/threads will be written.
236 * \return If all the data is found
238 bool parseHwLocSocketsCoresThreads(hwloc_topology_t topo, HardwareTopology::Machine* machine)
240 const hwloc_obj* root = hwloc_get_root_obj(topo);
241 std::vector<const hwloc_obj*> hwlocSockets = getHwLocDescendantsByType(topo, root, HWLOC_OBJ_PACKAGE);
243 machine->logicalProcessorCount = hwloc_get_nbobjs_by_type(topo, HWLOC_OBJ_PU);
244 machine->logicalProcessors.resize(machine->logicalProcessorCount);
245 machine->sockets.resize(hwlocSockets.size());
247 bool topologyOk = !hwlocSockets.empty(); // Fail if we have no sockets in machine
249 for (std::size_t i = 0; i < hwlocSockets.size() && topologyOk; i++)
251 // Assign information about this socket
252 machine->sockets[i].id = hwlocSockets[i]->logical_index;
254 // Get children (cores)
255 std::vector<const hwloc_obj*> hwlocCores =
256 getHwLocDescendantsByType(topo, hwlocSockets[i], HWLOC_OBJ_CORE);
257 machine->sockets[i].cores.resize(hwlocCores.size());
259 topologyOk = topologyOk && !hwlocCores.empty(); // Fail if we have no cores in socket
261 // Loop over child cores
262 for (std::size_t j = 0; j < hwlocCores.size() && topologyOk; j++)
264 // Assign information about this core
265 machine->sockets[i].cores[j].id = hwlocCores[j]->logical_index;
266 machine->sockets[i].cores[j].numaNodeId = -1;
268 // Get children (hwthreads)
269 std::vector<const hwloc_obj*> hwlocPUs =
270 getHwLocDescendantsByType(topo, hwlocCores[j], HWLOC_OBJ_PU);
271 machine->sockets[i].cores[j].hwThreads.resize(hwlocPUs.size());
273 topologyOk = topologyOk && !hwlocPUs.empty(); // Fail if we have no hwthreads in core
275 // Loop over child hwthreads
276 for (std::size_t k = 0; k < hwlocPUs.size() && topologyOk; k++)
278 // Assign information about this hwthread
279 std::size_t logicalProcessorId = hwlocPUs[k]->os_index;
280 machine->sockets[i].cores[j].hwThreads[k].id = hwlocPUs[k]->logical_index;
281 machine->sockets[i].cores[j].hwThreads[k].logicalProcessorId = logicalProcessorId;
283 if (logicalProcessorId < machine->logicalProcessors.size())
285 // Cross-assign data for this hwthread to the logicalprocess vector
286 machine->logicalProcessors[logicalProcessorId].socketRankInMachine =
288 machine->logicalProcessors[logicalProcessorId].coreRankInSocket =
290 machine->logicalProcessors[logicalProcessorId].hwThreadRankInCore =
292 machine->logicalProcessors[logicalProcessorId].numaNodeId = -1;
304 machine->logicalProcessors.clear();
305 machine->sockets.clear();
310 /*! \brief Read cache information from hwloc topology
312 * \param topo hwloc topology handle that has been initialized and loaded
313 * \param machine Pointer to the machine structure in the HardwareTopology
314 * class, where cache data will be filled.
316 * \return If any cache data is found
318 bool parseHwLocCache(hwloc_topology_t topo, HardwareTopology::Machine* machine)
320 // Parse caches up to L5
321 for (int cachelevel : { 1, 2, 3, 4, 5 })
323 int depth = hwloc_get_cache_type_depth(topo, cachelevel, HWLOC_OBJ_CACHE_DATA);
327 hwloc_obj_t cache = hwloc_get_next_obj_by_depth(topo, depth, nullptr);
328 if (cache != nullptr)
330 std::vector<const hwloc_obj*> hwThreads =
331 getHwLocDescendantsByType(topo, cache, HWLOC_OBJ_PU);
333 machine->caches.push_back({ static_cast<int>(cache->attr->cache.depth),
334 static_cast<std::size_t>(cache->attr->cache.size),
335 static_cast<int>(cache->attr->cache.linesize),
336 static_cast<int>(cache->attr->cache.associativity),
337 std::max<int>(hwThreads.size(), 1) });
341 return !machine->caches.empty();
345 /*! \brief Read numa information from hwloc topology
347 * \param topo hwloc topology handle that has been initialized and loaded
348 * \param machine Pointer to the machine structure in the HardwareTopology
349 * class, where numa information will be filled.
351 * Hwloc should virtually always be able to detect numa information, but if
352 * there is only a single numa node in the system it is not reported at all.
353 * In this case we create a single numa node covering all cores.
355 * This function uses the basic socket/core/thread information detected by
356 * parseHwLocSocketsCoresThreads(), which means that routine must have
357 * completed successfully before calling this one. If this is not the case,
358 * you will get an error return code.
360 * \return If the data found makes sense (either in the numa node or the
363 bool parseHwLocNuma(hwloc_topology_t topo, HardwareTopology::Machine* machine)
365 const hwloc_obj* root = hwloc_get_root_obj(topo);
366 std::vector<const hwloc_obj*> hwlocNumaNodes =
367 getHwLocDescendantsByType(topo, root, HWLOC_OBJ_NUMANODE);
368 bool topologyOk = true;
370 if (!hwlocNumaNodes.empty())
372 machine->numa.nodes.resize(hwlocNumaNodes.size());
374 for (std::size_t i = 0; i < hwlocNumaNodes.size(); i++)
376 machine->numa.nodes[i].id = hwlocNumaNodes[i]->logical_index;
377 machine->numa.nodes[i].memory = getHwLocObjectMemory(hwlocNumaNodes[i]);
379 machine->numa.nodes[i].logicalProcessorId.clear();
381 // 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
382 # if GMX_HWLOC_API_VERSION_IS_2XX
383 std::vector<const hwloc_obj*> hwlocPUs =
384 getHwLocDescendantsByType(topo, hwlocNumaNodes[i]->parent, HWLOC_OBJ_PU);
386 std::vector<const hwloc_obj*> hwlocPUs =
387 getHwLocDescendantsByType(topo, hwlocNumaNodes[i], HWLOC_OBJ_PU);
389 for (auto& p : hwlocPUs)
391 machine->numa.nodes[i].logicalProcessorId.push_back(p->os_index);
393 GMX_RELEASE_ASSERT(p->os_index < machine->logicalProcessors.size(),
394 "OS index of PU in hwloc larger than processor count");
396 machine->logicalProcessors[p->os_index].numaNodeId = static_cast<int>(i);
397 std::size_t s = machine->logicalProcessors[p->os_index].socketRankInMachine;
398 std::size_t c = machine->logicalProcessors[p->os_index].coreRankInSocket;
400 GMX_RELEASE_ASSERT(s < machine->sockets.size(),
401 "Socket index in logicalProcessors larger than socket count");
402 GMX_RELEASE_ASSERT(c < machine->sockets[s].cores.size(),
403 "Core index in logicalProcessors larger than core count");
404 // Set numaNodeId in core too
405 machine->sockets[s].cores[c].numaNodeId = i;
408 // Getting the distance matrix
409 # if GMX_HWLOC_API_VERSION_IS_2XX
410 // with hwloc api v. 2.x.x, distances are no longer directly accessible. Need to retrieve and release hwloc_distances_s object
411 // In addition, there can now be multiple types of distances, ie latency, bandwidth. We look only for latency, but have to check
412 // if multiple distance matrices are returned.
414 // If only 1 numa node exists, the v2.x.x hwloc api won't have a distances matrix, set manually
415 if (hwlocNumaNodes.size() == 1)
417 machine->numa.relativeLatency = { { 1.0 } };
421 hwloc_distances_s* dist;
422 // Set the number of distance matrices to return (1 in our case, but hwloc 2.x.x allows
423 // for multiple distances types and therefore multiple distance matrices)
425 hwloc_distances_get(topo, &nr, &dist, HWLOC_DISTANCES_KIND_MEANS_LATENCY, 0);
426 // If no distances were found, nr will be 0, otherwise distances will be populated with
427 // 1 hwloc_distances_s object
428 if (nr > 0 && dist->nbobjs == hwlocNumaNodes.size())
431 machine->numa.relativeLatency.resize(dist->nbobjs);
432 for (std::size_t i = 0; i < dist->nbobjs; i++)
434 machine->numa.relativeLatency[i].resize(dist->nbobjs);
435 for (std::size_t j = 0; j < dist->nbobjs; j++)
437 machine->numa.relativeLatency[i][j] = dist->values[i * dist->nbobjs + j];
445 hwloc_distances_release(topo, dist);
448 // hwloc-2.x provides latencies as integers, but to make things more similar to the case of
449 // a single numa node as well as hwloc-1.x, we rescale to relative floating-point values and
450 // also set the largest relative latency value.
452 // find smallest value in matrix
453 float minLatency = std::numeric_limits<float>::max(); // large number
454 float maxLatency = std::numeric_limits<float>::min(); // 0.0
455 for (const auto& v : machine->numa.relativeLatency)
457 auto result = std::minmax_element(v.begin(), v.end());
458 minLatency = std::min(minLatency, *result.first);
459 maxLatency = std::max(maxLatency, *result.second);
463 for (auto& v : machine->numa.relativeLatency)
465 std::transform(v.begin(), v.end(), v.begin(),
466 std::bind(std::multiplies<float>(), std::placeholders::_1, 1.0 / minLatency));
468 machine->numa.baseLatency = 1.0; // latencies still do not have any units in hwloc-2.x
469 machine->numa.maxRelativeLatency = maxLatency / minLatency;
471 # else // GMX_HWLOC_API_VERSION_IS_2XX == false, hwloc api is 1.x.x
472 int depth = hwloc_get_type_depth(topo, HWLOC_OBJ_NUMANODE);
473 const struct hwloc_distances_s* dist = hwloc_get_whole_distance_matrix_by_depth(topo, depth);
474 if (dist != nullptr && dist->nbobjs == hwlocNumaNodes.size())
476 machine->numa.baseLatency = dist->latency_base;
477 machine->numa.maxRelativeLatency = dist->latency_max;
478 machine->numa.relativeLatency.resize(dist->nbobjs);
479 for (std::size_t i = 0; i < dist->nbobjs; i++)
481 machine->numa.relativeLatency[i].resize(dist->nbobjs);
482 for (std::size_t j = 0; j < dist->nbobjs; j++)
484 machine->numa.relativeLatency[i][j] = dist->latency[i * dist->nbobjs + j];
492 # endif // end GMX_HWLOC_API_VERSION_IS_2XX == false
495 // Deals with the case of no numa nodes found.
496 # if GMX_HWLOC_API_VERSION_IS_2XX
497 // If the hwloc version is 2.x.x, and there is no numa node, something went wrong
503 // No numa nodes found. Use the entire machine as a numa node.
504 // Note that this should only be the case with hwloc api v 1.x.x,
505 // a numa node is assigned to the machine by default in v 2.x.x
506 const hwloc_obj* const hwlocMachine = hwloc_get_next_obj_by_type(topo, HWLOC_OBJ_MACHINE, nullptr);
508 if (hwlocMachine != nullptr)
510 machine->numa.nodes.resize(1);
511 machine->numa.nodes[0].id = 0;
512 machine->numa.nodes[0].memory = hwlocMachine->memory.total_memory;
513 machine->numa.baseLatency = 10;
514 machine->numa.maxRelativeLatency = 1;
515 machine->numa.relativeLatency = { { 1.0 } };
517 for (int i = 0; i < machine->logicalProcessorCount; i++)
519 machine->numa.nodes[0].logicalProcessorId.push_back(i);
521 for (auto& l : machine->logicalProcessors)
525 for (auto& s : machine->sockets)
527 for (auto& c : s.cores)
538 # endif // end if not GMX_HWLOC_API_VERSION_IS_2XX
541 machine->numa.nodes.clear();
546 /*! \brief Read PCI device information from hwloc topology
548 * \param topo hwloc topology handle that has been initialized and loaded
549 * \param machine Pointer to the machine structure in the HardwareTopology
550 * class, where PCI device information will be filled.
552 * \return If any devices were found
554 bool parseHwLocDevices(hwloc_topology_t topo, HardwareTopology::Machine* machine)
556 const hwloc_obj* root = hwloc_get_root_obj(topo);
557 std::vector<const hwloc_obj*> pcidevs = getHwLocDescendantsByType(topo, root, HWLOC_OBJ_PCI_DEVICE);
559 for (auto& p : pcidevs)
561 # if GMX_HWLOC_API_VERSION_IS_2XX
562 const hwloc_obj* ancestor = nullptr;
563 // Numa nodes not directly part of tree. Walk up the tree until we find an ancestor with a numa node
564 hwloc_obj_t parent = p->parent;
565 while (parent && !parent->memory_arity)
567 parent = parent->parent;
571 ancestor = parent->memory_first_child;
573 # else // GMX_HWLOC_API_VERSION_IS_2XX = false, api v 1.x.x
574 // numa nodes are normal part of tree, can use hwloc ancestor function
575 const hwloc_obj* const ancestor =
576 hwloc_get_ancestor_obj_by_type(topo, HWLOC_OBJ_NUMANODE, const_cast<hwloc_obj_t>(p));
577 # endif // end if GMX_HWLOC_API_VERSION_IS_2XX
579 if (ancestor != nullptr)
581 numaId = ancestor->logical_index;
585 // If we only have a single numa node we belong to it, otherwise set it to -1 (unknown)
586 numaId = (machine->numa.nodes.size() == 1) ? 0 : -1;
589 GMX_RELEASE_ASSERT(p->attr, "Attributes should not be NULL for hwloc PCI object");
591 machine->devices.push_back({ p->attr->pcidev.vendor_id, p->attr->pcidev.device_id,
592 p->attr->pcidev.class_id, p->attr->pcidev.domain,
593 p->attr->pcidev.bus, p->attr->pcidev.dev, p->attr->pcidev.func,
596 return !pcidevs.empty();
599 void parseHwLoc(HardwareTopology::Machine* machine, HardwareTopology::SupportLevel* supportLevel, bool* isThisSystem)
601 hwloc_topology_t topo;
603 // Initialize a hwloc object, set flags to request IO device information too,
604 // try to load the topology, and get the root object. If either step fails,
605 // return that we do not have any support at all from hwloc.
606 if (hwloc_topology_init(&topo) != 0)
608 hwloc_topology_destroy(topo);
609 return; // SupportLevel::None.
612 // Flags to look for io devices
613 # if GMX_HWLOC_API_VERSION_IS_2XX
615 (hwloc_get_api_version() >= 0x20000),
616 "Mismatch between hwloc headers and library, using v2 headers with v1 library");
617 hwloc_topology_set_io_types_filter(topo, HWLOC_TYPE_FILTER_KEEP_IMPORTANT);
620 (hwloc_get_api_version() < 0x20000),
621 "Mismatch between hwloc headers and library, using v1 headers with v2 library");
622 hwloc_topology_set_flags(topo, HWLOC_TOPOLOGY_FLAG_IO_DEVICES);
625 if (hwloc_topology_load(topo) != 0 || hwloc_get_root_obj(topo) == nullptr)
627 hwloc_topology_destroy(topo);
628 return; // SupportLevel::None.
631 // If we get here, we can get a valid root object for the topology
632 *isThisSystem = hwloc_topology_is_thissystem(topo) != 0;
634 // Parse basic information about sockets, cores, and hardware threads
635 if (parseHwLocSocketsCoresThreads(topo, machine))
637 *supportLevel = HardwareTopology::SupportLevel::Basic;
641 hwloc_topology_destroy(topo);
642 return; // SupportLevel::None.
645 // Get information about cache and numa nodes
646 if (parseHwLocCache(topo, machine) && parseHwLocNuma(topo, machine))
648 *supportLevel = HardwareTopology::SupportLevel::Full;
652 hwloc_topology_destroy(topo);
653 return; // SupportLevel::Basic.
657 if (parseHwLocDevices(topo, machine))
659 *supportLevel = HardwareTopology::SupportLevel::FullWithDevices;
662 hwloc_topology_destroy(topo);
663 // SupportLevel::Full or SupportLevel::FullWithDevices.
668 /*! \brief Try to detect the number of logical processors.
670 * \return The number of hardware processing units, or 0 if it fails.
672 int detectLogicalProcessorCount()
677 #if GMX_NATIVE_WINDOWS
680 GetSystemInfo(&sysinfo);
681 count = sysinfo.dwNumberOfProcessors;
682 #elif defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
683 // We are probably on Unix. Check if we have the argument to use before executing any calls
684 count = sysconf(_SC_NPROCESSORS_ONLN);
686 count = 0; // Neither windows nor Unix.
696 HardwareTopology HardwareTopology::detect()
698 HardwareTopology result;
701 parseHwLoc(&result.machine_, &result.supportLevel_, &result.isThisSystem_);
704 // If something went wrong in hwloc (or if it was not present) we might
705 // have more information in cpuInfo
706 if (result.supportLevel_ < SupportLevel::Basic)
708 // There might be topology information in cpuInfo
709 parseCpuInfo(&result.machine_, &result.supportLevel_);
711 // If we did not manage to get anything from either hwloc or cpuInfo, find the cpu count at least
712 if (result.supportLevel_ == SupportLevel::None)
714 // No topology information; try to detect the number of logical processors at least
715 result.machine_.logicalProcessorCount = detectLogicalProcessorCount();
716 if (result.machine_.logicalProcessorCount > 0)
718 result.supportLevel_ = SupportLevel::LogicalProcessorCount;
724 HardwareTopology::Machine::Machine()
726 logicalProcessorCount = 0;
727 numa.baseLatency = 0.0;
728 numa.maxRelativeLatency = 0.0;
732 HardwareTopology::HardwareTopology() :
733 supportLevel_(SupportLevel::None),
739 HardwareTopology::HardwareTopology(int logicalProcessorCount) :
740 supportLevel_(SupportLevel::None),
744 if (logicalProcessorCount > 0)
746 machine_.logicalProcessorCount = logicalProcessorCount;
747 supportLevel_ = SupportLevel::LogicalProcessorCount;
751 int HardwareTopology::numberOfCores() const
753 if (supportLevel() >= SupportLevel::Basic)
755 // We assume all sockets have the same number of cores as socket 0.
756 // Since topology information is present, we can assume there is at least one socket.
757 return machine().sockets.size() * machine().sockets[0].cores.size();
759 else if (supportLevel() >= SupportLevel::LogicalProcessorCount)
761 return machine().logicalProcessorCount;