2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013, by the GROMACS development team, led by
5 * David van der Spoel, Berk Hess, Erik Lindahl, and including many
6 * others, as listed in the AUTHORS file in the top-level source
7 * 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.
43 #include "types/enums.h"
44 #include "types/hw_info.h"
45 #include "types/commrec.h"
46 #include "gmx_fatal.h"
47 #include "gmx_fatal_collective.h"
49 #include "gpu_utils.h"
51 #include "gmx_detect_hardware.h"
53 #include "md_logging.h"
55 #include "thread_mpi/threads.h"
57 #if ((defined(WIN32) || defined( _WIN32 ) || defined(WIN64) || defined( _WIN64 )) && !(defined (__CYGWIN__) || defined (__CYGWIN32__)))
62 const gmx_bool bGPUBinary = TRUE;
64 const gmx_bool bGPUBinary = FALSE;
67 static const char * invalid_gpuid_hint =
68 "A delimiter-free sequence of valid numeric IDs of available GPUs is expected.";
70 /* The globally shared hwinfo structure. */
71 static gmx_hw_info_t *hwinfo_g;
72 /* A reference counter for the hwinfo structure */
73 static int n_hwinfo = 0;
74 /* A lock to protect the hwinfo structure */
75 static tMPI_Thread_mutex_t hw_info_lock = TMPI_THREAD_MUTEX_INITIALIZER;
79 static void limit_num_gpus_used(gmx_gpu_opt_t *gpu_opt, int count);
81 static void sprint_gpus(char *sbuf, const gmx_gpu_info_t *gpu_info, gmx_bool bPrintAll)
86 ndev = gpu_info->ncuda_dev;
89 for (i = 0; i < ndev; i++)
91 get_gpu_device_info_string(stmp, gpu_info, i);
101 static void print_gpu_detection_stats(FILE *fplog,
102 const gmx_gpu_info_t *gpu_info,
105 char onhost[266], stmp[STRLEN];
108 if (!gpu_info->bDetectGPUs)
110 /* We skipped the detection, so don't print detection stats */
114 ngpu = gpu_info->ncuda_dev;
116 #if defined GMX_MPI && !defined GMX_THREAD_MPI
117 /* We only print the detection on one, of possibly multiple, nodes */
118 strncpy(onhost, " on host ", 10);
119 gmx_gethostname(onhost+9, 256);
121 /* We detect all relevant GPUs */
122 strncpy(onhost, "", 1);
127 sprint_gpus(stmp, gpu_info, TRUE);
128 md_print_warn(cr, fplog, "%d GPU%s detected%s:\n%s\n",
129 ngpu, (ngpu > 1) ? "s" : "", onhost, stmp);
133 md_print_warn(cr, fplog, "No GPUs detected%s\n", onhost);
137 static void print_gpu_use_stats(FILE *fplog,
138 const gmx_gpu_info_t *gpu_info,
139 const gmx_gpu_opt_t *gpu_opt,
142 char sbuf[STRLEN], stmp[STRLEN];
143 int i, ngpu_comp, ngpu_use;
145 ngpu_comp = gpu_info->ncuda_dev_compatible;
146 ngpu_use = gpu_opt->ncuda_dev_use;
148 /* Issue a note if GPUs are available but not used */
149 if (ngpu_comp > 0 && ngpu_use < 1)
152 "%d compatible GPU%s detected in the system, but none will be used.\n"
153 "Consider trying GPU acceleration with the Verlet scheme!",
154 ngpu_comp, (ngpu_comp > 1) ? "s" : "");
158 sprintf(sbuf, "%d GPU%s %sselected for this run: ",
159 ngpu_use, (ngpu_use > 1) ? "s" : "",
160 gpu_opt->bUserSet ? "user-" : "auto-");
161 for (i = 0; i < ngpu_use; i++)
163 sprintf(stmp, "#%d", get_gpu_device_id(gpu_info, gpu_opt, i));
164 if (i < ngpu_use - 1)
171 md_print_info(cr, fplog, "%s\n\n", sbuf);
174 /* Parse a "plain" GPU ID string which contains a sequence of digits corresponding
175 * to GPU IDs; the order will indicate the process/tMPI thread - GPU assignment. */
176 static void parse_gpu_id_plain_string(const char *idstr, int *nid, int **idlist)
180 *nid = strlen(idstr);
184 for (i = 0; i < *nid; i++)
186 if (idstr[i] < '0' || idstr[i] > '9')
188 gmx_fatal(FARGS, "Invalid character in GPU ID string: '%c'\n%s\n",
189 idstr[i], invalid_gpuid_hint);
191 (*idlist)[i] = idstr[i] - '0';
195 static void parse_gpu_id_csv_string(const char *idstr, int *nid, int *idlist)
197 /* XXX implement cvs format to support more than 10 different GPUs in a box. */
198 gmx_incons("Not implemented yet");
201 void gmx_check_hw_runconf_consistency(FILE *fplog,
202 const gmx_hw_info_t *hwinfo,
204 const gmx_hw_opt_t *hw_opt,
208 char sbuf[STRLEN], th_or_proc[STRLEN], th_or_proc_plural[STRLEN], pernode[STRLEN];
209 gmx_bool btMPI, bMPI, bMaxMpiThreadsSet, bNthreadsAuto, bEmulateGPU;
214 /* Below we only do consistency checks for PP and GPUs,
215 * this is irrelevant for PME only nodes, so in that case we return
218 if (!(cr->duty & DUTY_PP))
223 btMPI = bMPI = FALSE;
224 bNthreadsAuto = FALSE;
225 #if defined(GMX_THREAD_MPI)
227 bNthreadsAuto = (hw_opt->nthreads_tmpi < 1);
228 #elif defined(GMX_LIB_MPI)
232 /* GPU emulation detection is done later, but we need here as well
233 * -- uncool, but there's no elegant workaround */
234 bEmulateGPU = (getenv("GMX_EMULATE_GPU") != NULL);
235 bMaxMpiThreadsSet = (getenv("GMX_MAX_MPI_THREADS") != NULL);
237 /* check the acceleration mdrun is compiled with against hardware
239 /* TODO: Here we assume homogeneous hardware which is not necessarily
240 the case! Might not hurt to add an extra check over MPI. */
241 gmx_cpuid_acceleration_check(hwinfo->cpuid_info, fplog, SIMMASTER(cr));
243 /* NOTE: this print is only for and on one physical node */
244 print_gpu_detection_stats(fplog, &hwinfo->gpu_info, cr);
246 if (hwinfo->gpu_info.ncuda_dev_compatible > 0)
248 /* NOTE: this print is only for and on one physical node */
249 print_gpu_use_stats(fplog, &hwinfo->gpu_info, &hw_opt->gpu_opt, cr);
252 /* Need to ensure that we have enough GPUs:
253 * - need one GPU per PP node
254 * - no GPU oversubscription with tMPI
256 /* number of PP processes per node */
257 npppn = cr->nrank_pp_intranode;
260 th_or_proc_plural[0] = '\0';
263 sprintf(th_or_proc, "thread-MPI thread");
266 sprintf(th_or_proc_plural, "s");
271 sprintf(th_or_proc, "MPI process");
274 sprintf(th_or_proc_plural, "es");
276 sprintf(pernode, " per node");
280 /* neither MPI nor tMPI */
281 sprintf(th_or_proc, "process");
284 if (bUseGPU && hwinfo->gpu_info.ncuda_dev_compatible > 0 &&
287 int ngpu_comp, ngpu_use;
288 char gpu_comp_plural[2], gpu_use_plural[2];
290 ngpu_comp = hwinfo->gpu_info.ncuda_dev_compatible;
291 ngpu_use = hw_opt->gpu_opt.ncuda_dev_use;
293 sprintf(gpu_comp_plural, "%s", (ngpu_comp> 1) ? "s" : "");
294 sprintf(gpu_use_plural, "%s", (ngpu_use > 1) ? "s" : "");
296 /* number of tMPI threads auto-adjusted */
297 if (btMPI && bNthreadsAuto)
299 if (hw_opt->gpu_opt.bUserSet && npppn < ngpu_use)
301 /* The user manually provided more GPUs than threads we
302 could automatically start. */
304 "%d GPU%s provided, but only %d PP thread-MPI thread%s coud be started.\n"
305 "%s requires one PP tread-MPI thread per GPU; use fewer GPUs%s.",
306 ngpu_use, gpu_use_plural,
307 npppn, th_or_proc_plural,
308 ShortProgram(), bMaxMpiThreadsSet ? "\nor allow more threads to be used" : "");
311 if (!hw_opt->gpu_opt.bUserSet && npppn < ngpu_comp)
313 /* There are more GPUs than tMPI threads; we have
314 limited the number GPUs used. */
315 md_print_warn(cr, fplog,
316 "NOTE: %d GPU%s were detected, but only %d PP thread-MPI thread%s can be started.\n"
317 " %s can use one GPU per PP tread-MPI thread, so only %d GPU%s will be used.%s\n",
318 ngpu_comp, gpu_comp_plural,
319 npppn, th_or_proc_plural,
320 ShortProgram(), npppn,
321 npppn > 1 ? "s" : "",
322 bMaxMpiThreadsSet ? "\n Also, you can allow more threads to be used by increasing GMX_MAX_MPI_THREADS" : "");
326 if (hw_opt->gpu_opt.bUserSet)
328 if (ngpu_use != npppn)
331 "Incorrect launch configuration: mismatching number of PP %s%s and GPUs%s.\n"
332 "%s was started with %d PP %s%s%s, but you provided %d GPU%s.",
333 th_or_proc, btMPI ? "s" : "es", pernode,
334 ShortProgram(), npppn, th_or_proc,
335 th_or_proc_plural, pernode,
336 ngpu_use, gpu_use_plural);
341 if (ngpu_comp > npppn)
343 md_print_warn(cr, fplog,
344 "NOTE: potentially sub-optimal launch configuration, %s started with less\n"
345 " PP %s%s%s than GPU%s available.\n"
346 " Each PP %s can use only one GPU, %d GPU%s%s will be used.\n",
347 ShortProgram(), th_or_proc,
348 th_or_proc_plural, pernode, gpu_comp_plural,
349 th_or_proc, npppn, gpu_use_plural, pernode);
352 if (ngpu_use != npppn)
354 /* Avoid duplicate error messages.
355 * Unfortunately we can only do this at the physical node
356 * level, since the hardware setup and MPI process count
357 * might differ between physical nodes.
359 if (cr->rank_pp_intranode == 0)
362 "Incorrect launch configuration: mismatching number of PP %s%s and GPUs%s.\n"
363 "%s was started with %d PP %s%s%s, but only %d GPU%s were detected.",
364 th_or_proc, btMPI ? "s" : "es", pernode,
365 ShortProgram(), npppn, th_or_proc,
366 th_or_proc_plural, pernode,
367 ngpu_use, gpu_use_plural);
375 same_count = gmx_count_gpu_dev_shared(&hw_opt->gpu_opt);
377 if (btMPI && same_count > 0)
380 "Invalid GPU assignment: can't share a GPU among multiple thread-MPI threads.\n"
381 "Use MPI if you are sure that you want to assign a GPU to multiple threads.");
386 md_print_warn(cr, fplog,
387 "NOTE: Potentially sub-optimal launch configuration: you assigned %s to\n"
388 " multiple %s%s; this should be avoided as it can cause\n"
389 " performance loss.\n",
390 same_count > 1 ? "GPUs" : "a GPU", th_or_proc, btMPI ? "s" : "es");
398 /* Avoid other ranks to continue after
400 MPI_Barrier(cr->mpi_comm_mygroup);
406 int gmx_count_gpu_dev_shared(const gmx_gpu_opt_t *gpu_opt)
409 int ngpu = gpu_opt->ncuda_dev_use;
411 if (gpu_opt->bUserSet)
415 for (i = 0; i < ngpu - 1; i++)
417 for (j = i + 1; j < ngpu; j++)
419 same_count += (gpu_opt->cuda_dev_use[i] ==
420 gpu_opt->cuda_dev_use[j]);
429 /* Return the number of hardware threads supported by the current CPU.
430 * We assume that this is equal with the number of CPUs reported to be
431 * online by the OS at the time of the call.
433 static int get_nthreads_hw_avail(FILE *fplog, const t_commrec *cr)
437 #if ((defined(WIN32) || defined( _WIN32 ) || defined(WIN64) || defined( _WIN64 )) && !(defined (__CYGWIN__) || defined (__CYGWIN32__)))
440 GetSystemInfo( &sysinfo );
441 ret = sysinfo.dwNumberOfProcessors;
442 #elif defined HAVE_SYSCONF
443 /* We are probably on Unix.
444 * Now check if we have the argument to use before executing the call
446 #if defined(_SC_NPROCESSORS_ONLN)
447 ret = sysconf(_SC_NPROCESSORS_ONLN);
448 #elif defined(_SC_NPROC_ONLN)
449 ret = sysconf(_SC_NPROC_ONLN);
450 #elif defined(_SC_NPROCESSORS_CONF)
451 ret = sysconf(_SC_NPROCESSORS_CONF);
452 #elif defined(_SC_NPROC_CONF)
453 ret = sysconf(_SC_NPROC_CONF);
454 #endif /* End of check for sysconf argument values */
457 /* Neither windows nor Unix. No fscking idea how many CPUs we have! */
463 fprintf(debug, "Detected %d processors, will use this as the number "
464 "of supported hardware threads.\n", ret);
468 if (ret != gmx_omp_get_num_procs())
470 md_print_warn(cr, fplog,
471 "Number of CPUs detected (%d) does not match the number reported by OpenMP (%d).\n"
472 "Consider setting the launch configuration manually!",
473 ret, gmx_omp_get_num_procs());
480 gmx_hw_info_t *gmx_detect_hardware(FILE *fplog, const t_commrec *cr,
481 gmx_bool bDetectGPUs)
485 gmx_gpu_info_t gpuinfo_auto, gpuinfo_user;
488 /* make sure no one else is doing the same thing */
489 ret = tMPI_Thread_mutex_lock(&hw_info_lock);
492 gmx_fatal(FARGS, "Error locking hwinfo mutex: %s", strerror(errno));
495 /* only initialize the hwinfo structure if it is not already initalized */
500 /* detect CPUID info; no fuss, we don't detect system-wide
501 * -- sloppy, but that's it for now */
502 if (gmx_cpuid_init(&hwinfo_g->cpuid_info) != 0)
504 gmx_fatal_collective(FARGS, cr, NULL, "CPUID detection failed!");
507 /* detect number of hardware threads */
508 hwinfo_g->nthreads_hw_avail = get_nthreads_hw_avail(fplog, cr);
511 hwinfo_g->gpu_info.ncuda_dev = 0;
512 hwinfo_g->gpu_info.cuda_dev = NULL;
513 hwinfo_g->gpu_info.ncuda_dev_compatible = 0;
515 /* Run the detection if the binary was compiled with GPU support
516 * and we requested detection.
518 hwinfo_g->gpu_info.bDetectGPUs =
519 (bGPUBinary && bDetectGPUs &&
520 getenv("GMX_DISABLE_GPU_DETECTION") == NULL);
521 if (hwinfo_g->gpu_info.bDetectGPUs)
523 char detection_error[STRLEN];
525 if (detect_cuda_gpus(&hwinfo_g->gpu_info, detection_error) != 0)
527 if (detection_error != NULL && detection_error[0] != '\0')
529 sprintf(sbuf, ":\n %s\n", detection_error);
535 md_print_warn(cr, fplog,
536 "NOTE: Error occurred during GPU detection%s"
537 " Can not use GPU acceleration, will fall back to CPU kernels.\n",
542 /* increase the reference counter */
545 ret = tMPI_Thread_mutex_unlock(&hw_info_lock);
548 gmx_fatal(FARGS, "Error unlocking hwinfo mutex: %s", strerror(errno));
554 void gmx_parse_gpu_ids(gmx_gpu_opt_t *gpu_opt)
558 if (gpu_opt->gpu_id != NULL && !bGPUBinary)
560 gmx_fatal(FARGS, "GPU ID string set, but %s was compiled without GPU support!", ShortProgram());
563 env = getenv("GMX_GPU_ID");
564 if (env != NULL && gpu_opt->gpu_id != NULL)
566 gmx_fatal(FARGS, "GMX_GPU_ID and -gpu_id can not be used at the same time");
570 env = gpu_opt->gpu_id;
573 /* parse GPU IDs if the user passed any */
576 parse_gpu_id_plain_string(env,
577 &gpu_opt->ncuda_dev_use,
578 &gpu_opt->cuda_dev_use);
580 if (gpu_opt->ncuda_dev_use == 0)
582 gmx_fatal(FARGS, "Empty GPU ID string encountered.\n%s\n",
586 gpu_opt->bUserSet = TRUE;
590 void gmx_select_gpu_ids(FILE *fplog, const t_commrec *cr,
591 const gmx_gpu_info_t *gpu_info,
592 gmx_bool bForceUseGPU,
593 gmx_gpu_opt_t *gpu_opt)
597 char sbuf[STRLEN], stmp[STRLEN];
599 /* Bail if binary is not compiled with GPU acceleration, but this is either
600 * explicitly (-nb gpu) or implicitly (gpu ID passed) requested. */
601 if (bForceUseGPU && !bGPUBinary)
603 gmx_fatal(FARGS, "GPU acceleration requested, but %s was compiled without GPU support!", ShortProgram());
606 if (gpu_opt->bUserSet)
608 /* Check the GPU IDs passed by the user.
609 * (GPU IDs have been parsed by gmx_parse_gpu_ids before)
614 snew(checkres, gpu_opt->ncuda_dev_use);
616 res = check_selected_cuda_gpus(checkres, gpu_info, gpu_opt);
620 print_gpu_detection_stats(fplog, gpu_info, cr);
622 sprintf(sbuf, "Some of the requested GPUs do not exist, behave strangely, or are not compatible:\n");
623 for (i = 0; i < gpu_opt->ncuda_dev_use; i++)
625 if (checkres[i] != egpuCompatible)
627 sprintf(stmp, " GPU #%d: %s\n",
628 gpu_opt->cuda_dev_use[i],
629 gpu_detect_res_str[checkres[i]]);
633 gmx_fatal(FARGS, "%s", sbuf);
640 pick_compatible_gpus(&hwinfo_g->gpu_info, gpu_opt);
642 if (gpu_opt->ncuda_dev_use > cr->nrank_pp_intranode)
644 /* We picked more GPUs than we can use: limit the number.
645 * We print detailed messages about this later in
646 * gmx_check_hw_runconf_consistency.
648 limit_num_gpus_used(gpu_opt, cr->nrank_pp_intranode);
651 gpu_opt->bUserSet = FALSE;
654 /* If the user asked for a GPU, check whether we have a GPU */
655 if (bForceUseGPU && gpu_info->ncuda_dev_compatible == 0)
657 gmx_fatal(FARGS, "GPU acceleration requested, but no compatible GPUs were detected.");
661 static void limit_num_gpus_used(gmx_gpu_opt_t *gpu_opt, int count)
667 ndev_use = gpu_opt->ncuda_dev_use;
669 if (count > ndev_use)
671 /* won't increase the # of GPUs */
678 sprintf(sbuf, "Limiting the number of GPUs to <1 doesn't make sense (detected %d, %d requested)!",
683 /* TODO: improve this implementation: either sort GPUs or remove the weakest here */
684 gpu_opt->ncuda_dev_use = count;
687 void gmx_hardware_info_free(gmx_hw_info_t *hwinfo)
691 ret = tMPI_Thread_mutex_lock(&hw_info_lock);
694 gmx_fatal(FARGS, "Error locking hwinfo mutex: %s", strerror(errno));
697 /* decrease the reference counter */
701 if (hwinfo != hwinfo_g)
703 gmx_incons("hwinfo < hwinfo_g");
708 gmx_incons("n_hwinfo < 0");
713 gmx_cpuid_done(hwinfo_g->cpuid_info);
714 free_gpu_info(&hwinfo_g->gpu_info);
718 ret = tMPI_Thread_mutex_unlock(&hw_info_lock);
721 gmx_fatal(FARGS, "Error unlocking hwinfo mutex: %s", strerror(errno));