2 * This file is part of the GROMACS molecular simulation package.
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.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 #include "gromacs/legacyheaders/gmx_detect_hardware.h"
53 #ifdef GMX_NATIVE_WINDOWS
57 #include "thread_mpi/threads.h"
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"
81 static const bool bGPUBinary = TRUE;
83 # ifdef GMX_USE_OPENCL
85 static const char *gpu_implementation = "OpenCL";
86 /* Our current OpenCL implementation only supports using exactly one
87 * GPU per PP rank, so sharing is impossible */
88 static const bool bGpuSharingSupported = false;
89 /* Our current OpenCL implementation is not known to handle
90 * concurrency correctly (at context creation, JIT compilation, or JIT
91 * cache-management stages). OpenCL runtimes need not support it
92 * either; library MPI segfaults when creating OpenCL contexts;
93 * thread-MPI seems to work but is not yet known to be safe. */
94 static const bool bMultiGpuPerNodeSupported = false;
96 # else /* GMX_USE_OPENCL */
98 // Our CUDA implementation supports everything
99 static const char *gpu_implementation = "CUDA";
100 static const bool bGpuSharingSupported = true;
101 static const bool bMultiGpuPerNodeSupported = true;
103 # endif /* GMX_USE_OPENCL */
107 // Not compiled with GPU support
108 static const bool bGPUBinary = false;
109 static const char *gpu_implementation = "non-GPU";
110 static const bool bGpuSharingSupported = false;
111 static const bool bMultiGpuPerNodeSupported = false;
115 /* Names of the GPU detection/check results (see e_gpu_detect_res_t in hw_info.h). */
116 const char * const gpu_detect_res_str[egpuNR] =
118 "compatible", "inexistent", "incompatible", "insane"
121 static const char * invalid_gpuid_hint =
122 "A delimiter-free sequence of valid numeric IDs of available GPUs is expected.";
124 /* The globally shared hwinfo structure. */
125 static gmx_hw_info_t *hwinfo_g;
126 /* A reference counter for the hwinfo structure */
127 static int n_hwinfo = 0;
128 /* A lock to protect the hwinfo structure */
129 static tMPI_Thread_mutex_t hw_info_lock = TMPI_THREAD_MUTEX_INITIALIZER;
131 #define HOSTNAMELEN 80
134 static void set_gpu_ids(gmx_gpu_opt_t *gpu_opt, int nrank, int rank);
135 static int gmx_count_gpu_dev_unique(const gmx_gpu_info_t *gpu_info,
136 const gmx_gpu_opt_t *gpu_opt);
138 gmx_bool gmx_multiple_gpu_per_node_supported()
140 return bMultiGpuPerNodeSupported;
143 gmx_bool gmx_gpu_sharing_supported()
145 return bGpuSharingSupported;
148 static void sprint_gpus(char *sbuf, const gmx_gpu_info_t *gpu_info)
153 ndev = gpu_info->n_dev;
156 for (i = 0; i < ndev; i++)
158 get_gpu_device_info_string(stmp, gpu_info, i);
168 static void print_gpu_detection_stats(FILE *fplog,
169 const gmx_gpu_info_t *gpu_info,
172 char onhost[HOSTNAMELEN+10], stmp[STRLEN];
175 if (!gpu_info->bDetectGPUs)
177 /* We skipped the detection, so don't print detection stats */
181 ngpu = gpu_info->n_dev;
183 #if defined GMX_MPI && !defined GMX_THREAD_MPI
184 /* We only print the detection on one, of possibly multiple, nodes */
185 strncpy(onhost, " on host ", 10);
186 gmx_gethostname(onhost + 9, HOSTNAMELEN);
188 /* We detect all relevant GPUs */
189 strncpy(onhost, "", 1);
194 sprint_gpus(stmp, gpu_info);
195 md_print_warn(cr, fplog, "%d GPU%s detected%s:\n%s\n",
196 ngpu, (ngpu > 1) ? "s" : "", onhost, stmp);
200 md_print_warn(cr, fplog, "No GPUs detected%s\n", onhost);
204 /*! \brief Helper function for reporting GPU usage information
205 * in the mdrun log file
207 * \param[in] gpu_info Pointer to per-node GPU info struct
208 * \param[in] gpu_opt Pointer to per-node GPU options struct
209 * \param[in] numPpRanks Number of PP ranks per node
210 * \param[in] bPrintHostName Print the hostname in the usage information
211 * \return String to write to the log file
212 * \throws std::bad_alloc if out of memory */
214 makeGpuUsageReport(const gmx_gpu_info_t *gpu_info,
215 const gmx_gpu_opt_t *gpu_opt,
219 int ngpu_use = gpu_opt->n_dev_use;
220 int ngpu_comp = gpu_info->n_dev_compatible;
221 char host[HOSTNAMELEN];
225 gmx_gethostname(host, HOSTNAMELEN);
228 /* Issue a note if GPUs are available but not used */
229 if (ngpu_comp > 0 && ngpu_use < 1)
231 return gmx::formatString("%d compatible GPU%s detected in the system, but none will be used.\n"
232 "Consider trying GPU acceleration with the Verlet scheme!\n",
233 ngpu_comp, (ngpu_comp > 1) ? "s" : "");
237 if (!gpu_opt->bUserSet)
239 // gpu_opt->dev_compatible is only populated during auto-selection
240 std::string gpuIdsString =
241 formatAndJoin(gmx::constArrayRefFromArray(gpu_opt->dev_compatible,
242 gpu_opt->n_dev_compatible),
243 ",", gmx::StringFormatter("%d"));
244 bool bPluralGpus = gpu_opt->n_dev_compatible > 1;
248 output += gmx::formatString("On host %s ", host);
250 output += gmx::formatString("%d compatible GPU%s %s present, with ID%s %s\n",
251 gpu_opt->n_dev_compatible,
252 bPluralGpus ? "s" : "",
253 bPluralGpus ? "are" : "is",
254 bPluralGpus ? "s" : "",
255 gpuIdsString.c_str());
259 std::vector<int> gpuIdsInUse;
260 for (int i = 0; i < ngpu_use; i++)
262 gpuIdsInUse.push_back(get_gpu_device_id(gpu_info, gpu_opt, i));
264 std::string gpuIdsString =
265 formatAndJoin(gpuIdsInUse, ",", gmx::StringFormatter("%d"));
266 int numGpusInUse = gmx_count_gpu_dev_unique(gpu_info, gpu_opt);
267 bool bPluralGpus = numGpusInUse > 1;
271 output += gmx::formatString("On host %s ", host);
273 output += gmx::formatString("%d GPU%s %sselected for this run.\n"
274 "Mapping of GPU ID%s to the %d PP rank%s in this node: %s\n",
275 numGpusInUse, bPluralGpus ? "s" : "",
276 gpu_opt->bUserSet ? "user-" : "auto-",
277 bPluralGpus ? "s" : "",
279 (numPpRanks > 1) ? "s" : "",
280 gpuIdsString.c_str());
286 /* Give a suitable fatal error or warning if the build configuration
287 and runtime CPU do not match. */
289 check_use_of_rdtscp_on_this_cpu(FILE *fplog,
291 const gmx_hw_info_t *hwinfo)
293 gmx_bool bCpuHasRdtscp, bBinaryUsesRdtscp;
295 bBinaryUsesRdtscp = TRUE;
297 bBinaryUsesRdtscp = FALSE;
300 bCpuHasRdtscp = gmx_cpuid_feature(hwinfo->cpuid_info, GMX_CPUID_FEATURE_X86_RDTSCP);
302 if (!bCpuHasRdtscp && bBinaryUsesRdtscp)
304 gmx_fatal(FARGS, "The %s executable was compiled to use the rdtscp CPU instruction. "
305 "However, this is not supported by the current hardware and continuing would lead to a crash. "
306 "Please rebuild GROMACS with the GMX_USE_RDTSCP=OFF CMake option.",
310 if (bCpuHasRdtscp && !bBinaryUsesRdtscp)
312 md_print_warn(cr, fplog, "The current CPU can measure timings more accurately than the code in\n"
313 "%s was configured to use. This might affect your simulation\n"
314 "speed as accurate timings are needed for load-balancing.\n"
315 "Please consider rebuilding %s with the GMX_USE_RDTSCP=ON CMake option.\n",
316 ShortProgram(), ShortProgram());
320 void gmx_check_hw_runconf_consistency(FILE *fplog,
321 const gmx_hw_info_t *hwinfo,
323 const gmx_hw_opt_t *hw_opt,
327 char th_or_proc[STRLEN], th_or_proc_plural[STRLEN], pernode[STRLEN];
328 gmx_bool btMPI, bMPI, bNthreadsAuto, bEmulateGPU;
333 /* Below we only do consistency checks for PP and GPUs,
334 * this is irrelevant for PME only nodes, so in that case we return
337 if (!(cr->duty & DUTY_PP))
342 #if defined(GMX_THREAD_MPI)
345 bNthreadsAuto = (hw_opt->nthreads_tmpi < 1);
346 #elif defined(GMX_LIB_MPI)
349 bNthreadsAuto = FALSE;
353 bNthreadsAuto = FALSE;
356 /* GPU emulation detection is done later, but we need here as well
357 * -- uncool, but there's no elegant workaround */
358 bEmulateGPU = (getenv("GMX_EMULATE_GPU") != NULL);
360 if (hwinfo->gpu_info.n_dev_compatible > 0)
362 std::string gpuUseageReport;
365 gpuUseageReport = makeGpuUsageReport(&hwinfo->gpu_info,
367 cr->nrank_pp_intranode,
368 bMPI && cr->nnodes > 1);
370 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
372 /* NOTE: this print is only for and on one physical node */
373 md_print_info(cr, fplog, "%s\n", gpuUseageReport.c_str());
376 /* Need to ensure that we have enough GPUs:
377 * - need one GPU per PP node
378 * - no GPU oversubscription with tMPI
380 /* number of PP processes per node */
381 npppn = cr->nrank_pp_intranode;
384 th_or_proc_plural[0] = '\0';
387 sprintf(th_or_proc, "thread-MPI thread");
390 sprintf(th_or_proc_plural, "s");
395 sprintf(th_or_proc, "MPI process");
398 sprintf(th_or_proc_plural, "es");
400 sprintf(pernode, " per node");
404 /* neither MPI nor tMPI */
405 sprintf(th_or_proc, "process");
408 if (bUseGPU && hwinfo->gpu_info.n_dev_compatible > 0 &&
411 int ngpu_comp, ngpu_use;
412 char gpu_comp_plural[2], gpu_use_plural[2];
414 ngpu_comp = hwinfo->gpu_info.n_dev_compatible;
415 ngpu_use = hw_opt->gpu_opt.n_dev_use;
417 sprintf(gpu_comp_plural, "%s", (ngpu_comp > 1) ? "s" : "");
418 sprintf(gpu_use_plural, "%s", (ngpu_use > 1) ? "s" : "");
420 /* number of tMPI threads auto-adjusted */
421 if (btMPI && bNthreadsAuto)
423 if (hw_opt->gpu_opt.bUserSet && npppn < ngpu_use)
425 /* The user manually provided more GPUs than threads we
426 could automatically start. */
428 "%d GPU%s provided, but only %d PP thread-MPI thread%s coud be started.\n"
429 "%s requires one PP tread-MPI thread per GPU; use fewer GPUs.",
430 ngpu_use, gpu_use_plural,
431 npppn, th_or_proc_plural,
435 if (!hw_opt->gpu_opt.bUserSet && npppn < ngpu_comp)
437 /* There are more GPUs than tMPI threads; we have
438 limited the number GPUs used. */
439 md_print_warn(cr, fplog,
440 "NOTE: %d GPU%s were detected, but only %d PP thread-MPI thread%s can be started.\n"
441 " %s can use one GPU per PP tread-MPI thread, so only %d GPU%s will be used.\n",
442 ngpu_comp, gpu_comp_plural,
443 npppn, th_or_proc_plural,
444 ShortProgram(), npppn,
445 npppn > 1 ? "s" : "");
449 if (hw_opt->gpu_opt.bUserSet)
451 if (ngpu_use != npppn)
454 "Incorrect launch configuration: mismatching number of PP %s%s and GPUs%s.\n"
455 "%s was started with %d PP %s%s%s, but you provided %d GPU%s.",
456 th_or_proc, btMPI ? "s" : "es", pernode,
457 ShortProgram(), npppn, th_or_proc,
458 th_or_proc_plural, pernode,
459 ngpu_use, gpu_use_plural);
464 /* TODO Should we have a gpu_opt->n_dev_supported field? */
465 if (ngpu_comp > npppn && gmx_multiple_gpu_per_node_supported())
467 md_print_warn(cr, fplog,
468 "NOTE: potentially sub-optimal launch configuration, %s started with less\n"
469 " PP %s%s%s than GPU%s available.\n"
470 " Each PP %s can use only one GPU, %d GPU%s%s will be used.\n",
471 ShortProgram(), th_or_proc,
472 th_or_proc_plural, pernode, gpu_comp_plural,
473 th_or_proc, npppn, gpu_use_plural, pernode);
476 if (ngpu_use != npppn)
478 /* Avoid duplicate error messages.
479 * Unfortunately we can only do this at the physical node
480 * level, since the hardware setup and MPI process count
481 * might differ between physical nodes.
483 if (cr->rank_pp_intranode == 0)
485 std::string reasonForLimit;
488 !gmx_multiple_gpu_per_node_supported())
490 reasonForLimit = "can be used by ";
491 reasonForLimit += gpu_implementation;
492 reasonForLimit += " in GROMACS";
496 reasonForLimit = "was detected";
499 "Incorrect launch configuration: mismatching number of PP %s%s and GPUs%s.\n"
500 "%s was started with %d PP %s%s%s, but only %d GPU%s %s.",
501 th_or_proc, btMPI ? "s" : "es", pernode,
502 ShortProgram(), npppn, th_or_proc,
503 th_or_proc_plural, pernode,
504 ngpu_use, gpu_use_plural, reasonForLimit.c_str());
512 same_count = gmx_count_gpu_dev_shared(&hw_opt->gpu_opt);
516 md_print_info(cr, fplog,
517 "NOTE: You assigned %s to multiple %s%s.\n",
518 same_count > 1 ? "GPUs" : "a GPU", th_or_proc, btMPI ? "s" : "es");
526 /* Avoid other ranks to continue after
528 MPI_Barrier(cr->mpi_comm_mygroup);
534 /* Return 0 if none of the GPU (per node) are shared among PP ranks.
536 * Sharing GPUs among multiple PP ranks is possible when the user passes
537 * GPU IDs. Here we check for sharing and return a non-zero value when
538 * this is detected. Note that the return value represents the number of
539 * PP rank pairs that share a device.
541 int gmx_count_gpu_dev_shared(const gmx_gpu_opt_t *gpu_opt)
544 int ngpu = gpu_opt->n_dev_use;
546 if (gpu_opt->bUserSet)
550 for (i = 0; i < ngpu - 1; i++)
552 for (j = i + 1; j < ngpu; j++)
554 same_count += (gpu_opt->dev_use[i] ==
555 gpu_opt->dev_use[j]);
563 /* Count and return the number of unique GPUs (per node) selected.
565 * As sharing GPUs among multiple PP ranks is possible when the user passes
566 * GPU IDs, the number of GPUs user (per node) can be different from the
567 * number of GPU IDs selected.
569 static int gmx_count_gpu_dev_unique(const gmx_gpu_info_t *gpu_info,
570 const gmx_gpu_opt_t *gpu_opt)
572 int i, uniq_count, ngpu;
578 ngpu = gpu_info->n_dev;
582 snew(uniq_ids, ngpu);
584 /* Each element in uniq_ids will be set to 0 or 1. The n-th element set
585 * to 1 indicates that the respective GPU was selected to be used. */
586 for (i = 0; i < gpu_opt->n_dev_use; i++)
590 device_id = gmx_gpu_sharing_supported() ? get_gpu_device_id(gpu_info, gpu_opt, i) : i;
591 uniq_ids[device_id] = 1;
593 /* Count the devices used. */
594 for (i = 0; i < ngpu; i++)
596 uniq_count += uniq_ids[i];
604 static int get_ncores(gmx_cpuid_t cpuid)
606 int nprocessors, npackages, ncores_per_package, nhwthreads_per_core;
607 const int *package_id, *core_id, *hwthread_id, *locality_order;
610 rc = gmx_cpuid_topology(cpuid,
611 &nprocessors, &npackages,
612 &ncores_per_package, &nhwthreads_per_core,
613 &package_id, &core_id,
614 &hwthread_id, &locality_order);
618 return npackages*ncores_per_package;
622 /* We don't have cpuid topology info, return 0 core count */
627 /* Return the number of hardware threads supported by the current CPU.
628 * We assume that this is equal with the number of "processors"
629 * reported to be online by the OS at the time of the call. The
630 * definition of "processor" is according to an old POSIX standard.
632 * Note that the number of hardware threads is generally greater than
633 * the number of cores (e.g. x86 hyper-threading, Power). Managing the
634 * mapping of software threads to hardware threads is managed
636 static int get_nthreads_hw_avail(FILE gmx_unused *fplog, const t_commrec gmx_unused *cr)
640 #if ((defined(WIN32) || defined( _WIN32 ) || defined(WIN64) || defined( _WIN64 )) && !(defined (__CYGWIN__) || defined (__CYGWIN32__)))
643 GetSystemInfo( &sysinfo );
644 ret = sysinfo.dwNumberOfProcessors;
645 #elif defined HAVE_SYSCONF
646 /* We are probably on Unix.
647 * Now check if we have the argument to use before executing the call
649 #if defined(_SC_NPROCESSORS_ONLN)
650 ret = sysconf(_SC_NPROCESSORS_ONLN);
651 #elif defined(_SC_NPROC_ONLN)
652 ret = sysconf(_SC_NPROC_ONLN);
653 #elif defined(_SC_NPROCESSORS_CONF)
654 ret = sysconf(_SC_NPROCESSORS_CONF);
655 #elif defined(_SC_NPROC_CONF)
656 ret = sysconf(_SC_NPROC_CONF);
658 #warning "No valid sysconf argument value found. Executables will not be able to determine the number of logical cores: mdrun will use 1 thread by default!"
659 #endif /* End of check for sysconf argument values */
662 /* Neither windows nor Unix. No fscking idea how many hardware threads we have! */
668 fprintf(debug, "Detected %d hardware threads to use.\n", ret);
672 if (ret != gmx_omp_get_num_procs())
674 md_print_warn(cr, fplog,
675 "Number of logical cores detected (%d) does not match the number reported by OpenMP (%d).\n"
676 "Consider setting the launch configuration manually!",
677 ret, gmx_omp_get_num_procs());
684 static void gmx_detect_gpus(FILE *fplog, const t_commrec *cr)
688 MPI_Comm physicalnode_comm;
692 /* Under certain circumstances MPI ranks on the same physical node
693 * can not simultaneously access the same GPU(s). Therefore we run
694 * the detection only on one MPI rank per node and broadcast the info.
695 * Note that with thread-MPI only a single thread runs this code.
697 * TODO: We should also do CPU hardware detection only once on each
698 * physical node and broadcast it, instead of do it on every MPI rank.
701 /* A split of MPI_COMM_WORLD over physical nodes is only required here,
702 * so we create and destroy it locally.
704 MPI_Comm_rank(MPI_COMM_WORLD, &rank_world);
705 MPI_Comm_split(MPI_COMM_WORLD, gmx_physicalnode_id_hash(),
706 rank_world, &physicalnode_comm);
707 MPI_Comm_rank(physicalnode_comm, &rank_local);
709 /* Here there should be only one process, check this */
710 assert(cr->nnodes == 1 && cr->sim_nodeid == 0);
717 char detection_error[STRLEN] = "", sbuf[STRLEN];
719 if (detect_gpus(&hwinfo_g->gpu_info, detection_error) != 0)
721 if (detection_error[0] != '\0')
723 sprintf(sbuf, ":\n %s\n", detection_error);
729 md_print_warn(cr, fplog,
730 "NOTE: Error occurred during GPU detection%s"
731 " Can not use GPU acceleration, will fall back to CPU kernels.\n",
737 /* Broadcast the GPU info to the other ranks within this node */
738 MPI_Bcast(&hwinfo_g->gpu_info.n_dev, 1, MPI_INT, 0, physicalnode_comm);
740 if (hwinfo_g->gpu_info.n_dev > 0)
744 dev_size = hwinfo_g->gpu_info.n_dev*sizeof_gpu_dev_info();
748 hwinfo_g->gpu_info.gpu_dev =
749 (struct gmx_device_info_t *)malloc(dev_size);
751 MPI_Bcast(hwinfo_g->gpu_info.gpu_dev, dev_size, MPI_BYTE,
752 0, physicalnode_comm);
753 MPI_Bcast(&hwinfo_g->gpu_info.n_dev_compatible, 1, MPI_INT,
754 0, physicalnode_comm);
757 MPI_Comm_free(&physicalnode_comm);
761 static void gmx_collect_hardware_mpi()
765 int nrank, rank, ncore, nhwthread, ngpu, i;
769 rank_id = gmx_physicalnode_id_hash();
770 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
771 MPI_Comm_size(MPI_COMM_WORLD, &nrank);
772 ncore = hwinfo_g->ncore;
773 nhwthread = hwinfo_g->nthreads_hw_avail;
774 ngpu = hwinfo_g->gpu_info.n_dev_compatible;
775 /* Create a unique hash of the GPU type(s) in this node */
777 /* Here it might be better to only loop over the compatible GPU, but we
778 * don't have that information available and it would also require
779 * removing the device ID from the device info string.
781 for (i = 0; i < hwinfo_g->gpu_info.n_dev; i++)
785 /* Since the device ID is incorporated in the hash, the order of
786 * the GPUs affects the hash. Also two identical GPUs won't give
787 * a gpu_hash of zero after XORing.
789 get_gpu_device_info_string(stmp, &hwinfo_g->gpu_info, i);
790 gpu_hash ^= gmx_string_fullhash_func(stmp, gmx_string_hash_init);
797 MPI_Allreduce(buf, all, nrank, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
800 int nnode0, ncore0, nhwthread0, ngpu0, r;
807 for (r = 0; r < nrank; r++)
809 if (all[r] == rank_id)
811 if (!bFound && r == rank)
813 /* We are the first rank in this physical node */
816 nhwthread0 = nhwthread;
826 int sum[4], maxmin[10];
831 /* Sum values from only intra-rank 0 so we get the sum over all nodes */
837 MPI_Allreduce(buf, sum, 4, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
843 /* Store + and - values for all ranks,
844 * so we can get max+min with one MPI call.
849 buf[3] = gmx_cpuid_simd_suggest(hwinfo_g->cpuid_info);
857 MPI_Allreduce(buf, maxmin, 10, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
860 hwinfo_g->nphysicalnode = sum[0];
861 hwinfo_g->ncore_tot = sum[1];
862 hwinfo_g->ncore_min = -maxmin[5];
863 hwinfo_g->ncore_max = maxmin[0];
864 hwinfo_g->nhwthread_tot = sum[2];
865 hwinfo_g->nhwthread_min = -maxmin[6];
866 hwinfo_g->nhwthread_max = maxmin[1];
867 hwinfo_g->ngpu_compatible_tot = sum[3];
868 hwinfo_g->ngpu_compatible_min = -maxmin[7];
869 hwinfo_g->ngpu_compatible_max = maxmin[2];
870 hwinfo_g->simd_suggest_min = static_cast<enum gmx_cpuid_simd>(-maxmin[8]);
871 hwinfo_g->simd_suggest_max = static_cast<enum gmx_cpuid_simd>(maxmin[3]);
872 hwinfo_g->bIdenticalGPUs = (maxmin[4] == -maxmin[9]);
874 /* All ranks use the same pointer, protect it with a mutex */
875 tMPI_Thread_mutex_lock(&hw_info_lock);
876 hwinfo_g->nphysicalnode = 1;
877 hwinfo_g->ncore_tot = hwinfo_g->ncore;
878 hwinfo_g->ncore_min = hwinfo_g->ncore;
879 hwinfo_g->ncore_max = hwinfo_g->ncore;
880 hwinfo_g->nhwthread_tot = hwinfo_g->nthreads_hw_avail;
881 hwinfo_g->nhwthread_min = hwinfo_g->nthreads_hw_avail;
882 hwinfo_g->nhwthread_max = hwinfo_g->nthreads_hw_avail;
883 hwinfo_g->ngpu_compatible_tot = hwinfo_g->gpu_info.n_dev_compatible;
884 hwinfo_g->ngpu_compatible_min = hwinfo_g->gpu_info.n_dev_compatible;
885 hwinfo_g->ngpu_compatible_max = hwinfo_g->gpu_info.n_dev_compatible;
886 hwinfo_g->simd_suggest_min = gmx_cpuid_simd_suggest(hwinfo_g->cpuid_info);
887 hwinfo_g->simd_suggest_max = gmx_cpuid_simd_suggest(hwinfo_g->cpuid_info);
888 hwinfo_g->bIdenticalGPUs = TRUE;
889 tMPI_Thread_mutex_unlock(&hw_info_lock);
893 gmx_hw_info_t *gmx_detect_hardware(FILE *fplog, const t_commrec *cr,
894 gmx_bool bDetectGPUs)
898 /* make sure no one else is doing the same thing */
899 ret = tMPI_Thread_mutex_lock(&hw_info_lock);
902 gmx_fatal(FARGS, "Error locking hwinfo mutex: %s", strerror(errno));
905 /* only initialize the hwinfo structure if it is not already initalized */
910 /* detect CPUID info; no fuss, we don't detect system-wide
911 * -- sloppy, but that's it for now */
912 if (gmx_cpuid_init(&hwinfo_g->cpuid_info) != 0)
914 gmx_fatal_collective(FARGS, cr, NULL, "CPUID detection failed!");
917 /* get the number of cores, will be 0 when not detected */
918 hwinfo_g->ncore = get_ncores(hwinfo_g->cpuid_info);
920 /* detect number of hardware threads */
921 hwinfo_g->nthreads_hw_avail = get_nthreads_hw_avail(fplog, cr);
924 hwinfo_g->gpu_info.n_dev = 0;
925 hwinfo_g->gpu_info.n_dev_compatible = 0;
926 hwinfo_g->gpu_info.gpu_dev = NULL;
928 /* Run the detection if the binary was compiled with GPU support
929 * and we requested detection.
931 hwinfo_g->gpu_info.bDetectGPUs =
932 (bGPUBinary && bDetectGPUs &&
933 getenv("GMX_DISABLE_GPU_DETECTION") == NULL);
934 if (hwinfo_g->gpu_info.bDetectGPUs)
936 gmx_detect_gpus(fplog, cr);
939 /* increase the reference counter */
942 ret = tMPI_Thread_mutex_unlock(&hw_info_lock);
945 gmx_fatal(FARGS, "Error unlocking hwinfo mutex: %s", strerror(errno));
948 gmx_collect_hardware_mpi();
953 static std::string detected_hardware_string(const gmx_hw_info_t *hwinfo,
958 s = gmx::formatString("\n");
959 s += gmx::formatString("Running on %d node%s with total",
960 hwinfo->nphysicalnode,
961 hwinfo->nphysicalnode == 1 ? "" : "s");
962 if (hwinfo->ncore_tot > 0)
964 s += gmx::formatString(" %d cores,", hwinfo->ncore_tot);
966 s += gmx::formatString(" %d logical cores", hwinfo->nhwthread_tot);
967 if (hwinfo->gpu_info.bDetectGPUs)
969 s += gmx::formatString(", %d compatible GPU%s",
970 hwinfo->ngpu_compatible_tot,
971 hwinfo->ngpu_compatible_tot == 1 ? "" : "s");
975 s += gmx::formatString(" (GPU detection deactivated)");
977 s += gmx::formatString("\n");
979 if (hwinfo->nphysicalnode > 1)
981 /* Print per node hardware feature counts */
982 if (hwinfo->ncore_max > 0)
984 s += gmx::formatString(" Cores per node: %2d", hwinfo->ncore_min);
985 if (hwinfo->ncore_max > hwinfo->ncore_min)
987 s += gmx::formatString(" - %2d", hwinfo->ncore_max);
989 s += gmx::formatString("\n");
991 s += gmx::formatString(" Logical cores per node: %2d", hwinfo->nhwthread_min);
992 if (hwinfo->nhwthread_max > hwinfo->nhwthread_min)
994 s += gmx::formatString(" - %2d", hwinfo->nhwthread_max);
996 s += gmx::formatString("\n");
999 s += gmx::formatString(" Compatible GPUs per node: %2d",
1000 hwinfo->ngpu_compatible_min);
1001 if (hwinfo->ngpu_compatible_max > hwinfo->ngpu_compatible_min)
1003 s += gmx::formatString(" - %2d", hwinfo->ngpu_compatible_max);
1005 s += gmx::formatString("\n");
1006 if (hwinfo->ngpu_compatible_tot > 0)
1008 if (hwinfo->bIdenticalGPUs)
1010 s += gmx::formatString(" All nodes have identical type(s) of GPUs\n");
1014 /* This message will also appear with identical GPU types
1015 * when at least one node has no GPU.
1017 s += gmx::formatString(" Different nodes have different type(s) and/or order of GPUs\n");
1024 char host[HOSTNAMELEN];
1027 gmx_gethostname(host, HOSTNAMELEN);
1028 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
1030 s += gmx::formatString("Hardware detected on host %s (the node of MPI rank %d):\n",
1033 s += gmx::formatString("Hardware detected:\n");
1035 s += gmx::formatString(" CPU info:\n");
1040 gmx_cpuid_formatstring(hwinfo->cpuid_info, buf, 1023);
1043 s += gmx::formatString("%s", buf);
1047 s += gmx::formatString(" Vendor: %s\n",
1048 gmx_cpuid_vendor_string[gmx_cpuid_vendor(hwinfo->cpuid_info)]);
1049 s += gmx::formatString(" Brand: %s\n",
1050 gmx_cpuid_brand(hwinfo->cpuid_info));
1052 s += gmx::formatString(" SIMD instructions most likely to fit this hardware: %s",
1053 gmx_cpuid_simd_string[hwinfo->simd_suggest_min]);
1054 if (hwinfo->simd_suggest_max > hwinfo->simd_suggest_min)
1056 s += gmx::formatString(" - %s",
1057 gmx_cpuid_simd_string[hwinfo->simd_suggest_max]);
1059 s += gmx::formatString("\n");
1060 s += gmx::formatString(" SIMD instructions selected at GROMACS compile time: %s\n",
1061 gmx_cpuid_simd_string[gmx_compiled_simd()]);
1062 if (bGPUBinary && (hwinfo->ngpu_compatible_tot > 0 ||
1063 hwinfo->gpu_info.n_dev > 0))
1065 s += gmx::formatString(" GPU info:\n");
1066 s += gmx::formatString(" Number of GPUs detected: %d\n",
1067 hwinfo->gpu_info.n_dev);
1068 if (hwinfo->gpu_info.n_dev > 0)
1072 sprint_gpus(buf, &hwinfo->gpu_info);
1073 s += gmx::formatString("%s\n", buf);
1080 void gmx_print_detected_hardware(FILE *fplog, const t_commrec *cr,
1081 const gmx_hw_info_t *hwinfo)
1085 std::string detected;
1087 detected = detected_hardware_string(hwinfo, TRUE);
1089 fprintf(fplog, "%s\n", detected.c_str());
1092 if (MULTIMASTER(cr))
1094 std::string detected;
1096 detected = detected_hardware_string(hwinfo, FALSE);
1098 fprintf(stderr, "%s\n", detected.c_str());
1101 /* Check the compiled SIMD instruction set against that of the node
1102 * with the lowest SIMD level support.
1104 gmx_cpuid_simd_check(hwinfo->simd_suggest_min, fplog, MULTIMASTER(cr));
1106 /* For RDTSCP we only check on our local node and skip the MPI reduction */
1107 check_use_of_rdtscp_on_this_cpu(fplog, cr, hwinfo);
1110 //! \brief Return if any GPU ID (e.g in a user-supplied string) is repeated
1111 static gmx_bool anyGpuIdIsRepeated(const gmx_gpu_opt_t *gpu_opt)
1113 /* Loop over IDs in the string */
1114 for (int i = 0; i < gpu_opt->n_dev_use - 1; ++i)
1116 /* Look for the ID in location i in the following part of the
1118 for (int j = i + 1; j < gpu_opt->n_dev_use; ++j)
1120 if (gpu_opt->dev_use[i] == gpu_opt->dev_use[j])
1122 /* Same ID found in locations i and j */
1131 void gmx_parse_gpu_ids(gmx_gpu_opt_t *gpu_opt)
1135 if (gpu_opt->gpu_id != NULL && !bGPUBinary)
1137 gmx_fatal(FARGS, "GPU ID string set, but %s was compiled without GPU support!", ShortProgram());
1140 env = getenv("GMX_GPU_ID");
1141 if (env != NULL && gpu_opt->gpu_id != NULL)
1143 gmx_fatal(FARGS, "GMX_GPU_ID and -gpu_id can not be used at the same time");
1147 env = gpu_opt->gpu_id;
1150 /* parse GPU IDs if the user passed any */
1153 /* Parse a "plain" GPU ID string which contains a sequence of
1154 * digits corresponding to GPU IDs; the order will indicate
1155 * the process/tMPI thread - GPU assignment. */
1156 parse_digits_from_plain_string(env,
1157 &gpu_opt->n_dev_use,
1159 if (!gmx_multiple_gpu_per_node_supported() && 1 < gpu_opt->n_dev_use)
1161 gmx_fatal(FARGS, "The %s implementation only supports using exactly one PP rank per node", gpu_implementation);
1163 if (!gmx_gpu_sharing_supported() && anyGpuIdIsRepeated(gpu_opt))
1165 gmx_fatal(FARGS, "The %s implementation only supports using exactly one PP rank per GPU", gpu_implementation);
1167 if (gpu_opt->n_dev_use == 0)
1169 gmx_fatal(FARGS, "Empty GPU ID string encountered.\n%s\n",
1170 invalid_gpuid_hint);
1173 gpu_opt->bUserSet = TRUE;
1177 void gmx_select_gpu_ids(FILE *fplog, const t_commrec *cr,
1178 const gmx_gpu_info_t *gpu_info,
1179 gmx_bool bForceUseGPU,
1180 gmx_gpu_opt_t *gpu_opt)
1183 char sbuf[STRLEN], stmp[STRLEN];
1185 /* Bail if binary is not compiled with GPU acceleration, but this is either
1186 * explicitly (-nb gpu) or implicitly (gpu ID passed) requested. */
1187 if (bForceUseGPU && !bGPUBinary)
1189 gmx_fatal(FARGS, "GPU acceleration requested, but %s was compiled without GPU support!", ShortProgram());
1192 if (!(cr->duty & DUTY_PP))
1194 /* Our rank is not doing PP, we don't use a GPU */
1198 if (gpu_opt->bUserSet)
1200 /* Check the GPU IDs passed by the user.
1201 * (GPU IDs have been parsed by gmx_parse_gpu_ids before)
1206 snew(checkres, gpu_opt->n_dev_use);
1208 res = check_selected_gpus(checkres, gpu_info, gpu_opt);
1212 print_gpu_detection_stats(fplog, gpu_info, cr);
1214 sprintf(sbuf, "Some of the requested GPUs do not exist, behave strangely, or are not compatible:\n");
1215 for (i = 0; i < gpu_opt->n_dev_use; i++)
1217 if (checkres[i] != egpuCompatible)
1219 sprintf(stmp, " GPU #%d: %s\n",
1220 gpu_opt->dev_use[i],
1221 gpu_detect_res_str[checkres[i]]);
1225 gmx_fatal(FARGS, "%s", sbuf);
1230 else if (getenv("GMX_EMULATE_GPU") == NULL)
1232 pick_compatible_gpus(&hwinfo_g->gpu_info, gpu_opt);
1233 set_gpu_ids(gpu_opt, cr->nrank_pp_intranode, cr->rank_pp_intranode);
1236 /* If the user asked for a GPU, check whether we have a GPU */
1237 if (bForceUseGPU && gpu_info->n_dev_compatible == 0)
1239 gmx_fatal(FARGS, "GPU acceleration requested, but no compatible GPUs were detected.");
1243 /* Select the GPUs we will use. This is an operation local to each physical
1244 * node. If we have less MPI ranks than GPUs, we will waste some GPUs.
1245 * nrank and rank are the rank count and id for PP processes in our node.
1247 static void set_gpu_ids(gmx_gpu_opt_t *gpu_opt, int nrank, int rank)
1249 GMX_RELEASE_ASSERT(gpu_opt, "Invalid gpu_opt pointer passed");
1250 GMX_RELEASE_ASSERT(nrank >= 1,
1251 gmx::formatString("Invalid limit (%d) for the number of GPUs (detected %d compatible GPUs)",
1252 rank, gpu_opt->n_dev_compatible).c_str());
1254 if (gpu_opt->n_dev_compatible == 0)
1256 char host[HOSTNAMELEN];
1258 gmx_gethostname(host, HOSTNAMELEN);
1259 gmx_fatal(FARGS, "A GPU was requested on host %s, but no compatible GPUs were detected. All nodes with PP ranks need to have GPUs. If you intended to use GPU acceleration in a parallel run, you can either avoid using the nodes that don't have GPUs or place PME ranks on these nodes.", host);
1265 if (nrank > gpu_opt->n_dev_compatible)
1267 if (nrank % gpu_opt->n_dev_compatible == 0)
1269 nshare = gmx_gpu_sharing_supported() ? nrank/gpu_opt->n_dev_compatible : 1;
1275 gmx_fatal(FARGS, "The number of MPI ranks (%d) in a physical node is not a multiple of the number of GPUs (%d). Select a different number of MPI ranks or use the -gpu_id option to manually specify the GPU to be used.",
1276 nrank, gpu_opt->n_dev_compatible);
1280 /* We use a global barrier to prevent ranks from continuing with
1283 MPI_Barrier(MPI_COMM_WORLD);
1288 /* Here we will waste GPUs when nrank < gpu_opt->n_dev_compatible */
1289 gpu_opt->n_dev_use = std::min(gpu_opt->n_dev_compatible*nshare, nrank);
1290 if (!gmx_multiple_gpu_per_node_supported())
1292 gpu_opt->n_dev_use = std::min(gpu_opt->n_dev_use, 1);
1294 snew(gpu_opt->dev_use, gpu_opt->n_dev_use);
1295 for (int i = 0; i != gpu_opt->n_dev_use; ++i)
1297 /* TODO: improve this implementation: either sort GPUs or remove the weakest here */
1298 gpu_opt->dev_use[i] = gpu_opt->dev_compatible[i/nshare];
1302 void gmx_hardware_info_free(gmx_hw_info_t *hwinfo)
1306 ret = tMPI_Thread_mutex_lock(&hw_info_lock);
1309 gmx_fatal(FARGS, "Error locking hwinfo mutex: %s", strerror(errno));
1312 /* decrease the reference counter */
1316 if (hwinfo != hwinfo_g)
1318 gmx_incons("hwinfo < hwinfo_g");
1323 gmx_incons("n_hwinfo < 0");
1328 gmx_cpuid_done(hwinfo_g->cpuid_info);
1329 free_gpu_info(&hwinfo_g->gpu_info);
1333 ret = tMPI_Thread_mutex_unlock(&hw_info_lock);
1336 gmx_fatal(FARGS, "Error unlocking hwinfo mutex: %s", strerror(errno));