NVIDIA Volta performance tweaks
[alexxy/gromacs.git] / src / gromacs / hardware / cpuinfo.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014,2015,2016,2017, 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
36 /*! \internal \file
37  * \brief
38  * Implements gmx::CpuInfo.
39  *
40  * We need to be able to compile this file in stand-alone mode to use basic
41  * CPU feature detection to set the SIMD acceleration and similar things in
42  * CMake, while we still want to use more features that enable topology
43  * detection when config.h is present.
44  *
45  * We solve this by skipping the advanced stuff when the preprocessor
46  * macro GMX_CPUINFO_STANDALONE is defined. In this case you likely also need to
47  * define GMX_X86_GCC_INLINE_ASM if you are on x86; without inline assembly
48  * support it is not possible to perform the actual detection on Linux/Mac.
49  * Since these macros are specific to this file, they do not use the GMX prefix.
50  *
51  * The remaining defines (GMX_NATIVE_WINDOWS,HAVE_UNISTD_H,HAVE_SCHED_H,
52  * HAVE_SYSCONF, HAVE_SCHED_AFFINITY) are only used to determine the topology on
53  * 86, and for this we rely on including config.h.
54  *
55  * \author Erik Lindahl <erik.lindahl@gmail.com>
56  * \ingroup module_hardware
57  */
58
59 #ifndef GMX_CPUINFO_STANDALONE
60 #    include "gmxpre.h"
61 #endif
62
63 #include "cpuinfo.h"
64
65 #ifndef GMX_CPUINFO_STANDALONE
66 #    include "config.h"
67 #else
68 #    define GMX_NATIVE_WINDOWS 0
69 #endif
70
71 #if defined _MSC_VER
72 #    include <intrin.h> // __cpuid()
73 #endif
74
75 #if GMX_NATIVE_WINDOWS
76 #    include <windows.h>    // sysinfo(), necessary for topology stuff
77 #endif
78
79 #ifdef HAVE_SCHED_H
80 #    include <sched.h>      // sched_getaffinity(), sched_setaffinity()
81 #endif
82 #ifdef HAVE_UNISTD_H
83 #    include <unistd.h>     // sysconf()
84 #endif
85
86 #include <cctype>
87 #include <cstdlib>
88
89 #include <algorithm>
90 #include <fstream>
91 #include <map>
92 #include <set>
93 #include <sstream>
94 #include <string>
95
96 #ifdef GMX_CPUINFO_STANDALONE
97 #    define gmx_unused
98 #else
99 #    include "gromacs/utility/basedefinitions.h"
100 #endif
101
102 namespace gmx
103 {
104
105 namespace
106 {
107
108 /*! \cond internal */
109
110 /******************************************************************************
111  *                                                                            *
112  *   Utility functions to make this file independent of the GROMACS library   *
113  *                                                                            *
114  ******************************************************************************/
115
116 /*! \brief Remove initial and trailing whitespace from string
117  *
118  *  \param s  Pointer to string where whitespace will be removed
119  */
120 void
121 trimString(std::string * s)
122 {
123     // heading
124     s->erase(s->begin(), std::find_if(s->begin(), s->end(), [](char &c) -> bool { return !std::isspace(c); }));
125     // trailing
126     s->erase(std::find_if(s->rbegin(), s->rend(), [](char &c) -> bool { return !std::isspace(c); }).base(), s->end());
127 }
128
129
130 /******************************************************************************
131  *                                                                            *
132  *                         x86 detection functions                            *
133  *                                                                            *
134  ******************************************************************************/
135
136 /*! \brief execute x86 cpuid instructions with custom level and extended level
137  *
138  *  \param level   The main cpuid level (input argument for eax register)
139  *  \param ecxval  Extended level (input argument for ecx register)
140  *  \param eax     Output in eax register
141  *  \param ebx     Output in ebx register
142  *  \param ecx     Output in ecx register
143  *  \param edx     Output in edx register
144  *
145  *  \return 0 on success, or non-zero if the instruction could not execute.
146  */
147 int
148 executeX86CpuID(unsigned int     gmx_unused level,
149                 unsigned int     gmx_unused ecxval,
150                 unsigned int *              eax,
151                 unsigned int *              ebx,
152                 unsigned int *              ecx,
153                 unsigned int *              edx)
154 {
155 #if defined __i386__ || defined __i386 || defined _X86_ || defined _M_IX86 || \
156     defined __x86_64__ || defined __amd64__ || defined _M_X64 || defined _M_AMD64
157
158 #    if defined __GNUC__ || GMX_X86_GCC_INLINE_ASM
159
160     // any compiler that understands gcc inline assembly
161     *eax = level;
162     *ecx = ecxval;
163     *ebx = 0;
164     *edx = 0;
165
166 #        if (defined __i386__ || defined __i386 || defined _X86_ || defined _M_IX86) && defined(__PIC__)
167     // Avoid clobbering the global offset table in 32-bit pic code (ebx register)
168     __asm__ __volatile__ ("xchgl %%ebx, %1  \n\t"
169                           "cpuid            \n\t"
170                           "xchgl %%ebx, %1  \n\t"
171                           : "+a" (*eax), "+r" (*ebx), "+c" (*ecx), "+d" (*edx));
172 #        else
173     // i386 without PIC, or x86-64. Things are easy and we can clobber any reg we want
174     __asm__ __volatile__ ("cpuid            \n\t"
175                           : "+a" (*eax), "+b" (*ebx), "+c" (*ecx), "+d" (*edx));
176 #        endif
177     return 0;
178
179 #    elif defined _MSC_VER
180
181     // MSVC (and icc on windows) on ia32 or x86-64
182     int cpuInfo[4];
183     __cpuidex(cpuInfo, level, ecxval);
184     *eax = static_cast<unsigned int>(cpuInfo[0]);
185     *ebx = static_cast<unsigned int>(cpuInfo[1]);
186     *ecx = static_cast<unsigned int>(cpuInfo[2]);
187     *edx = static_cast<unsigned int>(cpuInfo[3]);
188     return 0;
189
190 #    else
191
192     // We are on x86, but without compiler support for cpuid if we get here
193     *eax = 0;
194     *ebx = 0;
195     *ecx = 0;
196     *edx = 0;
197     return 1;
198
199 #    endif  // check for inline asm on x86
200
201 #else
202
203     // We are not on x86
204     *eax = 0;
205     *ebx = 0;
206     *ecx = 0;
207     *edx = 0;
208     return 1;
209
210 #endif      // x86
211 }
212
213
214 /*! \brief Detect x86 vendors by using the cpuid assembly instructions
215  *
216  *  If support for the cpuid instruction is present, we check for Intel
217  *  or AMD vendors.
218  *
219  *  \return gmx::CpuInfo::Vendor::Intel, gmx::CpuInfo::Vendor::Amd. If neither
220  *          Intel nor Amd can be identified, or if the code fails to execute,
221  *          gmx::CpuInfo::Vendor::Unknown is returned.
222  */
223 CpuInfo::Vendor
224 detectX86Vendor()
225 {
226     unsigned int    eax, ebx, ecx, edx;
227     CpuInfo::Vendor v = CpuInfo::Vendor::Unknown;
228
229     if (executeX86CpuID(0x0, 0, &eax, &ebx, &ecx, &edx) == 0)
230     {
231         if (ebx == 0x756e6547 && ecx == 0x6c65746e && edx == 0x49656e69)
232         {
233             v = CpuInfo::Vendor::Intel; // ebx=='uneG', ecx=='letn', edx=='Ieni'
234         }
235         else if (ebx == 0x68747541 && ecx == 0x444d4163 && edx == 0x69746e65)
236         {
237             v = CpuInfo::Vendor::Amd; // ebx=='htuA', ecx=='DMAc', edx=='itne'
238         }
239     }
240     return v;
241 }
242
243 /*! \brief Simple utility function to set/clear feature in a set
244  *
245  *  \param featureSet    Pointer to the feature set to update
246  *  \param feature       The specific feature to set/clear
247  *  \param registerValue Register value (returned from cpuid)
248  *  \param bit           Bit to check in registerValue. The feature will be
249  *                       added to the featureSet if this bit is set.
250  *
251  *  \note Nothing is done if the bit is not set. In particular, this will not
252  *        erase anything if the feature already exists in the set.
253  */
254 void
255 setFeatureFromBit(std::set<CpuInfo::Feature> *   featureSet,
256                   CpuInfo::Feature               feature,
257                   unsigned int                   registerValue,
258                   unsigned char                  bit)
259 {
260     if (registerValue & (1 << bit))
261     {
262         featureSet->insert(feature);
263     }
264 }
265
266 /*! \brief Process x86 cpuinfo features that are common to Intel and AMD CPUs
267  *
268  *  \param[out] brand      String where to write the x86 brand string
269  *  \param[out] family     Major version of processor
270  *  \param[out] model      Middle version of processor
271  *  \param[out] stepping   Minor version of processor
272  *  \param[out] features   Feature set where supported features are inserted
273  */
274 void
275 detectX86Features(std::string *                  brand,
276                   int *                          family,
277                   int *                          model,
278                   int *                          stepping,
279                   std::set<CpuInfo::Feature> *   features)
280 {
281     unsigned int eax, ebx, ecx, edx;
282
283     // Return if we cannot execute any levels
284     if (executeX86CpuID(0x0, 0, &eax, &ebx, &ecx, &edx) != 0)
285     {
286         return;
287     }
288     unsigned int maxStdLevel = eax;
289
290     if (maxStdLevel >= 0x1)
291     {
292         executeX86CpuID(0x1, 0, &eax, &ebx, &ecx, &edx);
293
294         *family   = ((eax & 0x0ff00000) >> 20) + ((eax & 0x00000f00) >> 8);
295         *model    = ((eax & 0x000f0000) >> 12) + ((eax & 0x000000f0) >> 4);
296         *stepping = (eax & 0x0000000f);
297
298         setFeatureFromBit(features, CpuInfo::Feature::X86_Sse3,     ecx,  0 );
299         setFeatureFromBit(features, CpuInfo::Feature::X86_Pclmuldq, ecx,  1 );
300         setFeatureFromBit(features, CpuInfo::Feature::X86_Ssse3,    ecx,  9 );
301         setFeatureFromBit(features, CpuInfo::Feature::X86_Fma,      ecx, 12 );
302         setFeatureFromBit(features, CpuInfo::Feature::X86_Cx16,     ecx, 13 );
303         setFeatureFromBit(features, CpuInfo::Feature::X86_Pdcm,     ecx, 15 );
304         setFeatureFromBit(features, CpuInfo::Feature::X86_Pcid,     ecx, 17 );
305         setFeatureFromBit(features, CpuInfo::Feature::X86_Sse4_1,   ecx, 19 );
306         setFeatureFromBit(features, CpuInfo::Feature::X86_Sse4_2,   ecx, 20 );
307         setFeatureFromBit(features, CpuInfo::Feature::X86_X2Apic,   ecx, 21 );
308         setFeatureFromBit(features, CpuInfo::Feature::X86_Popcnt,   ecx, 23 );
309         setFeatureFromBit(features, CpuInfo::Feature::X86_Tdt,      ecx, 24 );
310         setFeatureFromBit(features, CpuInfo::Feature::X86_Aes,      ecx, 25 );
311         setFeatureFromBit(features, CpuInfo::Feature::X86_Avx,      ecx, 28 );
312         setFeatureFromBit(features, CpuInfo::Feature::X86_F16C,     ecx, 29 );
313         setFeatureFromBit(features, CpuInfo::Feature::X86_Rdrnd,    ecx, 30 );
314
315         setFeatureFromBit(features, CpuInfo::Feature::X86_Pse,      edx,  3 );
316         setFeatureFromBit(features, CpuInfo::Feature::X86_Msr,      edx,  5 );
317         setFeatureFromBit(features, CpuInfo::Feature::X86_Cx8,      edx,  8 );
318         setFeatureFromBit(features, CpuInfo::Feature::X86_Apic,     edx,  9 );
319         setFeatureFromBit(features, CpuInfo::Feature::X86_Cmov,     edx, 15 );
320         setFeatureFromBit(features, CpuInfo::Feature::X86_Clfsh,    edx, 19 );
321         setFeatureFromBit(features, CpuInfo::Feature::X86_Mmx,      edx, 23 );
322         setFeatureFromBit(features, CpuInfo::Feature::X86_Sse2,     edx, 26 );
323         setFeatureFromBit(features, CpuInfo::Feature::X86_Htt,      edx, 28 );
324     }
325
326     if (maxStdLevel >= 0x7)
327     {
328         executeX86CpuID(0x7, 0, &eax, &ebx, &ecx, &edx);
329
330         setFeatureFromBit(features, CpuInfo::Feature::X86_Hle,      ebx,  4 );
331         setFeatureFromBit(features, CpuInfo::Feature::X86_Avx2,     ebx,  5 );
332         setFeatureFromBit(features, CpuInfo::Feature::X86_Rtm,      ebx, 11 );
333         setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512F,  ebx, 16 );
334         setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512PF, ebx, 26 );
335         setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512ER, ebx, 27 );
336         setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512CD, ebx, 28 );
337         setFeatureFromBit(features, CpuInfo::Feature::X86_Sha,      ebx, 29 );
338         setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512BW, ebx, 30 );
339         setFeatureFromBit(features, CpuInfo::Feature::X86_Avx512VL, ebx, 31 );
340     }
341
342     // Check whether Hyper-threading is really possible to enable in the hardware,
343     // not just technically supported by this generation of processors
344     if (features->count(CpuInfo::Feature::X86_Htt) && maxStdLevel >= 0x4)
345     {
346         executeX86CpuID(0x1, 0, &eax, &ebx, &ecx, &edx);
347         unsigned int maxLogicalCores  = (ebx >> 16) & 0x0ff;
348         executeX86CpuID(0x4, 0, &eax, &ebx, &ecx, &edx);
349         unsigned int maxPhysicalCores = ((eax >> 26) & 0x3f) + 1;
350         if (maxLogicalCores/maxPhysicalCores < 2)
351         {
352             features->erase(CpuInfo::Feature::X86_Htt);
353         }
354     }
355
356     if (executeX86CpuID(0x80000000, 0, &eax, &ebx, &ecx, &edx) != 0)
357     {
358         // No point in continuing if we don't support any extended levels
359         return;
360     }
361     unsigned int maxExtLevel = eax;
362
363     if (maxExtLevel >= 0x80000001)
364     {
365         executeX86CpuID(0x80000001, 0, &eax, &ebx, &ecx, &edx);
366
367         setFeatureFromBit(features, CpuInfo::Feature::X86_Lahf,        ecx,  0 );
368         setFeatureFromBit(features, CpuInfo::Feature::X86_Sse4A,       ecx,  6 );
369         setFeatureFromBit(features, CpuInfo::Feature::X86_MisalignSse, ecx,  7 );
370         setFeatureFromBit(features, CpuInfo::Feature::X86_Xop,         ecx, 11 );
371         setFeatureFromBit(features, CpuInfo::Feature::X86_Fma4,        ecx, 16 );
372         setFeatureFromBit(features, CpuInfo::Feature::X86_PDPE1GB,     edx, 26 );
373         setFeatureFromBit(features, CpuInfo::Feature::X86_Rdtscp,      edx, 27 );
374     }
375
376     if (maxExtLevel >= 0x80000005)
377     {
378         // Get the x86 CPU brand string (3 levels, 16 bytes in each)
379         brand->clear();
380         for (unsigned int level = 0x80000002; level < 0x80000005; level++)
381         {
382             executeX86CpuID(level, 0, &eax, &ebx, &ecx, &edx);
383             // Add eax, ebx, ecx, edx contents as 4 chars each to the brand string
384             brand->append(reinterpret_cast<const char *>(&eax), sizeof(eax));
385             brand->append(reinterpret_cast<const char *>(&ebx), sizeof(ebx));
386             brand->append(reinterpret_cast<const char *>(&ecx), sizeof(ecx));
387             brand->append(reinterpret_cast<const char *>(&edx), sizeof(edx));
388         }
389         trimString(brand);
390     }
391
392     if (maxExtLevel >= 0x80000007)
393     {
394         executeX86CpuID(0x80000007, 0, &eax, &ebx, &ecx, &edx);
395
396         setFeatureFromBit(features, CpuInfo::Feature::X86_NonstopTsc, edx,  8 );
397     }
398 }
399
400
401 /*! \brief Return a vector with x86 APIC IDs for all threads
402  *
403  *  \param haveX2Apic  True if the processors supports x2APIC, otherwise vanilla APIC.
404  *
405  *  \returns A new std::vector of unsigned integer APIC IDs, one for each
406  *           logical processor in the system.
407  */
408 const std::vector<unsigned int>
409 detectX86ApicIDs(bool gmx_unused haveX2Apic)
410 {
411     std::vector<unsigned int>  apicID;
412
413     // We cannot just ask for all APIC IDs, but must force execution on each
414     // hardware thread and extract the APIC id there.
415 #if HAVE_SCHED_AFFINITY && defined HAVE_SYSCONF
416     unsigned int   eax, ebx, ecx, edx;
417     unsigned int   nApic = sysconf(_SC_NPROCESSORS_ONLN);
418     cpu_set_t      saveCpuSet;
419     cpu_set_t      cpuSet;
420     sched_getaffinity(0, sizeof(cpu_set_t), &saveCpuSet);
421     CPU_ZERO(&cpuSet);
422     for (unsigned int i = 0; i < nApic; i++)
423     {
424         CPU_SET(i, &cpuSet);
425         sched_setaffinity(0, sizeof(cpu_set_t), &cpuSet);
426         if (haveX2Apic)
427         {
428             executeX86CpuID(0xb, 0, &eax, &ebx, &ecx, &edx);
429             apicID.push_back(edx);
430         }
431         else
432         {
433             executeX86CpuID(0x1, 0, &eax, &ebx, &ecx, &edx);
434             apicID.push_back(ebx >> 24);
435         }
436         CPU_CLR(i, &cpuSet);
437     }
438     sched_setaffinity(0, sizeof(cpu_set_t), &saveCpuSet);
439 #elif GMX_NATIVE_WINDOWS
440     unsigned int   eax, ebx, ecx, edx;
441     SYSTEM_INFO    sysinfo;
442     GetSystemInfo( &sysinfo );
443     unsigned int   nApic        = sysinfo.dwNumberOfProcessors;
444     unsigned int   saveAffinity = SetThreadAffinityMask(GetCurrentThread(), 1);
445     for (DWORD_PTR i = 0; i < nApic; i++)
446     {
447         SetThreadAffinityMask(GetCurrentThread(), (((DWORD_PTR)1)<<i));
448         Sleep(0);
449         if (haveX2Apic)
450         {
451             executeX86CpuID(0xb, 0, &eax, &ebx, &ecx, &edx);
452             apicID.push_back(edx);
453         }
454         else
455         {
456             executeX86CpuID(0x1, 0, &eax, &ebx, &ecx, &edx);
457             apicID.push_back(ebx >> 24);
458         }
459     }
460     SetThreadAffinityMask(GetCurrentThread(), saveAffinity);
461 #endif
462     return apicID;
463 }
464
465
466 /*! \brief Utility to renumber indices extracted from APIC IDs
467  *
468  * \param v  Vector with unsigned integer indices
469  *
470  * This routine returns the number of unique different elements found in the vector,
471  * and renumbers these starting from 0. For example, the vector {0,1,2,8,9,10,8,9,10,0,1,2}
472  * will be rewritten to {0,1,2,3,4,5,3,4,5,0,1,2}, and it returns 6 for the
473  * number of unique elements.
474  */
475 void
476 renumberIndex(std::vector<unsigned int> * v)
477 {
478     std::vector<unsigned int> sortedV (*v);
479     std::sort(sortedV.begin(), sortedV.end());
480
481     std::vector<unsigned int> uniqueSortedV (sortedV);
482     auto                      it = std::unique(uniqueSortedV.begin(), uniqueSortedV.end());
483     uniqueSortedV.resize( std::distance(uniqueSortedV.begin(), it) );
484
485     for (std::size_t i = 0; i < uniqueSortedV.size(); i++)
486     {
487         unsigned int val = uniqueSortedV[i];
488         std::replace_if(v->begin(), v->end(), [val](unsigned int &c) -> bool { return c == val; }, static_cast<unsigned int>(i));
489     }
490 }
491
492
493 /*! \brief Try to detect basic CPU topology information using x86 cpuid
494  *
495  *  If x2APIC support is present, this is our first choice, otherwise we
496  *  attempt to use old vanilla APIC.
497  *
498  *  \return A new vector of entries with socket, core, hwthread information
499  *          for each logical processor.
500  */
501 std::vector<CpuInfo::LogicalProcessor>
502 detectX86LogicalProcessors()
503 {
504     unsigned int   eax;
505     unsigned int   ebx;
506     unsigned int   ecx;
507     unsigned int   edx;
508     unsigned int   maxStdLevel;
509     unsigned int   maxExtLevel;
510     bool           haveApic;
511     bool           haveX2Apic;
512
513     std::vector<CpuInfo::LogicalProcessor> logicalProcessors;
514
515     // Find largest standard & extended level input values allowed
516     executeX86CpuID(0x0, 0, &eax, &ebx, &ecx, &edx);
517     maxStdLevel = eax;
518     executeX86CpuID(0x80000000, 0, &eax, &ebx, &ecx, &edx);
519     maxExtLevel = eax;
520
521     if (maxStdLevel >= 0x1)
522     {
523         executeX86CpuID(0x1, 0, &eax, &ebx, &ecx, &edx);
524         haveX2Apic = (ecx & (1 << 21)) && maxStdLevel >= 0xb;
525         haveApic   = (edx & (1 <<  9)) && maxExtLevel >= 0x80000008;
526     }
527     else
528     {
529         haveX2Apic = false,
530         haveApic   = false;
531     }
532
533     if (haveX2Apic || haveApic)
534     {
535         unsigned int   hwThreadBits;
536         unsigned int   coreBits;
537         // Get bits for cores and hardware threads
538         if (haveX2Apic)
539         {
540             executeX86CpuID(0xb, 0, &eax, &ebx, &ecx, &edx);
541             hwThreadBits    = eax & 0x1f;
542             executeX86CpuID(0xb, 1, &eax, &ebx, &ecx, &edx);
543             coreBits        = (eax & 0x1f) - hwThreadBits;
544         }
545         else    // haveApic
546         {
547             // AMD without x2APIC does not support SMT - there are no hwthread bits in apic ID
548             hwThreadBits = 0;
549             // Get number of core bits in apic ID - try modern extended method first
550             executeX86CpuID(0x80000008, 0, &eax, &ebx, &ecx, &edx);
551             coreBits = (ecx >> 12) & 0xf;
552             if (coreBits == 0)
553             {
554                 // Legacy method for old single/dual core AMD CPUs
555                 int i = ecx & 0xf;
556                 while (i >> coreBits)
557                 {
558                     coreBits++;
559                 }
560             }
561         }
562
563         std::vector<unsigned int>  apicID = detectX86ApicIDs(haveX2Apic);
564
565         if (!apicID.empty())
566         {
567             // APIC IDs can be buggy, and it is always a mess. Typically more bits are
568             // reserved than needed, and the numbers might not increment by 1 even in
569             // a single socket or core. Extract, renumber, and check that things make sense.
570             unsigned int               hwThreadMask  = (1 << hwThreadBits) - 1;
571             unsigned int               coreMask      = (1 << coreBits) - 1;
572             std::vector<unsigned int>  hwThreadRanks;
573             std::vector<unsigned int>  coreRanks;
574             std::vector<unsigned int>  socketRanks;
575
576             for (auto a : apicID)
577             {
578                 hwThreadRanks.push_back( static_cast<int>( a & hwThreadMask ) );
579                 coreRanks.push_back( static_cast<int>( ( a >> hwThreadBits ) & coreMask ) );
580                 socketRanks.push_back( static_cast<int>( a >> ( coreBits + hwThreadBits ) ) );
581             }
582
583             renumberIndex(&hwThreadRanks);
584             renumberIndex(&coreRanks);
585             renumberIndex(&socketRanks);
586
587             unsigned int  hwThreadRankSize = 1 + *std::max_element(hwThreadRanks.begin(), hwThreadRanks.end());
588             unsigned int  coreRankSize     = 1 + *std::max_element(coreRanks.begin(), coreRanks.end());
589             unsigned int  socketRankSize   = 1 + *std::max_element(socketRanks.begin(), socketRanks.end());
590
591             if (socketRankSize * coreRankSize * hwThreadRankSize == apicID.size() )
592             {
593                 // Alright, everything looks consistent, so put it in the result
594                 for (std::size_t i = 0; i < apicID.size(); i++)
595                 {
596                     // While the internal APIC IDs are always unsigned integers, we also cast to
597                     // plain integers for the externally exposed vectors, since that will make
598                     // it possible to use '-1' for invalid entries in the future.
599                     logicalProcessors.push_back( { int(socketRanks[i]), int(coreRanks[i]), int(hwThreadRanks[i]) } );
600                 }
601             }
602         }
603     }
604     return logicalProcessors; // Will only have contents if everything worked
605 }
606
607
608 /******************************************************************************
609  *                                                                            *
610  *              Generic Linux detection by parsing /proc/cpuinfo              *
611  *                                                                            *
612  ******************************************************************************/
613
614 /*! \brief Parse /proc/cpuinfo into a simple string map
615  *
616  * This routine will read the contents of /proc/cpuinfo, and for each
617  * line that is not empty we will assign the (trimmed) string to the right of
618  * the colon as a key, and the left-hand side as the value in the map.
619  * For multi-processor systems where lines are repeated the latter lines will
620  * overwrite the first occurrence.
621  *
622  * \return New map with the contents. If the file is not available, the returned
623  *         map will be empty.
624  */
625 const std::map<std::string, std::string>
626 parseProcCpuInfo()
627 {
628     std::ifstream                       procCpuInfo("/proc/cpuinfo");
629     std::string                         line;
630     std::map<std::string, std::string>  cpuInfo;
631
632     while (std::getline(procCpuInfo, line))
633     {
634         if (!line.empty())
635         {
636             std::stringstream iss(line);
637             std::string       key;
638             std::string       val;
639             std::getline(iss, key, ':');  // part before colon
640             std::getline(iss, val);       // part after colon
641             trimString(&key);
642             trimString(&val);
643             // put it in the map. This will overwrite previous processors, but we don't care.
644             cpuInfo[key] = val;
645         }
646     }
647     return cpuInfo;
648 }
649
650
651 /*! \brief Try to detect vendor from /proc/cpuinfo
652  *
653  *  \param cpuInfo  Map returned from parseProcCpuinfo()
654  *
655  *  This routine tries to match a few common labels in /proc/cpuinfo to see if
656  *  they begin with the name of a standard vendor. If the file cannot be read
657  *  or if no match is found, we return gmx::CpuInfo::Vendor::Unknown.
658  */
659 CpuInfo::Vendor
660 detectProcCpuInfoVendor(const std::map<std::string, std::string> &cpuInfo)
661 {
662     const std::map<std::string, CpuInfo::Vendor> testVendors =
663     {
664         { "GenuineIntel", CpuInfo::Vendor::Intel   },
665         { "Intel",        CpuInfo::Vendor::Intel   },
666         { "AuthenticAmd", CpuInfo::Vendor::Amd     },
667         { "AMD",          CpuInfo::Vendor::Amd     },
668         { "ARM",          CpuInfo::Vendor::Arm     },
669         { "AArch64",      CpuInfo::Vendor::Arm     },
670         { "Fujitsu",      CpuInfo::Vendor::Fujitsu },
671         { "IBM",          CpuInfo::Vendor::Ibm     },
672         { "POWER",        CpuInfo::Vendor::Ibm     },
673         { "Oracle",       CpuInfo::Vendor::Oracle  },
674     };
675
676     // For each label in /proc/cpuinfo, compare the value to the name in the
677     // testNames map above, and if it's a match return the vendor.
678     for (auto &l : { "vendor_id", "vendor", "manufacture", "model", "processor", "cpu" })
679     {
680         if (cpuInfo.count(l))
681         {
682             // there was a line with this left-hand side in /proc/cpuinfo
683             const std::string &s1 = cpuInfo.at(l);
684
685             for (auto &t : testVendors)
686             {
687                 const std::string &s2 = t.first;
688
689                 // If the entire name we are testing (s2) matches the first part of
690                 // the string after the colon in /proc/cpuinfo (s1) we found our vendor
691                 if (std::equal(s2.begin(), s2.end(), s1.begin(),
692                                [](const char &x, const char &y) -> bool { return tolower(x) == tolower(y); }))
693                 {
694                     return t.second;
695                 }
696             }
697         }
698     }
699     return CpuInfo::Vendor::Unknown;
700 }
701
702
703 /*! \brief Detect IBM processor name and features from /proc/cpuinfo
704  *
705  *  \param      cpuInfo    Map returned from parseProcCpuinfo()
706  *  \param[out] brand      String where to write the brand string
707  *  \param[out] features   Feature set where supported features are inserted
708  *
709  *  This routine tries to match a few common labels in /proc/cpuinfo to see if
710  *  we can find the processor name and features. It is likely fragile.
711  */
712 void
713 detectProcCpuInfoIbm(const std::map<std::string, std::string> &cpuInfo,
714                      std::string *                             brand,
715                      std::set<CpuInfo::Feature> *              features)
716 {
717     // Get brand string from 'cpu' label if present, otherwise 'Processor'
718     if (cpuInfo.count("cpu"))
719     {
720         *brand = cpuInfo.at("cpu");
721     }
722     else if (cpuInfo.count("Processor"))
723     {
724         *brand = cpuInfo.at("Processor");
725     }
726
727     if (brand->find("A2") != std::string::npos)
728     {
729         // If the processor identification contains "A2", this is BlueGene/Q with QPX
730         features->insert(CpuInfo::Feature::Ibm_Qpx);
731     }
732
733     for (auto &l : { "model name", "model", "Processor", "cpu" })
734     {
735         if (cpuInfo.count(l))
736         {
737             std::string s1 = cpuInfo.at(l);
738             std::transform(s1.begin(), s1.end(), s1.begin(), ::tolower);
739
740             if (s1.find("altivec") != std::string::npos)
741             {
742                 features->insert(CpuInfo::Feature::Ibm_Vmx);
743                 // If this is a power6, we only have VMX. All later processors have VSX.
744                 if (s1.find("power6") == std::string::npos)
745                 {
746                     features->insert(CpuInfo::Feature::Ibm_Vsx);
747                 }
748             }
749         }
750     }
751 }
752
753
754 /*! \brief Detect ARM processor name and features from /proc/cpuinfo
755  *
756  *  \param      cpuInfo    Map returned from parseProcCpuinfo()
757  *  \param[out] brand      String where to write the brand string
758  *  \param[out] family     Major version of processor
759  *  \param[out] model      Middle version of processor
760  *  \param[out] stepping   Minor version of processor
761  *  \param[out] features   Feature set where supported features are inserted
762  *
763  *  This routine tries to match a few common labels in /proc/cpuinfo to see if
764  *  we can find the processor name and features. It is likely fragile.
765  */
766 void
767 detectProcCpuInfoArm(const std::map<std::string, std::string>   &cpuInfo,
768                      std::string *                               brand,
769                      int *                                       family,
770                      int *                                       model,
771                      int *                                       stepping,
772                      std::set<CpuInfo::Feature> *                features)
773 {
774     if (cpuInfo.count("Processor"))
775     {
776         *brand = cpuInfo.at("Processor");
777     }
778     if (cpuInfo.count("CPU architecture"))
779     {
780         *family = std::strtol(cpuInfo.at("CPU architecture").c_str(), NULL, 10);
781         // For some 64-bit CPUs it appears to say 'AArch64' instead
782         if (*family == 0 && cpuInfo.at("CPU architecture").find("AArch64") != std::string::npos)
783         {
784             *family = 8;  // fragile - no idea how a future ARMv9 will be represented in this case
785         }
786     }
787     if (cpuInfo.count("CPU variant"))
788     {
789         *model    = std::strtol(cpuInfo.at("CPU variant").c_str(), NULL, 16);
790     }
791     if (cpuInfo.count("CPU revision"))
792     {
793         *stepping = std::strtol(cpuInfo.at("CPU revision").c_str(), NULL, 10);
794     }
795
796     if (cpuInfo.count("Features"))
797     {
798         const std::string &s = cpuInfo.at("Features");
799         if (s.find("neon") != std::string::npos)
800         {
801             features->insert(CpuInfo::Feature::Arm_Neon);
802         }
803         if (s.find("asimd") != std::string::npos)
804         {
805             // At least Jetson TX1 runs a 32-bit environment by default, although
806             // the kernel is 64-bits, and reports asimd feature flags. We cannot
807             // use Neon-asimd in this case, so make sure we are on a 64-bit platform.
808             if (sizeof(void *) == 8)
809             {
810                 features->insert(CpuInfo::Feature::Arm_NeonAsimd);
811             }
812         }
813     }
814 }
815
816
817 /*! \brief Try to detect vendor, cpu and features from /proc/cpuinfo
818  *
819  *  \param[out] vendor     Detected hardware vendor
820  *  \param[out] brand      String where to write the brand string
821  *  \param[out] family     Major version of processor
822  *  \param[out] model      Middle version of processor
823  *  \param[out] stepping   Minor version of processor
824  *  \param[out] features   Feature set where supported features are inserted
825  *
826  *  This routine reads the /proc/cpuinfo file into a map and calls subroutines
827  *  that attempt to parse by matching keys and values to known strings. It is
828  *  much more fragile than our x86 detection, but it does not depend on
829  *  specific system calls, intrinsics or assembly instructions.
830  */
831 void
832 detectProcCpuInfo(CpuInfo::Vendor *              vendor,
833                   std::string   *                brand,
834                   int   *                        family,
835                   int   *                        model,
836                   int   *                        stepping,
837                   std::set<CpuInfo::Feature> *   features)
838 {
839     std::map<std::string, std::string> cpuInfo = parseProcCpuInfo();
840
841     if (*vendor == CpuInfo::Vendor::Unknown)
842     {
843         *vendor = detectProcCpuInfoVendor(cpuInfo);
844     }
845
846     // Unfortunately there is no standard for contents in /proc/cpuinfo. We cannot
847     // indiscriminately look for e.g. 'cpu' since it could be either name or an index.
848     // To handle this slightly better we use one subroutine per vendor.
849     switch (*vendor)
850     {
851         case CpuInfo::Vendor::Ibm:
852             detectProcCpuInfoIbm(cpuInfo, brand, features);
853             break;
854
855         case CpuInfo::Vendor::Arm:
856             detectProcCpuInfoArm(cpuInfo, brand, family, model, stepping, features);
857             break;
858
859         default:
860             // We only have a single check for fujitsu for now
861 #ifdef __HPC_ACE__
862             features->insert(CpuInfo::Feature::Fujitsu_HpcAce);
863 #endif
864             break;
865     }
866 }
867 /*! \endcond */
868 }   // namespace anonymous
869
870
871 // static
872 CpuInfo CpuInfo::detect()
873 {
874     CpuInfo result;
875
876 #if defined __i386__ || defined __i386 || defined _X86_ || defined _M_IX86 || \
877     defined __x86_64__ || defined __amd64__ || defined _M_X64 || defined _M_AMD64
878
879     result.vendor_            = detectX86Vendor();
880     detectX86Features(&result.brandString_, &result.family_, &result.model_,
881                       &result.stepping_, &result.features_);
882     result.logicalProcessors_ = detectX86LogicalProcessors();
883 #else   // not x86
884
885 #    if defined __arm__ || defined __arm || defined _M_ARM || defined __aarch64__
886     result.vendor_  = CpuInfo::Vendor::Arm;
887 #    elif defined __powerpc__ || defined __ppc__ || defined __PPC__
888     result.vendor_  = CpuInfo::Vendor::Ibm;
889 #    endif
890
891 #    if defined __aarch64__ || ( defined _M_ARM && _M_ARM >= 8 )
892     result.features_.insert(Feature::Arm_Neon);      // ARMv8 always has Neon
893     result.features_.insert(Feature::Arm_NeonAsimd); // ARMv8 always has Neon-asimd
894 #    endif
895
896 #    if defined sun
897     result.vendor_ = CpuInfo::Vendor::Oracle;
898 #    endif
899
900     // On Linux we might be able to find information in /proc/cpuinfo. If vendor or brand
901     // is set to a known value this routine will not overwrite it.
902     detectProcCpuInfo(&result.vendor_, &result.brandString_, &result.family_,
903                       &result.model_, &result.stepping_, &result.features_);
904
905 #endif  // x86 or not
906
907     if (!result.logicalProcessors_.empty())
908     {
909         result.supportLevel_ = CpuInfo::SupportLevel::LogicalProcessorInfo;
910     }
911     else if (!result.features_.empty())
912     {
913         result.supportLevel_ = CpuInfo::SupportLevel::Features;
914     }
915     else if (result.vendor_ != CpuInfo::Vendor::Unknown
916              || result.brandString_ != "Unknown CPU brand")
917     {
918         result.supportLevel_ = CpuInfo::SupportLevel::Name;
919     }
920     else
921     {
922         result.supportLevel_ = CpuInfo::SupportLevel::None;
923     }
924
925     return result;
926 }
927
928
929 CpuInfo::CpuInfo()
930     : vendor_(CpuInfo::Vendor::Unknown), brandString_("Unknown CPU brand"),
931       family_(0), model_(0), stepping_(0)
932 {
933 }
934
935
936 const std::map<CpuInfo::Vendor, std::string>
937 CpuInfo::s_vendorStrings_ =
938 {
939     { CpuInfo::Vendor::Unknown, "Unknown vendor"                  },
940     { CpuInfo::Vendor::Intel, "Intel"                             },
941     { CpuInfo::Vendor::Amd, "AMD"                                 },
942     { CpuInfo::Vendor::Fujitsu, "Fujitsu"                         },
943     { CpuInfo::Vendor::Ibm, "IBM"                                 },
944     { CpuInfo::Vendor::Arm, "ARM"                                 },
945     { CpuInfo::Vendor::Oracle, "Oracle"                           },
946 };
947
948
949 const std::map<CpuInfo::Feature, std::string>
950 CpuInfo::s_featureStrings_ =
951 {
952     { CpuInfo::Feature::X86_Aes, "aes"                            },
953     { CpuInfo::Feature::X86_Apic, "apic"                          },
954     { CpuInfo::Feature::X86_Avx, "avx"                            },
955     { CpuInfo::Feature::X86_Avx2, "avx2"                          },
956     { CpuInfo::Feature::X86_Avx512F, "avx512f"                    },
957     { CpuInfo::Feature::X86_Avx512PF, "avx512pf"                  },
958     { CpuInfo::Feature::X86_Avx512ER, "avx512er"                  },
959     { CpuInfo::Feature::X86_Avx512CD, "avx512cd"                  },
960     { CpuInfo::Feature::X86_Avx512BW, "avx512bw"                  },
961     { CpuInfo::Feature::X86_Avx512VL, "avx512vl"                  },
962     { CpuInfo::Feature::X86_Clfsh, "clfsh"                        },
963     { CpuInfo::Feature::X86_Cmov, "cmov"                          },
964     { CpuInfo::Feature::X86_Cx8, "cx8"                            },
965     { CpuInfo::Feature::X86_Cx16, "cx16"                          },
966     { CpuInfo::Feature::X86_F16C, "f16c"                          },
967     { CpuInfo::Feature::X86_Fma, "fma"                            },
968     { CpuInfo::Feature::X86_Fma4, "fma4"                          },
969     { CpuInfo::Feature::X86_Hle, "hle"                            },
970     { CpuInfo::Feature::X86_Htt, "htt"                            },
971     { CpuInfo::Feature::X86_Lahf, "lahf"                          },
972     { CpuInfo::Feature::X86_MisalignSse, "misalignsse"            },
973     { CpuInfo::Feature::X86_Mmx, "mmx"                            },
974     { CpuInfo::Feature::X86_Msr, "msr"                            },
975     { CpuInfo::Feature::X86_NonstopTsc, "nonstop_tsc"             },
976     { CpuInfo::Feature::X86_Pcid, "pcid"                          },
977     { CpuInfo::Feature::X86_Pclmuldq, "pclmuldq"                  },
978     { CpuInfo::Feature::X86_Pdcm, "pdcm"                          },
979     { CpuInfo::Feature::X86_PDPE1GB, "pdpe1gb"                    },
980     { CpuInfo::Feature::X86_Popcnt, "popcnt"                      },
981     { CpuInfo::Feature::X86_Pse, "pse"                            },
982     { CpuInfo::Feature::X86_Rdrnd, "rdrnd"                        },
983     { CpuInfo::Feature::X86_Rdtscp, "rdtscp"                      },
984     { CpuInfo::Feature::X86_Rtm, "rtm"                            },
985     { CpuInfo::Feature::X86_Sha, "sha"                            },
986     { CpuInfo::Feature::X86_Sse2, "sse2"                          },
987     { CpuInfo::Feature::X86_Sse3, "sse3"                          },
988     { CpuInfo::Feature::X86_Sse4A, "sse4a"                        },
989     { CpuInfo::Feature::X86_Sse4_1, "sse4.1"                      },
990     { CpuInfo::Feature::X86_Sse4_2, "sse4.2"                      },
991     { CpuInfo::Feature::X86_Ssse3, "ssse3"                        },
992     { CpuInfo::Feature::X86_Tdt, "tdt"                            },
993     { CpuInfo::Feature::X86_X2Apic, "x2apic"                      },
994     { CpuInfo::Feature::X86_Xop, "xop"                            },
995     { CpuInfo::Feature::Arm_Neon, "neon"                          },
996     { CpuInfo::Feature::Arm_NeonAsimd, "neon_asimd"               },
997     { CpuInfo::Feature::Ibm_Qpx, "qpx"                            },
998     { CpuInfo::Feature::Ibm_Vmx, "vmx"                            },
999     { CpuInfo::Feature::Ibm_Vsx, "vsx"                            },
1000     { CpuInfo::Feature::Fujitsu_HpcAce, "hpc-ace"                 }
1001 };
1002
1003
1004 bool
1005 cpuIsX86Nehalem(const CpuInfo &cpuInfo)
1006 {
1007     return (cpuInfo.vendor() == gmx::CpuInfo::Vendor::Intel &&
1008             cpuInfo.family() == 6 &&
1009             (cpuInfo.model() == 0x2E || cpuInfo.model() == 0x1A ||
1010              cpuInfo.model() == 0x1E || cpuInfo.model() == 0x2F ||
1011              cpuInfo.model() == 0x2C || cpuInfo.model() == 0x25) );
1012 }
1013
1014 }  // namespace gmx
1015
1016 #ifdef GMX_CPUINFO_STANDALONE
1017 int
1018 main(int argc, char **argv)
1019 {
1020     if (argc < 2)
1021     {
1022         fprintf(stdout,
1023                 "Usage:\n\n%s [flags]\n\n"
1024                 "Available flags:\n"
1025                 "-vendor        Print CPU vendor.\n"
1026                 "-brand         Print CPU brand string.\n"
1027                 "-family        Print CPU family version.\n"
1028                 "-model         Print CPU model version.\n"
1029                 "-stepping      Print CPU stepping version.\n"
1030                 "-features      Print CPU feature flags.\n",
1031                 argv[0]);
1032         exit(1);
1033     }
1034
1035     std::string   arg(argv[1]);
1036     gmx::CpuInfo  cpuInfo(gmx::CpuInfo::detect());
1037
1038     if (arg == "-vendor")
1039     {
1040         printf("%s\n", cpuInfo.vendorString().c_str());
1041     }
1042     else if (arg == "-brand")
1043     {
1044         printf("%s\n", cpuInfo.brandString().c_str());
1045     }
1046     else if (arg == "-family")
1047     {
1048         printf("%d\n", cpuInfo.family());
1049     }
1050     else if (arg == "-model")
1051     {
1052         printf("%d\n", cpuInfo.model());
1053     }
1054     else if (arg == "-stepping")
1055     {
1056         printf("%d\n", cpuInfo.stepping());
1057     }
1058     else if (arg == "-features")
1059     {
1060         for (auto &f : cpuInfo.featureSet() )
1061         {
1062             printf(" %s", cpuInfo.featureString(f).c_str());
1063         }
1064         printf(" \n"); // extra space so we can grep output for " <feature> " in CMake
1065     }
1066     else if (arg == "-topology")
1067     {
1068         // Undocumented debug option, usually not present in standalone version
1069         for (auto &t : cpuInfo.logicalProcessors() )
1070         {
1071             printf("%3u %3u %3u\n", t.socketRankInMachine, t.coreRankInSocket, t.hwThreadRankInCore);
1072         }
1073     }
1074     return 0;
1075 }
1076 #endif