b2dcf352cb8fe65511f50cc06babb039e7216567
[alexxy/gromacs.git] / src / gromacs / hardware / hardwaretopology.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2015,2016, 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.
8  *
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.
13  *
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.
18  *
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.
23  *
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.
31  *
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.
34  */
35 /*! \libinternal \file
36  * \brief
37  * Declares gmx::HardwareTopology
38  *
39  * \author Erik Lindahl <erik.lindahl@gmail.com>
40  * \inlibraryapi
41  * \ingroup module_hardware
42  */
43 #ifndef GMX_HARDWARE_HARDWARETOPOLOGY_H
44 #define GMX_HARDWARE_HARDWARETOPOLOGY_H
45
46 #include <cstdint>
47
48 #include <vector>
49
50 namespace gmx
51 {
52
53 /*! \libinternal \brief Information about sockets, cores, threads, numa, caches
54  *
55  * This class is the main GROMACS interface to provide information about the
56  * hardware of the system we are running on. Internally, it uses either
57  * hwloc for full or almost-full information, or a fallback implementation
58  * that relies on CpuInfo on x86.
59  *
60  * You should always use this class (rather than CpuInfo directly) to query
61  * the hardware layout in user code. Note that you cannot rely on any
62  * information being present, but you must check with the supportLevel()
63  * method before trying to access any information.
64  */
65 class HardwareTopology
66 {
67     public:
68
69         /*! \brief Amount of topology information present (incremental) */
70         enum class SupportLevel
71         {
72             None,                  //!< No hardware information whatsoever. Sorry.
73             LogicalProcessorCount, //!< Only machine().logicalProcessorCount is valid
74             Basic,                 //!< Socket, core and hardware thread info
75             Full,                  //!< Cache, memory and numa node info
76             FullWithDevices        //!< Information about devices on the PCI bus
77         };
78
79         /*! \libinternal \brief Information about a single cache level */
80         struct Cache
81         {
82             int                    level;          //!< Level relative to core (starts at 1)
83             std::size_t            size;           //!< size in bytes, 0 if unknown
84             int                    linesize;       //!< size of each cache line in bytes, 0 if unknown
85             int                    associativity;  //!< associativity, -1 means fully associative
86             int                    shared;         //!< Number of logical processors sharing this cache
87         };
88
89         /*! \libinternal \brief Information about a single hardware thread in a core
90          *
91          * The id of the thread typically increases continuously as you walk
92          * through sockets and cores in order of their ids. In general, this can
93          * be different from the logical processor id provided by the operating
94          * system. To achieve better load balancing when using SMT, Linux
95          * typically assigns logical processors in a round-robin fashion
96          * over all cores.
97          */
98         struct HWThread
99         {
100             int                    id;                 //!< Absolute id of this thread in hardware topology
101             int                    logicalProcessorId; //!< Id of the operating system logical processor
102         };
103
104         /*! \libinternal \brief Information about a single core in a socket */
105         struct Core
106         {
107             int                    id;              //!< Absolute id of this core in hardware topology
108             int                    numaNodeId;      //!< id of the numa node of this core
109             std::vector<HWThread>  hwThreads;       //!< All the hardware threads in this core
110         };
111
112         /*! \libinternal \brief Information about a single socket in the system */
113         struct Socket
114         {
115             int                    id;             //!< Absolute id of this socket in hardware topology
116             std::vector<Core>      cores;          //!< All the cores in this socket
117         };
118
119         /*! \libinternal \brief Information about each numa node in system */
120         struct NumaNode
121         {
122             int                    id;                 //!< Absolute id of numa node in hardware topology
123             std::size_t            memory;             //!< Total detected memory in bytes
124             std::vector<int>       logicalProcessorId; //!< Vector of all the logical processors in this node
125         };
126
127         /*! \libinternal \brief Information about a single numa node */
128         struct Numa
129         {
130             std::vector<NumaNode>               nodes;              //!< Information about each numa node
131             float                               baseLatency;        //!< Scale factor for relative latencies
132             std::vector< std::vector<float> >   relativeLatency;    //!< 2D matrix of relative latencies between nodes
133             float                               maxRelativeLatency; //!< Largest relative latency
134         };
135
136         /*! \libinternal \brief Information about a single PCI device.
137          *
138          *  \note On many systems the PCI bus is not directly connected to any numa node.
139          *        For these systems the numaNodeId will be -1, so you cannot rely on this
140          *        number reflecting a specific numa node.
141          */
142         struct Device
143         {
144             std::uint16_t          vendorId;    //!< Vendor identification
145             std::uint16_t          deviceId;    //!< Vendor-specific device identification
146             std::uint16_t          classId;     //!< class (high 8 bits) and subclass (low 8 bits)
147             std::uint16_t          domain;      //!< Domain, usually 0 for PCI bus
148             std::uint8_t           bus;         //!< Bus number in domain
149             std::uint8_t           dev;         //!< Device on bus
150             std::uint8_t           func;        //!< Function id for multi-function devices
151             int                    numaNodeId;  //!< Numa node, -1 if the bus is not located inside a node
152         };
153
154         /*! \libinternal \brief Information about socket, core and hwthread for a logical processor */
155         struct LogicalProcessor
156         {
157             int                    socketRankInMachine; //!< Index of socket in machine
158             int                    coreRankInSocket;    //!< Index of core in socket
159             int                    hwThreadRankInCore;  //!< Index of hardware thread in core
160             int                    numaNodeId;          //!< Index of numa node
161         };
162
163         /*! \libinternal \brief Hardware topology information about the entire machine
164          *
165          * The machine structure is a tree with top-down information about all
166          * sockets, cores, and hardware threads in the system. For example, an
167          * operating system logical processor index can be found as
168          * machine.socket[0].core[1].hwthread[2].logicalProcessorId.
169          * In some cases you might need the opposite lookup, i.e. the physical
170          * hardware data for a specific logical processor. This is present in the
171          * logicalProcessor vector for convenience.
172          *
173          * \note The logicalProcessor vector will only have non-zero length if the
174          *       support level is SupportLevel::Basic or higher. You cannot use the
175          *       size of this vector to query the number of logical processors on
176          *       lower support levels.
177          */
178         struct Machine
179         {
180             Machine();
181
182             int                            logicalProcessorCount; //!< Number of logical processors in system
183             std::vector<LogicalProcessor>  logicalProcessors;     //!< Map logical processors to socket/core
184             std::vector<Socket>            sockets;               //!< All the sockets in the system
185             std::vector<Cache>             caches;                //!< Caches in increasing level order
186             Numa                           numa;                  //!< Structure with all numa information
187             std::vector<Device>            devices;               //!< Devices on PCI bus
188         };
189
190     public:
191
192         /*! \brief Detects the hardware topology. */
193         static HardwareTopology detect();
194
195         /*! \brief Creates a topology with given number of logical cores.
196          *
197          * The support level will be either None or LogicalProcessorCount.
198          *
199          * Intended for testing of code that uses the hardware topology.
200          */
201         explicit HardwareTopology(int logicalProcessorCount);
202
203         /*! \brief Check what topology information that is available and valid
204          *
205          *  The amount of hardware topology information that can be detected depends
206          *  on both the hardware and whether GROMACS was linked with the external
207          *  hwloc library. You cannot assume that any information is present,
208          *  although we can almost always provide the number of logical processors.
209          *  On x86 we can usually get basic information about how sockets, cores
210          *  and hardware threads are ordered even without hwloc.
211          *  With the hwloc library we can usually also get information about cache,
212          *  memory and concepts such as core groups and ccNUMA nodes.
213          *  Finally, if hwloc was built with support for libpci we can also
214          *  detect how the PCI devices are connected.
215          */
216         SupportLevel
217         supportLevel() const { return supportLevel_; }
218
219         /*! \brief Return true if we actually detected hardware.
220          *
221          *  \return This method will normally return true, when we actually ran
222          *          the hardware detection as part of this process to construct
223          *          the object. It will be false when the object was constructed
224          *          by reading a cached XML file, or possibly generated from
225          *          synthetic data.
226          */
227         bool
228         isThisSystem() const { return isThisSystem_; }
229
230         /*! \brief Return the machine topology tree
231          *
232          *  You can always call this routine, but be aware that some or all contents
233          *  will not be valid unless supportLevel() returns a sufficient level.
234          *
235          *  - With SupportLevel::LogicalProcessorCount, only the field
236          *    machine.logicalProcessorCount is valid.
237          *  - With SupportLevel::Basic, you can access the vectors of sockets,
238          *    cores, and hardware threads, and query what logical processorId
239          *    each hardware thread corresponds to.
240          *  - SupportLevel::Full adds cache, memory and ccNUMA information.
241          *  - SupportLevel::FullWithDevices also adds the PCI express bus.
242          *
243          *  While data that is not valid has been initialized to special values,
244          *  you should not rely on those but query the supportLevel() method before
245          *  accessing it.
246          */
247         const Machine &
248         machine() const { return machine_; }
249
250         /*! \brief Returns the number of cores.
251          *
252          * You can always call this routine, but if sufficient support is not
253          * available, it may return the logical processor count or zero instead
254          * of the physical core count.
255          */
256         int numberOfCores() const;
257
258     private:
259
260         HardwareTopology();
261
262         SupportLevel        supportLevel_; //!< Available topology information
263         Machine             machine_;      //!< The machine map
264         bool                isThisSystem_; //!< Machine map is real (vs. cached/synthetic)
265 };
266
267 }
268
269 #endif // GMX_HARDWARE_HARDWARETOPOLOGY_H