Apply re-formatting to C++ in src/ tree.
[alexxy/gromacs.git] / src / gromacs / hardware / printhardware.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014,2015,2016, 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.
9  *
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.
14  *
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.
19  *
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.
24  *
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.
32  *
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.
35  */
36 #include "gmxpre.h"
37
38 #include "printhardware.h"
39
40 #include "config.h"
41
42 #include <cstdlib>
43
44 #include <string>
45 #include <vector>
46
47 #include "gromacs/hardware/cpuinfo.h"
48 #include "gromacs/hardware/device_management.h"
49 #include "gromacs/hardware/hardwaretopology.h"
50 #include "gromacs/hardware/hw_info.h"
51 #include "gromacs/hardware/identifyavx512fmaunits.h"
52 #include "gromacs/simd/support.h"
53 #include "gromacs/utility/basedefinitions.h"
54 #include "gromacs/utility/basenetwork.h"
55 #include "gromacs/utility/cstringutil.h"
56 #include "gromacs/utility/fatalerror.h"
57 #include "gromacs/utility/gmxmpi.h"
58 #include "gromacs/utility/logger.h"
59 #include "gromacs/utility/programcontext.h"
60 #include "gromacs/utility/stringutil.h"
61 #include "gromacs/utility/sysinfo.h"
62
63 #include "architecture.h"
64
65 //! Constant used to help minimize preprocessed code
66 static constexpr bool bGPUBinary = (GMX_GPU != 0);
67
68 /*! \internal \brief
69  * Returns the GPU information text, one GPU per line.
70  */
71 static std::string sprint_gpus(const std::vector<std::unique_ptr<DeviceInformation>>& deviceInfoList)
72 {
73     std::vector<std::string> gpuStrings(0);
74     for (const auto& deviceInfo : deviceInfoList)
75     {
76         gpuStrings.emplace_back("    " + getDeviceInformationString(*deviceInfo));
77     }
78     return gmx::joinStrings(gpuStrings, "\n");
79 }
80
81 /* Give a suitable fatal error or warning if the build configuration
82    and runtime CPU do not match. */
83 static void check_use_of_rdtscp_on_this_cpu(const gmx::MDLogger& mdlog, const gmx::CpuInfo& cpuInfo)
84 {
85     bool binaryUsesRdtscp = GMX_USE_RDTSCP;
86
87     const char* programName = gmx::getProgramContext().displayName();
88
89     if (cpuInfo.supportLevel() < gmx::CpuInfo::SupportLevel::Features)
90     {
91         if (binaryUsesRdtscp)
92         {
93             GMX_LOG(mdlog.warning)
94                     .asParagraph()
95                     .appendTextFormatted(
96                             "The %s executable was compiled to use the rdtscp CPU instruction. "
97                             "We cannot detect the features of your current CPU, but will proceed "
98                             "anyway. "
99                             "If you get a crash, rebuild GROMACS with the GMX_USE_RDTSCP=OFF CMake "
100                             "option.",
101                             programName);
102         }
103     }
104     else
105     {
106         bool cpuHasRdtscp = cpuInfo.feature(gmx::CpuInfo::Feature::X86_Rdtscp);
107
108         if (!cpuHasRdtscp && binaryUsesRdtscp)
109         {
110             gmx_fatal(FARGS,
111                       "The %s executable was compiled to use the rdtscp CPU instruction. "
112                       "However, this is not supported by the current hardware and continuing would "
113                       "lead to a crash. "
114                       "Please rebuild GROMACS with the GMX_USE_RDTSCP=OFF CMake option.",
115                       programName);
116         }
117
118         if (cpuHasRdtscp && !binaryUsesRdtscp)
119         {
120             GMX_LOG(mdlog.warning)
121                     .asParagraph()
122                     .appendTextFormatted(
123                             "The current CPU can measure timings more accurately than the code in\n"
124                             "%s was configured to use. This might affect your simulation\n"
125                             "speed as accurate timings are needed for load-balancing.\n"
126                             "Please consider rebuilding %s with the GMX_USE_RDTSCP=ON CMake "
127                             "option.",
128                             programName,
129                             programName);
130         }
131     }
132 }
133
134 static std::string detected_hardware_string(const gmx_hw_info_t* hwinfo, bool bFullCpuInfo)
135 {
136     std::string s;
137
138     const gmx::CpuInfo&          cpuInfo = *hwinfo->cpuInfo;
139     const gmx::HardwareTopology& hwTop   = *hwinfo->hardwareTopology;
140
141     s = gmx::formatString("\n");
142     s += gmx::formatString("Running on %d node%s with total",
143                            hwinfo->nphysicalnode,
144                            hwinfo->nphysicalnode == 1 ? "" : "s");
145     if (hwinfo->ncore_tot > 0)
146     {
147         s += gmx::formatString(" %d cores,", hwinfo->ncore_tot);
148     }
149     s += gmx::formatString(" %d logical cores", hwinfo->nhwthread_tot);
150     if (canPerformDeviceDetection(nullptr))
151     {
152         s += gmx::formatString(", %d compatible GPU%s",
153                                hwinfo->ngpu_compatible_tot,
154                                hwinfo->ngpu_compatible_tot == 1 ? "" : "s");
155     }
156     else if (bGPUBinary)
157     {
158         if (isDeviceDetectionEnabled())
159         {
160             s += gmx::formatString(" (GPU detection failed)");
161         }
162         else
163         {
164             s += gmx::formatString(" (GPU detection deactivated)");
165         }
166     }
167     s += gmx::formatString("\n");
168
169     if (hwinfo->nphysicalnode > 1)
170     {
171         /* Print per node hardware feature counts */
172         if (hwinfo->ncore_max > 0)
173         {
174             s += gmx::formatString("  Cores per node:           %2d", hwinfo->ncore_min);
175             if (hwinfo->ncore_max > hwinfo->ncore_min)
176             {
177                 s += gmx::formatString(" - %2d", hwinfo->ncore_max);
178             }
179             s += gmx::formatString("\n");
180         }
181         s += gmx::formatString("  Logical cores per node:   %2d", hwinfo->nhwthread_min);
182         if (hwinfo->nhwthread_max > hwinfo->nhwthread_min)
183         {
184             s += gmx::formatString(" - %2d", hwinfo->nhwthread_max);
185         }
186         s += gmx::formatString("\n");
187         if (bGPUBinary)
188         {
189             s += gmx::formatString("  Compatible GPUs per node: %2d", hwinfo->ngpu_compatible_min);
190             if (hwinfo->ngpu_compatible_max > hwinfo->ngpu_compatible_min)
191             {
192                 s += gmx::formatString(" - %2d", hwinfo->ngpu_compatible_max);
193             }
194             s += gmx::formatString("\n");
195             if (hwinfo->ngpu_compatible_tot > 0)
196             {
197                 if (hwinfo->bIdenticalGPUs)
198                 {
199                     s += gmx::formatString("  All nodes have identical type(s) of GPUs\n");
200                 }
201                 else
202                 {
203                     /* This message will also appear with identical GPU types
204                      * when at least one node has no GPU.
205                      */
206                     s += gmx::formatString(
207                             "  Different nodes have different type(s) and/or order of GPUs\n");
208                 }
209             }
210         }
211     }
212
213 #if GMX_LIB_MPI
214     int  rank;
215     char host[STRLEN];
216
217     gmx_gethostname(host, STRLEN);
218
219     MPI_Comm_rank(MPI_COMM_WORLD, &rank);
220
221     // TODO Use a wrapper around MPI_Get_processor_name instead.
222     s += gmx::formatString("Hardware detected on host %s (the node of MPI rank %d):\n", host, rank);
223 #else
224     s += gmx::formatString("Hardware detected:\n");
225 #endif
226     s += gmx::formatString("  CPU info:\n");
227
228     s += gmx::formatString("    Vendor: %s\n", cpuInfo.vendorString().c_str());
229
230     s += gmx::formatString("    Brand:  %s\n", cpuInfo.brandString().c_str());
231
232     if (bFullCpuInfo)
233     {
234         s += gmx::formatString("    Family: %d   Model: %d   Stepping: %d\n",
235                                cpuInfo.family(),
236                                cpuInfo.model(),
237                                cpuInfo.stepping());
238
239         s += gmx::formatString("    Features:");
240         for (auto& f : cpuInfo.featureSet())
241         {
242             s += gmx::formatString(" %s", gmx::CpuInfo::featureString(f).c_str());
243         }
244         s += gmx::formatString("\n");
245     }
246
247     if (cpuInfo.feature(gmx::CpuInfo::Feature::X86_Avx512F))
248     {
249         int avx512fmaunits = gmx::identifyAvx512FmaUnits();
250         s += gmx::formatString("    Number of AVX-512 FMA units:");
251         if (avx512fmaunits > 0)
252         {
253             s += gmx::formatString(" %d", avx512fmaunits);
254             if (avx512fmaunits == 1)
255             {
256                 s += gmx::formatString(" (AVX2 is faster w/o 2 AVX-512 FMA units)");
257             }
258         }
259         else
260         {
261             s += gmx::formatString(" Cannot run AVX-512 detection - assuming 2");
262         }
263         s += gmx::formatString("\n");
264     }
265
266     s += gmx::formatString("  Hardware topology: ");
267     switch (hwTop.supportLevel())
268     {
269         case gmx::HardwareTopology::SupportLevel::None: s += gmx::formatString("None\n"); break;
270         case gmx::HardwareTopology::SupportLevel::LogicalProcessorCount:
271             s += gmx::formatString("Only logical processor count\n");
272             break;
273         case gmx::HardwareTopology::SupportLevel::Basic: s += gmx::formatString("Basic\n"); break;
274         case gmx::HardwareTopology::SupportLevel::Full: s += gmx::formatString("Full\n"); break;
275         case gmx::HardwareTopology::SupportLevel::FullWithDevices:
276             s += gmx::formatString("Full, with devices\n");
277             break;
278     }
279
280     if (!hwTop.isThisSystem())
281     {
282         s += gmx::formatString("  NOTE: Hardware topology cached or synthetic, not detected.\n");
283         if (char* p = std::getenv("HWLOC_XMLFILE"))
284         {
285             s += gmx::formatString("        HWLOC_XMLFILE=%s\n", p);
286         }
287     }
288
289     if (bFullCpuInfo)
290     {
291         if (hwTop.supportLevel() >= gmx::HardwareTopology::SupportLevel::Basic)
292         {
293             s += gmx::formatString("    Sockets, cores, and logical processors:\n");
294
295             for (auto& socket : hwTop.machine().sockets)
296             {
297                 s += gmx::formatString("      Socket %2d:", socket.id);
298                 for (auto& c : socket.cores)
299                 {
300                     s += gmx::formatString(" [");
301                     for (auto& t : c.hwThreads)
302                     {
303                         s += gmx::formatString(" %3d", t.logicalProcessorId);
304                     }
305                     s += gmx::formatString("]");
306                 }
307                 s += gmx::formatString("\n");
308             }
309         }
310         if (hwTop.supportLevel() >= gmx::HardwareTopology::SupportLevel::Full)
311         {
312             s += gmx::formatString("    Numa nodes:\n");
313             for (auto& n : hwTop.machine().numa.nodes)
314             {
315                 s += gmx::formatString("      Node %2d (%zu bytes mem):", n.id, n.memory);
316                 for (auto& l : n.logicalProcessorId)
317                 {
318                     s += gmx::formatString(" %3d", l);
319                 }
320                 s += gmx::formatString("\n");
321             }
322             s += gmx::formatString("      Latency:\n          ");
323             for (std::size_t j = 0; j < hwTop.machine().numa.nodes.size(); j++)
324             {
325                 s += gmx::formatString(" %5zu", j);
326             }
327             s += gmx::formatString("\n");
328             for (std::size_t i = 0; i < hwTop.machine().numa.nodes.size(); i++)
329             {
330                 s += gmx::formatString("     %5zu", i);
331                 for (std::size_t j = 0; j < hwTop.machine().numa.nodes.size(); j++)
332                 {
333                     s += gmx::formatString(" %5.2f", hwTop.machine().numa.relativeLatency[i][j]);
334                 }
335                 s += gmx::formatString("\n");
336             }
337
338
339             s += gmx::formatString("    Caches:\n");
340             for (auto& c : hwTop.machine().caches)
341             {
342                 s += gmx::formatString(
343                         "      L%d: %zu bytes, linesize %d bytes, assoc. %d, shared %d ways\n",
344                         c.level,
345                         c.size,
346                         c.linesize,
347                         c.associativity,
348                         c.shared);
349             }
350         }
351         if (hwTop.supportLevel() >= gmx::HardwareTopology::SupportLevel::FullWithDevices)
352         {
353             s += gmx::formatString("    PCI devices:\n");
354             for (auto& d : hwTop.machine().devices)
355             {
356                 s += gmx::formatString(
357                         "      %04x:%02x:%02x.%1x  Id: %04x:%04x  Class: 0x%04x  Numa: %d\n",
358                         d.domain,
359                         d.bus,
360                         d.dev,
361                         d.func,
362                         d.vendorId,
363                         d.deviceId,
364                         d.classId,
365                         d.numaNodeId);
366             }
367         }
368     }
369
370     if (bGPUBinary && !hwinfo->deviceInfoList.empty())
371     {
372         s += gmx::formatString("  GPU info:\n");
373         s += gmx::formatString("    Number of GPUs detected: %d\n",
374                                static_cast<int>(hwinfo->deviceInfoList.size()));
375         s += sprint_gpus(hwinfo->deviceInfoList) + "\n";
376     }
377     return s;
378 }
379
380 void gmx_print_detected_hardware(FILE*                fplog,
381                                  const bool           warnToStdErr,
382                                  const gmx::MDLogger& mdlog,
383                                  const gmx_hw_info_t* hwinfo)
384 {
385     const gmx::CpuInfo& cpuInfo = *hwinfo->cpuInfo;
386
387     if (fplog != nullptr)
388     {
389         std::string detected;
390
391         detected = detected_hardware_string(hwinfo, TRUE);
392
393         fprintf(fplog, "%s\n", detected.c_str());
394     }
395
396     // Do not spam stderr with all our internal information unless
397     // there was something that actually went wrong; general information
398     // belongs in the logfile.
399
400     /* Check the compiled SIMD instruction set against that of the node
401      * with the lowest SIMD level support (skip if SIMD detection did not work)
402      */
403     if (cpuInfo.supportLevel() >= gmx::CpuInfo::SupportLevel::Features)
404     {
405         gmx::simdCheck(static_cast<gmx::SimdType>(hwinfo->simd_suggest_min), fplog, warnToStdErr);
406     }
407
408     /* For RDTSCP we only check on our local node and skip the MPI reduction, only on x86 */
409     if (gmx::c_architecture == gmx::Architecture::X86)
410     {
411         check_use_of_rdtscp_on_this_cpu(mdlog, cpuInfo);
412     }
413 }