a851dbee5d6b88b8847a3e8bba847b6ad565baef
[alexxy/gromacs.git] / src / gromacs / gmxlib / gmx_detect_hardware.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014,2015, 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 #include "gmxpre.h"
36
37 #include "gromacs/legacyheaders/gmx_detect_hardware.h"
38
39 #include "config.h"
40
41 #include <assert.h>
42 #include <errno.h>
43 #include <stdlib.h>
44 #include <string.h>
45
46 #include <string>
47 #include <vector>
48
49 #ifdef HAVE_UNISTD_H
50 /* For sysconf */
51 #include <unistd.h>
52 #endif
53 #ifdef GMX_NATIVE_WINDOWS
54 #include <windows.h>
55 #endif
56
57 #include "thread_mpi/threads.h"
58
59 #include "gromacs/gmxlib/gpu_utils/gpu_utils.h"
60 #include "gromacs/legacyheaders/copyrite.h"
61 #include "gromacs/legacyheaders/gmx_cpuid.h"
62 #include "gromacs/legacyheaders/md_logging.h"
63 #include "gromacs/legacyheaders/network.h"
64 #include "gromacs/legacyheaders/types/commrec.h"
65 #include "gromacs/legacyheaders/types/enums.h"
66 #include "gromacs/legacyheaders/types/hw_info.h"
67 #include "gromacs/utility/arrayref.h"
68 #include "gromacs/utility/basenetwork.h"
69 #include "gromacs/utility/cstringutil.h"
70 #include "gromacs/utility/exceptions.h"
71 #include "gromacs/utility/fatalerror.h"
72 #include "gromacs/utility/gmxassert.h"
73 #include "gromacs/utility/gmxomp.h"
74 #include "gromacs/utility/smalloc.h"
75 #include "gromacs/utility/stringutil.h"
76 #include "gromacs/utility/sysinfo.h"
77
78
79 #ifdef GMX_GPU
80 const gmx_bool bGPUBinary = TRUE;
81 #else
82 const gmx_bool bGPUBinary = FALSE;
83 #endif
84
85 static const char * invalid_gpuid_hint =
86     "A delimiter-free sequence of valid numeric IDs of available GPUs is expected.";
87
88 /* The globally shared hwinfo structure. */
89 static gmx_hw_info_t      *hwinfo_g;
90 /* A reference counter for the hwinfo structure */
91 static int                 n_hwinfo = 0;
92 /* A lock to protect the hwinfo structure */
93 static tMPI_Thread_mutex_t hw_info_lock = TMPI_THREAD_MUTEX_INITIALIZER;
94
95
96 /* FW decl. */
97 static void limit_num_gpus_used(gmx_gpu_opt_t *gpu_opt, int count);
98 static int gmx_count_gpu_dev_unique(const gmx_gpu_info_t *gpu_info,
99                                     const gmx_gpu_opt_t  *gpu_opt);
100
101 static void sprint_gpus(char *sbuf, const gmx_gpu_info_t *gpu_info)
102 {
103     int      i, ndev;
104     char     stmp[STRLEN];
105
106     ndev = gpu_info->n_dev;
107
108     sbuf[0] = '\0';
109     for (i = 0; i < ndev; i++)
110     {
111         get_gpu_device_info_string(stmp, gpu_info, i);
112         strcat(sbuf, "    ");
113         strcat(sbuf, stmp);
114         if (i < ndev - 1)
115         {
116             strcat(sbuf, "\n");
117         }
118     }
119 }
120
121 static void print_gpu_detection_stats(FILE                 *fplog,
122                                       const gmx_gpu_info_t *gpu_info,
123                                       const t_commrec      *cr)
124 {
125     char onhost[266], stmp[STRLEN];
126     int  ngpu;
127
128     if (!gpu_info->bDetectGPUs)
129     {
130         /* We skipped the detection, so don't print detection stats */
131         return;
132     }
133
134     ngpu = gpu_info->n_dev;
135
136 #if defined GMX_MPI && !defined GMX_THREAD_MPI
137     /* We only print the detection on one, of possibly multiple, nodes */
138     strncpy(onhost, " on host ", 10);
139     gmx_gethostname(onhost+9, 256);
140 #else
141     /* We detect all relevant GPUs */
142     strncpy(onhost, "", 1);
143 #endif
144
145     if (ngpu > 0)
146     {
147         sprint_gpus(stmp, gpu_info);
148         md_print_warn(cr, fplog, "%d GPU%s detected%s:\n%s\n",
149                       ngpu, (ngpu > 1) ? "s" : "", onhost, stmp);
150     }
151     else
152     {
153         md_print_warn(cr, fplog, "No GPUs detected%s\n", onhost);
154     }
155 }
156
157 /*! \brief Helper function for reporting GPU usage information
158  * in the mdrun log file
159  *
160  * \param[in] gpu_info    Pointer to per-node GPU info struct
161  * \param[in] gpu_opt     Pointer to per-node GPU options struct
162  * \param[in] numPpRanks  Number of PP ranks per node
163  * \return                String to write to the log file
164  * \throws                std::bad_alloc if out of memory */
165 static std::string
166 makeGpuUsageReport(const gmx_gpu_info_t *gpu_info,
167                    const gmx_gpu_opt_t  *gpu_opt,
168                    size_t                numPpRanks)
169 {
170     int ngpu_use  = gpu_opt->n_dev_use;
171     int ngpu_comp = gpu_info->n_dev_compatible;
172
173     /* Issue a note if GPUs are available but not used */
174     if (ngpu_comp > 0 && ngpu_use < 1)
175     {
176         return gmx::formatString("%d compatible GPU%s detected in the system, but none will be used.\n"
177                                  "Consider trying GPU acceleration with the Verlet scheme!\n",
178                                  ngpu_comp, (ngpu_comp > 1) ? "s" : "");
179     }
180
181     std::string output;
182     if (!gpu_opt->bUserSet)
183     {
184         // gpu_opt->dev_compatible is only populated during auto-selection
185         std::string gpuIdsString =
186             formatAndJoin(gmx::constArrayRefFromArray(gpu_opt->dev_compatible,
187                                                       gpu_opt->n_dev_compatible),
188                           ",", gmx::StringFormatter("%d"));
189         bool bPluralGpus = gpu_opt->n_dev_compatible > 1;
190         output += gmx::formatString("%d compatible GPU%s %s present, with ID%s %s\n",
191                                     gpu_opt->n_dev_compatible,
192                                     bPluralGpus ? "s" : "",
193                                     bPluralGpus ? "are" : "is",
194                                     bPluralGpus ? "s" : "",
195                                     gpuIdsString.c_str());
196     }
197
198     {
199         std::vector<int>   gpuIdsInUse;
200         for (int i = 0; i < ngpu_use; i++)
201         {
202             gpuIdsInUse.push_back(get_cuda_gpu_device_id(gpu_info, gpu_opt, i));
203         }
204         std::string gpuIdsString =
205             formatAndJoin(gpuIdsInUse, ",", gmx::StringFormatter("%d"));
206         int         numGpusInUse = gmx_count_gpu_dev_unique(gpu_info, gpu_opt);
207         bool        bPluralGpus  = numGpusInUse > 1;
208
209         output += gmx::formatString("%d GPU%s %sselected for this run.\n"
210                                     "Mapping of GPU ID%s to the %d PP rank%s in this node: %s\n",
211                                     numGpusInUse, bPluralGpus ? "s" : "",
212                                     gpu_opt->bUserSet ? "user-" : "auto-",
213                                     bPluralGpus ? "s" : "",
214                                     numPpRanks,
215                                     (numPpRanks > 1) ? "s" : "",
216                                     gpuIdsString.c_str());
217     }
218
219     return output;
220 }
221
222 /* Give a suitable fatal error or warning if the build configuration
223    and runtime CPU do not match. */
224 static void
225 check_use_of_rdtscp_on_this_cpu(FILE                *fplog,
226                                 const t_commrec     *cr,
227                                 const gmx_hw_info_t *hwinfo)
228 {
229     gmx_bool bCpuHasRdtscp, bBinaryUsesRdtscp;
230 #ifdef HAVE_RDTSCP
231     bBinaryUsesRdtscp = TRUE;
232 #else
233     bBinaryUsesRdtscp = FALSE;
234 #endif
235
236     bCpuHasRdtscp = gmx_cpuid_feature(hwinfo->cpuid_info, GMX_CPUID_FEATURE_X86_RDTSCP);
237
238     if (!bCpuHasRdtscp && bBinaryUsesRdtscp)
239     {
240         gmx_fatal(FARGS, "The %s executable was compiled to use the rdtscp CPU instruction. "
241                   "However, this is not supported by the current hardware and continuing would lead to a crash. "
242                   "Please rebuild GROMACS with the GMX_USE_RDTSCP=OFF CMake option.",
243                   ShortProgram());
244     }
245
246     if (bCpuHasRdtscp && !bBinaryUsesRdtscp)
247     {
248         md_print_warn(cr, fplog, "The current CPU can measure timings more accurately than the code in\n"
249                       "%s was configured to use. This might affect your simulation\n"
250                       "speed as accurate timings are needed for load-balancing.\n"
251                       "Please consider rebuilding %s with the GMX_USE_RDTSCP=ON CMake option.\n",
252                       ShortProgram(), ShortProgram());
253     }
254 }
255
256 void gmx_check_hw_runconf_consistency(FILE                *fplog,
257                                       const gmx_hw_info_t *hwinfo,
258                                       const t_commrec     *cr,
259                                       const gmx_hw_opt_t  *hw_opt,
260                                       gmx_bool             bUseGPU)
261 {
262     int      npppn;
263     char     th_or_proc[STRLEN], th_or_proc_plural[STRLEN], pernode[STRLEN];
264     gmx_bool btMPI, bMPI, bMaxMpiThreadsSet, bNthreadsAuto, bEmulateGPU;
265
266     assert(hwinfo);
267     assert(cr);
268
269     /* Below we only do consistency checks for PP and GPUs,
270      * this is irrelevant for PME only nodes, so in that case we return
271      * here.
272      */
273     if (!(cr->duty & DUTY_PP))
274     {
275         return;
276     }
277
278 #if defined(GMX_THREAD_MPI)
279     bMPI          = FALSE;
280     btMPI         = TRUE;
281     bNthreadsAuto = (hw_opt->nthreads_tmpi < 1);
282 #elif defined(GMX_LIB_MPI)
283     bMPI          = TRUE;
284     btMPI         = FALSE;
285     bNthreadsAuto = FALSE;
286 #else
287     bMPI          = FALSE;
288     btMPI         = FALSE;
289     bNthreadsAuto = FALSE;
290 #endif
291
292     /* GPU emulation detection is done later, but we need here as well
293      * -- uncool, but there's no elegant workaround */
294     bEmulateGPU       = (getenv("GMX_EMULATE_GPU") != NULL);
295     bMaxMpiThreadsSet = (getenv("GMX_MAX_MPI_THREADS") != NULL);
296
297     if (hwinfo->gpu_info.n_dev_compatible > 0)
298     {
299         std::string gpuUseageReport;
300         try
301         {
302             gpuUseageReport = makeGpuUsageReport(&hwinfo->gpu_info,
303                                                  &hw_opt->gpu_opt,
304                                                  cr->nrank_pp_intranode);
305         }
306         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
307
308         /* NOTE: this print is only for and on one physical node */
309         md_print_info(cr, fplog, "%s\n", gpuUseageReport.c_str());
310     }
311
312     /* Need to ensure that we have enough GPUs:
313      * - need one GPU per PP node
314      * - no GPU oversubscription with tMPI
315      * */
316     /* number of PP processes per node */
317     npppn = cr->nrank_pp_intranode;
318
319     pernode[0]           = '\0';
320     th_or_proc_plural[0] = '\0';
321     if (btMPI)
322     {
323         sprintf(th_or_proc, "thread-MPI thread");
324         if (npppn > 1)
325         {
326             sprintf(th_or_proc_plural, "s");
327         }
328     }
329     else if (bMPI)
330     {
331         sprintf(th_or_proc, "MPI process");
332         if (npppn > 1)
333         {
334             sprintf(th_or_proc_plural, "es");
335         }
336         sprintf(pernode, " per node");
337     }
338     else
339     {
340         /* neither MPI nor tMPI */
341         sprintf(th_or_proc, "process");
342     }
343
344     if (bUseGPU && hwinfo->gpu_info.n_dev_compatible > 0 &&
345         !bEmulateGPU)
346     {
347         int  ngpu_comp, ngpu_use;
348         char gpu_comp_plural[2], gpu_use_plural[2];
349
350         ngpu_comp = hwinfo->gpu_info.n_dev_compatible;
351         ngpu_use  = hw_opt->gpu_opt.n_dev_use;
352
353         sprintf(gpu_comp_plural, "%s", (ngpu_comp > 1) ? "s" : "");
354         sprintf(gpu_use_plural,  "%s", (ngpu_use > 1) ? "s" : "");
355
356         /* number of tMPI threads auto-adjusted */
357         if (btMPI && bNthreadsAuto)
358         {
359             if (hw_opt->gpu_opt.bUserSet && npppn < ngpu_use)
360             {
361                 /* The user manually provided more GPUs than threads we
362                    could automatically start. */
363                 gmx_fatal(FARGS,
364                           "%d GPU%s provided, but only %d PP thread-MPI thread%s coud be started.\n"
365                           "%s requires one PP tread-MPI thread per GPU; use fewer GPUs%s.",
366                           ngpu_use, gpu_use_plural,
367                           npppn, th_or_proc_plural,
368                           ShortProgram(), bMaxMpiThreadsSet ? "\nor allow more threads to be used" : "");
369             }
370
371             if (!hw_opt->gpu_opt.bUserSet && npppn < ngpu_comp)
372             {
373                 /* There are more GPUs than tMPI threads; we have
374                    limited the number GPUs used. */
375                 md_print_warn(cr, fplog,
376                               "NOTE: %d GPU%s were detected, but only %d PP thread-MPI thread%s can be started.\n"
377                               "      %s can use one GPU per PP tread-MPI thread, so only %d GPU%s will be used.%s\n",
378                               ngpu_comp, gpu_comp_plural,
379                               npppn, th_or_proc_plural,
380                               ShortProgram(), npppn,
381                               npppn > 1 ? "s" : "",
382                               bMaxMpiThreadsSet ? "\n      Also, you can allow more threads to be used by increasing GMX_MAX_MPI_THREADS" : "");
383             }
384         }
385
386         if (hw_opt->gpu_opt.bUserSet)
387         {
388             if (ngpu_use != npppn)
389             {
390                 gmx_fatal(FARGS,
391                           "Incorrect launch configuration: mismatching number of PP %s%s and GPUs%s.\n"
392                           "%s was started with %d PP %s%s%s, but you provided %d GPU%s.",
393                           th_or_proc, btMPI ? "s" : "es", pernode,
394                           ShortProgram(), npppn, th_or_proc,
395                           th_or_proc_plural, pernode,
396                           ngpu_use, gpu_use_plural);
397             }
398         }
399         else
400         {
401             if (ngpu_comp > npppn)
402             {
403                 md_print_warn(cr, fplog,
404                               "NOTE: potentially sub-optimal launch configuration, %s started with less\n"
405                               "      PP %s%s%s than GPU%s available.\n"
406                               "      Each PP %s can use only one GPU, %d GPU%s%s will be used.\n",
407                               ShortProgram(), th_or_proc,
408                               th_or_proc_plural, pernode, gpu_comp_plural,
409                               th_or_proc, npppn, gpu_use_plural, pernode);
410             }
411
412             if (ngpu_use != npppn)
413             {
414                 /* Avoid duplicate error messages.
415                  * Unfortunately we can only do this at the physical node
416                  * level, since the hardware setup and MPI process count
417                  * might differ between physical nodes.
418                  */
419                 if (cr->rank_pp_intranode == 0)
420                 {
421                     gmx_fatal(FARGS,
422                               "Incorrect launch configuration: mismatching number of PP %s%s and GPUs%s.\n"
423                               "%s was started with %d PP %s%s%s, but only %d GPU%s were detected.",
424                               th_or_proc, btMPI ? "s" : "es", pernode,
425                               ShortProgram(), npppn, th_or_proc,
426                               th_or_proc_plural, pernode,
427                               ngpu_use, gpu_use_plural);
428                 }
429             }
430         }
431
432         {
433             int      same_count;
434
435             same_count = gmx_count_gpu_dev_shared(&hw_opt->gpu_opt);
436
437             if (same_count > 0)
438             {
439                 md_print_info(cr, fplog,
440                               "NOTE: You assigned %s to multiple %s%s.\n",
441                               same_count > 1 ? "GPUs" : "a GPU", th_or_proc, btMPI ? "s" : "es");
442             }
443         }
444     }
445
446 #ifdef GMX_MPI
447     if (PAR(cr))
448     {
449         /* Avoid other ranks to continue after
450            inconsistency */
451         MPI_Barrier(cr->mpi_comm_mygroup);
452     }
453 #endif
454
455 }
456
457 /* Return 0 if none of the GPU (per node) are shared among PP ranks.
458  *
459  * Sharing GPUs among multiple PP ranks is possible when the user passes
460  * GPU IDs. Here we check for sharing and return a non-zero value when
461  * this is detected. Note that the return value represents the number of
462  * PP rank pairs that share a device.
463  */
464 int gmx_count_gpu_dev_shared(const gmx_gpu_opt_t *gpu_opt)
465 {
466     int      same_count    = 0;
467     int      ngpu          = gpu_opt->n_dev_use;
468
469     if (gpu_opt->bUserSet)
470     {
471         int      i, j;
472
473         for (i = 0; i < ngpu - 1; i++)
474         {
475             for (j = i + 1; j < ngpu; j++)
476             {
477                 same_count      += (gpu_opt->dev_use[i] ==
478                                     gpu_opt->dev_use[j]);
479             }
480         }
481     }
482
483     return same_count;
484 }
485
486 /* Count and return the number of unique GPUs (per node) selected.
487  *
488  * As sharing GPUs among multiple PP ranks is possible when the user passes
489  * GPU IDs, the number of GPUs user (per node) can be different from the
490  * number of GPU IDs selected.
491  */
492 static int gmx_count_gpu_dev_unique(const gmx_gpu_info_t *gpu_info,
493                                     const gmx_gpu_opt_t  *gpu_opt)
494 {
495     int  i, uniq_count, ngpu;
496     int *uniq_ids;
497
498     assert(gpu_info);
499     assert(gpu_opt);
500
501     ngpu = gpu_info->n_dev;
502
503     uniq_count  = 0;
504
505     snew(uniq_ids, ngpu);
506
507     /* Each element in uniq_ids will be set to 0 or 1. The n-th element set
508      * to 1 indicates that the respective GPU was selected to be used. */
509     for (i = 0; i < gpu_opt->n_dev_use; i++)
510     {
511         uniq_ids[get_cuda_gpu_device_id(gpu_info, gpu_opt, i)] = 1;
512     }
513     /* Count the devices used. */
514     for (i = 0; i < ngpu; i++)
515     {
516         uniq_count += uniq_ids[i];
517     }
518
519     sfree(uniq_ids);
520
521     return uniq_count;
522 }
523
524 static int get_ncores(gmx_cpuid_t cpuid)
525 {
526     int        nprocessors, npackages, ncores_per_package, nhwthreads_per_core;
527     const int *package_id, *core_id, *hwthread_id, *locality_order;
528     int        rc;
529
530     rc = gmx_cpuid_topology(cpuid,
531                             &nprocessors, &npackages,
532                             &ncores_per_package, &nhwthreads_per_core,
533                             &package_id, &core_id,
534                             &hwthread_id, &locality_order);
535
536     if (rc == 0)
537     {
538         return npackages*ncores_per_package;
539     }
540     else
541     {
542         /* We don't have cpuid topology info, return 0 core count */
543         return 0;
544     }
545 }
546
547 /* Return the number of hardware threads supported by the current CPU.
548  * We assume that this is equal with the number of "processors"
549  * reported to be online by the OS at the time of the call. The
550  * definition of "processor" is according to an old POSIX standard.
551  *
552  * Note that the number of hardware threads is generally greater than
553  * the number of cores (e.g. x86 hyper-threading, Power). Managing the
554  * mapping of software threads to hardware threads is managed
555  * elsewhere. */
556 static int get_nthreads_hw_avail(FILE gmx_unused *fplog, const t_commrec gmx_unused *cr)
557 {
558     int ret = 0;
559
560 #if ((defined(WIN32) || defined( _WIN32 ) || defined(WIN64) || defined( _WIN64 )) && !(defined (__CYGWIN__) || defined (__CYGWIN32__)))
561     /* Windows */
562     SYSTEM_INFO sysinfo;
563     GetSystemInfo( &sysinfo );
564     ret = sysinfo.dwNumberOfProcessors;
565 #elif defined HAVE_SYSCONF
566     /* We are probably on Unix.
567      * Now check if we have the argument to use before executing the call
568      */
569 #if defined(_SC_NPROCESSORS_ONLN)
570     ret = sysconf(_SC_NPROCESSORS_ONLN);
571 #elif defined(_SC_NPROC_ONLN)
572     ret = sysconf(_SC_NPROC_ONLN);
573 #elif defined(_SC_NPROCESSORS_CONF)
574     ret = sysconf(_SC_NPROCESSORS_CONF);
575 #elif defined(_SC_NPROC_CONF)
576     ret = sysconf(_SC_NPROC_CONF);
577 #else
578 #warning "No valid sysconf argument value found. Executables will not be able to determine the number of hardware threads: mdrun will use 1 thread by default!"
579 #endif /* End of check for sysconf argument values */
580
581 #else
582     /* Neither windows nor Unix. No fscking idea how many hardware threads we have! */
583     ret = -1;
584 #endif
585
586     if (debug)
587     {
588         fprintf(debug, "Detected %d hardware threads to use.\n", ret);
589     }
590
591 #ifdef GMX_OPENMP
592     if (ret != gmx_omp_get_num_procs())
593     {
594         md_print_warn(cr, fplog,
595                       "Number of hardware threads detected (%d) does not match the number reported by OpenMP (%d).\n"
596                       "Consider setting the launch configuration manually!",
597                       ret, gmx_omp_get_num_procs());
598     }
599 #endif
600
601     return ret;
602 }
603
604 static void gmx_detect_gpus(FILE *fplog, const t_commrec *cr)
605 {
606 #ifdef GMX_LIB_MPI
607     int              rank_world;
608     MPI_Comm         physicalnode_comm;
609 #endif
610     int              rank_local;
611
612     /* Under certain circumstances MPI ranks on the same physical node
613      * can not simultaneously access the same GPU(s). Therefore we run
614      * the detection only on one MPI rank per node and broadcast the info.
615      * Note that with thread-MPI only a single thread runs this code.
616      *
617      * TODO: We should also do CPU hardware detection only once on each
618      * physical node and broadcast it, instead of do it on every MPI rank.
619      */
620 #ifdef GMX_LIB_MPI
621     /* A split of MPI_COMM_WORLD over physical nodes is only required here,
622      * so we create and destroy it locally.
623      */
624     MPI_Comm_rank(MPI_COMM_WORLD, &rank_world);
625     MPI_Comm_split(MPI_COMM_WORLD, gmx_physicalnode_id_hash(),
626                    rank_world, &physicalnode_comm);
627     MPI_Comm_rank(physicalnode_comm, &rank_local);
628 #else
629     /* Here there should be only one process, check this */
630     assert(cr->nnodes == 1 && cr->sim_nodeid == 0);
631
632     rank_local = 0;
633 #endif
634
635     if (rank_local == 0)
636     {
637         char detection_error[STRLEN] = "", sbuf[STRLEN];
638
639         if (detect_gpus(&hwinfo_g->gpu_info, detection_error) != 0)
640         {
641             if (detection_error[0] != '\0')
642             {
643                 sprintf(sbuf, ":\n      %s\n", detection_error);
644             }
645             else
646             {
647                 sprintf(sbuf, ".");
648             }
649             md_print_warn(cr, fplog,
650                           "NOTE: Error occurred during GPU detection%s"
651                           "      Can not use GPU acceleration, will fall back to CPU kernels.\n",
652                           sbuf);
653         }
654     }
655
656 #ifdef GMX_LIB_MPI
657     /* Broadcast the GPU info to the other ranks within this node */
658     MPI_Bcast(&hwinfo_g->gpu_info.n_dev, 1, MPI_INT, 0, physicalnode_comm);
659
660     if (hwinfo_g->gpu_info.n_dev > 0)
661     {
662         int dev_size;
663
664         dev_size = hwinfo_g->gpu_info.n_dev*sizeof_gpu_dev_info();
665
666         if (rank_local > 0)
667         {
668             hwinfo_g->gpu_info.gpu_dev =
669                 (struct gmx_device_info_t *)malloc(dev_size);
670         }
671         MPI_Bcast(hwinfo_g->gpu_info.gpu_dev, dev_size, MPI_BYTE,
672                   0, physicalnode_comm);
673         MPI_Bcast(&hwinfo_g->gpu_info.n_dev_compatible, 1, MPI_INT,
674                   0, physicalnode_comm);
675     }
676
677     MPI_Comm_free(&physicalnode_comm);
678 #endif
679 }
680
681 static void gmx_collect_hardware_mpi()
682 {
683 #ifdef GMX_LIB_MPI
684     int  rank_id;
685     int  nrank, rank, ncore, nhwthread, ngpu, i;
686     int  gpu_hash;
687     int *buf, *all;
688
689     rank_id   = gmx_physicalnode_id_hash();
690     MPI_Comm_rank(MPI_COMM_WORLD, &rank);
691     MPI_Comm_size(MPI_COMM_WORLD, &nrank);
692     ncore     = hwinfo_g->ncore;
693     nhwthread = hwinfo_g->nthreads_hw_avail;
694     ngpu      = hwinfo_g->gpu_info.n_dev_compatible;
695     /* Create a unique hash of the GPU type(s) in this node */
696     gpu_hash  = 0;
697     /* Here it might be better to only loop over the compatible GPU, but we
698      * don't have that information available and it would also require
699      * removing the device ID from the device info string.
700      */
701     for (i = 0; i < hwinfo_g->gpu_info.n_dev; i++)
702     {
703         char stmp[STRLEN];
704
705         /* Since the device ID is incorporated in the hash, the order of
706          * the GPUs affects the hash. Also two identical GPUs won't give
707          * a gpu_hash of zero after XORing.
708          */
709         get_gpu_device_info_string(stmp, &hwinfo_g->gpu_info, i);
710         gpu_hash ^= gmx_string_fullhash_func(stmp, gmx_string_hash_init);
711     }
712
713     snew(buf, nrank);
714     snew(all, nrank);
715     buf[rank] = rank_id;
716
717     MPI_Allreduce(buf, all, nrank, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
718
719     gmx_bool bFound;
720     int      nnode0, ncore0, nhwthread0, ngpu0, r;
721
722     bFound     = FALSE;
723     ncore0     = 0;
724     nnode0     = 0;
725     nhwthread0 = 0;
726     ngpu0      = 0;
727     for (r = 0; r < nrank; r++)
728     {
729         if (all[r] == rank_id)
730         {
731             if (!bFound && r == rank)
732             {
733                 /* We are the first rank in this physical node */
734                 nnode0     = 1;
735                 ncore0     = ncore;
736                 nhwthread0 = nhwthread;
737                 ngpu0      = ngpu;
738             }
739             bFound = TRUE;
740         }
741     }
742
743     sfree(buf);
744     sfree(all);
745
746     int sum[4], maxmin[10];
747
748     {
749         int buf[4];
750
751         /* Sum values from only intra-rank 0 so we get the sum over all nodes */
752         buf[0] = nnode0;
753         buf[1] = ncore0;
754         buf[2] = nhwthread0;
755         buf[3] = ngpu0;
756
757         MPI_Allreduce(buf, sum, 4, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
758     }
759
760     {
761         int buf[10];
762
763         /* Store + and - values for all ranks,
764          * so we can get max+min with one MPI call.
765          */
766         buf[0] = ncore;
767         buf[1] = nhwthread;
768         buf[2] = ngpu;
769         buf[3] = gmx_cpuid_simd_suggest(hwinfo_g->cpuid_info);
770         buf[4] = gpu_hash;
771         buf[5] = -buf[0];
772         buf[6] = -buf[1];
773         buf[7] = -buf[2];
774         buf[8] = -buf[3];
775         buf[9] = -buf[4];
776
777         MPI_Allreduce(buf, maxmin, 10, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
778     }
779
780     hwinfo_g->nphysicalnode       = sum[0];
781     hwinfo_g->ncore_tot           = sum[1];
782     hwinfo_g->ncore_min           = -maxmin[5];
783     hwinfo_g->ncore_max           = maxmin[0];
784     hwinfo_g->nhwthread_tot       = sum[2];
785     hwinfo_g->nhwthread_min       = -maxmin[6];
786     hwinfo_g->nhwthread_max       = maxmin[1];
787     hwinfo_g->ngpu_compatible_tot = sum[3];
788     hwinfo_g->ngpu_compatible_min = -maxmin[7];
789     hwinfo_g->ngpu_compatible_max = maxmin[2];
790     hwinfo_g->simd_suggest_min    = static_cast<enum gmx_cpuid_simd>(-maxmin[8]);
791     hwinfo_g->simd_suggest_max    = static_cast<enum gmx_cpuid_simd>(maxmin[3]);
792     hwinfo_g->bIdenticalGPUs      = (maxmin[4] == -maxmin[9]);
793 #else
794     /* All ranks use the same pointer, protect it with a mutex */
795     tMPI_Thread_mutex_lock(&hw_info_lock);
796     hwinfo_g->nphysicalnode       = 1;
797     hwinfo_g->ncore_tot           = hwinfo_g->ncore;
798     hwinfo_g->ncore_min           = hwinfo_g->ncore;
799     hwinfo_g->ncore_max           = hwinfo_g->ncore;
800     hwinfo_g->nhwthread_tot       = hwinfo_g->nthreads_hw_avail;
801     hwinfo_g->nhwthread_min       = hwinfo_g->nthreads_hw_avail;
802     hwinfo_g->nhwthread_max       = hwinfo_g->nthreads_hw_avail;
803     hwinfo_g->ngpu_compatible_tot = hwinfo_g->gpu_info.n_dev_compatible;
804     hwinfo_g->ngpu_compatible_min = hwinfo_g->gpu_info.n_dev_compatible;
805     hwinfo_g->ngpu_compatible_max = hwinfo_g->gpu_info.n_dev_compatible;
806     hwinfo_g->simd_suggest_min    = gmx_cpuid_simd_suggest(hwinfo_g->cpuid_info);
807     hwinfo_g->simd_suggest_max    = gmx_cpuid_simd_suggest(hwinfo_g->cpuid_info);
808     hwinfo_g->bIdenticalGPUs      = TRUE;
809     tMPI_Thread_mutex_unlock(&hw_info_lock);
810 #endif
811 }
812
813 gmx_hw_info_t *gmx_detect_hardware(FILE *fplog, const t_commrec *cr,
814                                    gmx_bool bDetectGPUs)
815 {
816     int ret;
817
818     /* make sure no one else is doing the same thing */
819     ret = tMPI_Thread_mutex_lock(&hw_info_lock);
820     if (ret != 0)
821     {
822         gmx_fatal(FARGS, "Error locking hwinfo mutex: %s", strerror(errno));
823     }
824
825     /* only initialize the hwinfo structure if it is not already initalized */
826     if (n_hwinfo == 0)
827     {
828         snew(hwinfo_g, 1);
829
830         /* detect CPUID info; no fuss, we don't detect system-wide
831          * -- sloppy, but that's it for now */
832         if (gmx_cpuid_init(&hwinfo_g->cpuid_info) != 0)
833         {
834             gmx_fatal_collective(FARGS, cr, NULL, "CPUID detection failed!");
835         }
836
837         /* get the number of cores, will be 0 when not detected */
838         hwinfo_g->ncore             = get_ncores(hwinfo_g->cpuid_info);
839
840         /* detect number of hardware threads */
841         hwinfo_g->nthreads_hw_avail = get_nthreads_hw_avail(fplog, cr);
842
843         /* detect GPUs */
844         hwinfo_g->gpu_info.n_dev            = 0;
845         hwinfo_g->gpu_info.n_dev_compatible = 0;
846         hwinfo_g->gpu_info.gpu_dev          = NULL;
847
848         /* Run the detection if the binary was compiled with GPU support
849          * and we requested detection.
850          */
851         hwinfo_g->gpu_info.bDetectGPUs =
852             (bGPUBinary && bDetectGPUs &&
853              getenv("GMX_DISABLE_GPU_DETECTION") == NULL);
854         if (hwinfo_g->gpu_info.bDetectGPUs)
855         {
856             gmx_detect_gpus(fplog, cr);
857         }
858     }
859     /* increase the reference counter */
860     n_hwinfo++;
861
862     ret = tMPI_Thread_mutex_unlock(&hw_info_lock);
863     if (ret != 0)
864     {
865         gmx_fatal(FARGS, "Error unlocking hwinfo mutex: %s", strerror(errno));
866     }
867
868     gmx_collect_hardware_mpi();
869
870     return hwinfo_g;
871 }
872
873 static std::string detected_hardware_string(const gmx_hw_info_t *hwinfo,
874                                             bool                 bFullCpuInfo)
875 {
876     std::string s;
877
878     s  = gmx::formatString("\n");
879     s += gmx::formatString("Running on %d node%s with total",
880                            hwinfo->nphysicalnode,
881                            hwinfo->nphysicalnode == 1 ? "" : "s");
882     if (hwinfo->ncore_tot > 0)
883     {
884         s += gmx::formatString(" %d cores,", hwinfo->ncore_tot);
885     }
886     s += gmx::formatString(" %d hardware threads", hwinfo->nhwthread_tot);
887     if (hwinfo->gpu_info.bDetectGPUs)
888     {
889         s += gmx::formatString(", %d compatible GPU%s",
890                                hwinfo->ngpu_compatible_tot,
891                                hwinfo->ngpu_compatible_tot == 1 ? "" : "s");
892     }
893     else if (bGPUBinary)
894     {
895         s += gmx::formatString(" (GPU detection deactivated)");
896     }
897     s += gmx::formatString("\n");
898
899     if (hwinfo->nphysicalnode > 1)
900     {
901         /* Print per node hardware feature counts */
902         if (hwinfo->ncore_max > 0)
903         {
904             s += gmx::formatString("Cores per node:            %2d", hwinfo->ncore_min);
905             if (hwinfo->ncore_max > hwinfo->ncore_min)
906             {
907                 s += gmx::formatString(" - %2d", hwinfo->ncore_max);
908             }
909             s += gmx::formatString("\n");
910         }
911         s += gmx::formatString("Hardware threads per node: %2d", hwinfo->nhwthread_min);
912         if (hwinfo->nhwthread_max > hwinfo->nhwthread_min)
913         {
914             s += gmx::formatString(" - %2d", hwinfo->nhwthread_max);
915         }
916         s += gmx::formatString("\n");
917         if (bGPUBinary)
918         {
919             s += gmx::formatString("Compatible GPUs per node:  %2d",
920                                    hwinfo->ngpu_compatible_min);
921             if (hwinfo->ngpu_compatible_max > hwinfo->ngpu_compatible_min)
922             {
923                 s += gmx::formatString(" - %2d", hwinfo->ngpu_compatible_max);
924             }
925             s += gmx::formatString("\n");
926             if (hwinfo->ngpu_compatible_tot > 0)
927             {
928                 if (hwinfo->bIdenticalGPUs)
929                 {
930                     s += gmx::formatString("All nodes have identical type(s) of GPUs\n");
931                 }
932                 else
933                 {
934                     /* This message will also appear with identical GPU types
935                      * when at least one node has no GPU.
936                      */
937                     s += gmx::formatString("Different nodes have different type(s) and/or order of GPUs\n");
938                 }
939             }
940         }
941     }
942
943 #ifdef GMX_LIB_MPI
944     char host[255];
945     int  rank;
946
947     gmx_gethostname(host, 255);
948     MPI_Comm_rank(MPI_COMM_WORLD, &rank);
949
950     s += gmx::formatString("Hardware detected on host %s (the node of MPI rank %d):\n",
951                            host, rank);
952 #else
953     s += gmx::formatString("Hardware detected:\n");
954 #endif
955     s += gmx::formatString("  CPU info:\n");
956     if (bFullCpuInfo)
957     {
958         char buf[1024];
959
960         gmx_cpuid_formatstring(hwinfo->cpuid_info, buf, 1023);
961         buf[1023] = '\0';
962
963         s += gmx::formatString("%s", buf);
964     }
965     else
966     {
967         s += gmx::formatString("    Vendor: %s\n",
968                                gmx_cpuid_vendor_string[gmx_cpuid_vendor(hwinfo->cpuid_info)]);
969         s += gmx::formatString("    Brand:  %s\n",
970                                gmx_cpuid_brand(hwinfo->cpuid_info));
971     }
972     s += gmx::formatString("    SIMD instructions most likely to fit this hardware: %s",
973                            gmx_cpuid_simd_string[hwinfo->simd_suggest_min]);
974     if (hwinfo->simd_suggest_max > hwinfo->simd_suggest_min)
975     {
976         s += gmx::formatString(" - %s",
977                                gmx_cpuid_simd_string[hwinfo->simd_suggest_max]);
978     }
979     s += gmx::formatString("\n");
980     s += gmx::formatString("    SIMD instructions selected at GROMACS compile time: %s\n",
981                            gmx_cpuid_simd_string[gmx_compiled_simd()]);
982     if (bGPUBinary && (hwinfo->ngpu_compatible_tot > 0 ||
983                        hwinfo->gpu_info.n_dev > 0))
984     {
985         s += gmx::formatString("  GPU info:\n");
986         s += gmx::formatString("    Number of GPUs detected: %d\n",
987                                hwinfo->gpu_info.n_dev);
988         if (hwinfo->gpu_info.n_dev > 0)
989         {
990             char buf[STRLEN];
991
992             sprint_gpus(buf, &hwinfo->gpu_info);
993             s += gmx::formatString("%s\n", buf);
994         }
995     }
996
997     return s;
998 }
999
1000 void gmx_print_detected_hardware(FILE *fplog, const t_commrec *cr,
1001                                  const gmx_hw_info_t *hwinfo)
1002 {
1003     if (fplog != NULL)
1004     {
1005         std::string detected;
1006
1007         detected = detected_hardware_string(hwinfo, TRUE);
1008
1009         fprintf(fplog, "%s\n", detected.c_str());
1010     }
1011
1012     if (MULTIMASTER(cr))
1013     {
1014         std::string detected;
1015
1016         detected = detected_hardware_string(hwinfo, FALSE);
1017
1018         fprintf(stderr, "%s\n", detected.c_str());
1019     }
1020
1021     /* Check the compiled SIMD instruction set against that of the node
1022      * with the lowest SIMD level support.
1023      */
1024     gmx_cpuid_simd_check(hwinfo->simd_suggest_min, fplog, MULTIMASTER(cr));
1025
1026     /* For RDTSCP we only check on our local node and skip the MPI reduction */
1027     check_use_of_rdtscp_on_this_cpu(fplog, cr, hwinfo);
1028 }
1029
1030 void gmx_parse_gpu_ids(gmx_gpu_opt_t *gpu_opt)
1031 {
1032     char *env;
1033
1034     if (gpu_opt->gpu_id != NULL && !bGPUBinary)
1035     {
1036         gmx_fatal(FARGS, "GPU ID string set, but %s was compiled without GPU support!", ShortProgram());
1037     }
1038
1039     env = getenv("GMX_GPU_ID");
1040     if (env != NULL && gpu_opt->gpu_id != NULL)
1041     {
1042         gmx_fatal(FARGS, "GMX_GPU_ID and -gpu_id can not be used at the same time");
1043     }
1044     if (env == NULL)
1045     {
1046         env = gpu_opt->gpu_id;
1047     }
1048
1049     /* parse GPU IDs if the user passed any */
1050     if (env != NULL)
1051     {
1052         /* Parse a "plain" GPU ID string which contains a sequence of
1053          * digits corresponding to GPU IDs; the order will indicate
1054          * the process/tMPI thread - GPU assignment. */
1055         parse_digits_from_plain_string(env,
1056                                        &gpu_opt->n_dev_use,
1057                                        &gpu_opt->dev_use);
1058
1059         if (gpu_opt->n_dev_use == 0)
1060         {
1061             gmx_fatal(FARGS, "Empty GPU ID string encountered.\n%s\n",
1062                       invalid_gpuid_hint);
1063         }
1064
1065         gpu_opt->bUserSet = TRUE;
1066     }
1067 }
1068
1069 void gmx_select_gpu_ids(FILE *fplog, const t_commrec *cr,
1070                         const gmx_gpu_info_t *gpu_info,
1071                         gmx_bool bForceUseGPU,
1072                         gmx_gpu_opt_t *gpu_opt)
1073 {
1074     int              i;
1075     char             sbuf[STRLEN], stmp[STRLEN];
1076
1077     /* Bail if binary is not compiled with GPU acceleration, but this is either
1078      * explicitly (-nb gpu) or implicitly (gpu ID passed) requested. */
1079     if (bForceUseGPU && !bGPUBinary)
1080     {
1081         gmx_fatal(FARGS, "GPU acceleration requested, but %s was compiled without GPU support!", ShortProgram());
1082     }
1083
1084     if (gpu_opt->bUserSet)
1085     {
1086         /* Check the GPU IDs passed by the user.
1087          * (GPU IDs have been parsed by gmx_parse_gpu_ids before)
1088          */
1089         int *checkres;
1090         int  res;
1091
1092         snew(checkres, gpu_opt->n_dev_use);
1093
1094         res = check_selected_gpus(checkres, gpu_info, gpu_opt);
1095
1096         if (!res)
1097         {
1098             print_gpu_detection_stats(fplog, gpu_info, cr);
1099
1100             sprintf(sbuf, "Some of the requested GPUs do not exist, behave strangely, or are not compatible:\n");
1101             for (i = 0; i < gpu_opt->n_dev_use; i++)
1102             {
1103                 if (checkres[i] != egpuCompatible)
1104                 {
1105                     sprintf(stmp, "    GPU #%d: %s\n",
1106                             gpu_opt->dev_use[i],
1107                             gpu_detect_res_str[checkres[i]]);
1108                     strcat(sbuf, stmp);
1109                 }
1110             }
1111             gmx_fatal(FARGS, "%s", sbuf);
1112         }
1113
1114         sfree(checkres);
1115     }
1116     else
1117     {
1118         pick_compatible_gpus(&hwinfo_g->gpu_info, gpu_opt);
1119         limit_num_gpus_used(gpu_opt, cr->nrank_pp_intranode);
1120     }
1121
1122     /* If the user asked for a GPU, check whether we have a GPU */
1123     if (bForceUseGPU && gpu_info->n_dev_compatible == 0)
1124     {
1125         gmx_fatal(FARGS, "GPU acceleration requested, but no compatible GPUs were detected.");
1126     }
1127 }
1128
1129 /* If we detected more compatible GPUs than we can use, limit the
1130  * number. We print detailed messages about this later in
1131  * gmx_check_hw_runconf_consistency.
1132  */
1133 static void limit_num_gpus_used(gmx_gpu_opt_t *gpu_opt, int maxNumberToUse)
1134 {
1135     GMX_RELEASE_ASSERT(gpu_opt, "Invalid gpu_opt pointer passed");
1136     GMX_RELEASE_ASSERT(maxNumberToUse >= 1,
1137                        gmx::formatString("Invalid limit (%d) for the number of GPUs (detected %d compatible GPUs)",
1138                                          maxNumberToUse, gpu_opt->n_dev_compatible).c_str());
1139
1140     /* Don't increase the number of GPUs used beyond (e.g.) the number
1141        of PP ranks */
1142     gpu_opt->n_dev_use = std::min(gpu_opt->n_dev_compatible, maxNumberToUse);
1143     snew(gpu_opt->dev_use, gpu_opt->n_dev_use);
1144     for (int i = 0; i != gpu_opt->n_dev_use; ++i)
1145     {
1146         /* TODO: improve this implementation: either sort GPUs or remove the weakest here */
1147         gpu_opt->dev_use[i] = gpu_opt->dev_compatible[i];
1148     }
1149 }
1150
1151 void gmx_hardware_info_free(gmx_hw_info_t *hwinfo)
1152 {
1153     int ret;
1154
1155     ret = tMPI_Thread_mutex_lock(&hw_info_lock);
1156     if (ret != 0)
1157     {
1158         gmx_fatal(FARGS, "Error locking hwinfo mutex: %s", strerror(errno));
1159     }
1160
1161     /* decrease the reference counter */
1162     n_hwinfo--;
1163
1164
1165     if (hwinfo != hwinfo_g)
1166     {
1167         gmx_incons("hwinfo < hwinfo_g");
1168     }
1169
1170     if (n_hwinfo < 0)
1171     {
1172         gmx_incons("n_hwinfo < 0");
1173     }
1174
1175     if (n_hwinfo == 0)
1176     {
1177         gmx_cpuid_done(hwinfo_g->cpuid_info);
1178         free_gpu_info(&hwinfo_g->gpu_info);
1179         sfree(hwinfo_g);
1180     }
1181
1182     ret = tMPI_Thread_mutex_unlock(&hw_info_lock);
1183     if (ret != 0)
1184     {
1185         gmx_fatal(FARGS, "Error unlocking hwinfo mutex: %s", strerror(errno));
1186     }
1187 }