2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 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.
38 #include "resource-division.h"
48 #include "gromacs/legacyheaders/gmx_detect_hardware.h"
49 #include "gromacs/legacyheaders/gmx_omp_nthreads.h"
50 #include "gromacs/legacyheaders/md_logging.h"
51 #include "gromacs/legacyheaders/names.h"
52 #include "gromacs/legacyheaders/types/commrec.h"
53 #include "gromacs/utility/fatalerror.h"
57 /* The minimum number of atoms per tMPI thread. With fewer atoms than this,
58 * the number of threads will get lowered.
60 static const int min_atoms_per_mpi_thread = 90;
61 static const int min_atoms_per_gpu = 900;
62 #endif /* GMX_THREAD_MPI */
64 /* TODO choose nthreads_omp based on hardware topology
65 when we have a hardware topology detection library */
66 /* First we consider the case of no MPI (1 MPI rank).
67 * In general, when running up to 4 threads, OpenMP should be faster.
68 * Note: on AMD Bulldozer we should avoid running OpenMP over two dies.
69 * On Intel>=Nehalem running OpenMP on a single CPU is always faster,
70 * even on two CPUs it's usually faster (but with many OpenMP threads
71 * it could be faster not to use HT, currently we always use HT).
72 * On Nehalem/Westmere we want to avoid running 16 threads over
73 * two CPUs with HT, so we need a limit<16; thus we use 12.
74 * A reasonable limit for Intel Sandy and Ivy bridge,
75 * not knowing the topology, is 16 threads.
76 * Below we check for Intel and AVX, which for now includes
77 * Sandy/Ivy Bridge, Has/Broadwell. By checking for AVX instead of
78 * model numbers we ensure also future Intel CPUs are covered.
80 const int nthreads_omp_always_faster_default = 8;
81 const int nthreads_omp_always_faster_Nehalem = 12;
82 const int nthreads_omp_always_faster_Intel_AVX = 16;
83 /* For CPU only runs the fastest options are usually MPI or OpenMP only.
84 * With one GPU, using MPI only is almost never optimal, so we need to
85 * compare running pure OpenMP with combined MPI+OpenMP. This means higher
86 * OpenMP threads counts can still be ok. Multiplying the numbers above
87 * by a factor of 2 seems to be a good estimate.
89 const int nthreads_omp_always_faster_gpu_fac = 2;
91 /* This is the case with MPI (2 or more MPI PP ranks).
92 * By default we will terminate with a fatal error when more than 8
93 * OpenMP thread are (indirectly) requested, since using less threads
94 * nearly always results in better performance.
95 * With thread-mpi and multiple GPUs or one GPU and too many threads
96 * we first try 6 OpenMP threads and then less until the number of MPI ranks
97 * is divisible by the number of GPUs.
99 #if defined GMX_OPENMP && defined GMX_MPI
100 const int nthreads_omp_mpi_ok_max = 8;
101 const int nthreads_omp_mpi_ok_min_cpu = 1;
103 const int nthreads_omp_mpi_ok_min_gpu = 2;
104 const int nthreads_omp_mpi_target_max = 6;
107 #ifdef GMX_USE_OPENCL
108 static const bool bGpuSharingSupported = false;
110 static const bool bGpuSharingSupported = true;
114 static int nthreads_omp_always_faster(gmx_cpuid_t cpuid_info, gmx_bool bUseGPU)
118 if (gmx_cpuid_vendor(cpuid_info) == GMX_CPUID_VENDOR_INTEL &&
119 gmx_cpuid_feature(cpuid_info, GMX_CPUID_FEATURE_X86_AVX))
121 nth = nthreads_omp_always_faster_Intel_AVX;
123 else if (gmx_cpuid_is_intel_nehalem(cpuid_info))
125 nth = nthreads_omp_always_faster_Nehalem;
129 nth = nthreads_omp_always_faster_default;
134 nth *= nthreads_omp_always_faster_gpu_fac;
137 nth = std::min(nth, GMX_OPENMP_MAX_THREADS);
142 static int get_tmpi_omp_thread_division(const gmx_hw_info_t *hwinfo,
143 const gmx_hw_opt_t *hw_opt,
149 assert(nthreads_tot > 0);
151 /* There are no separate PME nodes here, as we ensured in
152 * check_and_update_hw_opt that nthreads_tmpi>0 with PME nodes
153 * and a conditional ensures we would not have ended up here.
154 * Note that separate PME nodes might be switched on later.
159 if (nthreads_tot < nrank)
161 /* #thread < #gpu is very unlikely, but if so: waste gpu(s) */
162 nrank = nthreads_tot;
164 else if (bGpuSharingSupported &&
165 (nthreads_tot > nthreads_omp_always_faster(hwinfo->cpuid_info,
167 (ngpu > 1 && nthreads_tot/ngpu > nthreads_omp_mpi_target_max)))
169 /* The high OpenMP thread count will likely result in sub-optimal
170 * performance. Increase the rank count to reduce the thread count
171 * per rank. This will lead to GPU sharing by MPI ranks/threads.
175 /* Increase the rank count as long as have we more than 6 OpenMP
176 * threads per rank or the number of hardware threads is not
177 * divisible by the rank count. Don't go below 2 OpenMP threads.
185 while (nthreads_tot/nrank > nthreads_omp_mpi_target_max ||
186 (nthreads_tot/(ngpu*(nshare + 1)) >= nthreads_omp_mpi_ok_min_gpu && nthreads_tot % nrank != 0));
189 else if (hw_opt->nthreads_omp > 0)
191 /* Here we could oversubscribe, when we do, we issue a warning later */
192 nrank = std::max(1, nthreads_tot/hw_opt->nthreads_omp);
196 if (nthreads_tot <= nthreads_omp_always_faster(hwinfo->cpuid_info,
199 /* Use pure OpenMP parallelization */
204 /* Don't use OpenMP parallelization */
205 nrank = nthreads_tot;
213 #ifdef GMX_THREAD_MPI
214 /* Get the number of MPI ranks to use for thread-MPI based on how many
215 * were requested, which algorithms we're using,
216 * and how many particles there are.
217 * At the point we have already called check_and_update_hw_opt.
218 * Thus all options should be internally consistent and consistent
219 * with the hardware, except that ntmpi could be larger than #GPU.
221 int get_nthreads_mpi(const gmx_hw_info_t *hwinfo,
222 const gmx_hw_opt_t *hw_opt,
223 const t_inputrec *inputrec,
224 const gmx_mtop_t *mtop,
228 int nthreads_hw, nthreads_tot_max, nrank, ngpu;
229 int min_atoms_per_mpi_rank;
232 /* Check if an algorithm does not support parallel simulation. */
233 if (inputrec->eI == eiLBFGS ||
234 inputrec->coulombtype == eelEWALD)
236 md_print_warn(cr, fplog, "The integration or electrostatics algorithm doesn't support parallel runs. Using a single thread-MPI thread.\n");
237 if (hw_opt->nthreads_tmpi > 1)
239 gmx_fatal(FARGS, "You asked for more than 1 thread-MPI thread, but an algorithm doesn't support that");
245 if (hw_opt->nthreads_tmpi > 0)
247 /* Trivial, return right away */
248 return hw_opt->nthreads_tmpi;
251 nthreads_hw = hwinfo->nthreads_hw_avail;
253 if (nthreads_hw <= 0)
255 /* This should normally not happen, but if it does, we handle it */
256 gmx_fatal(FARGS, "The number of available hardware threads can not be detected, please specify the number of MPI ranks and the number of OpenMP threads (if supported) manually with options -ntmpi and -ntomp, respectively");
259 /* How many total (#tMPI*#OpenMP) threads can we start? */
260 if (hw_opt->nthreads_tot > 0)
262 nthreads_tot_max = hw_opt->nthreads_tot;
266 nthreads_tot_max = nthreads_hw;
269 bCanUseGPU = (inputrec->cutoff_scheme == ecutsVERLET &&
270 hwinfo->gpu_info.n_dev_compatible > 0);
273 ngpu = hwinfo->gpu_info.n_dev_compatible;
280 if (inputrec->cutoff_scheme == ecutsGROUP)
282 /* We checked this before, but it doesn't hurt to do it once more */
283 assert(hw_opt->nthreads_omp == 1);
287 get_tmpi_omp_thread_division(hwinfo, hw_opt, nthreads_tot_max, ngpu);
289 if (inputrec->eI == eiNM || EI_TPI(inputrec->eI))
291 /* Dims/steps are divided over the nodes iso splitting the atoms.
292 * With NM we can't have more ranks than #atoms*#dim. With TPI it's
293 * unlikely we have fewer atoms than ranks, and if so, communication
294 * would become a bottleneck, so we set the limit to 1 atom/rank.
296 min_atoms_per_mpi_rank = 1;
302 min_atoms_per_mpi_rank = min_atoms_per_gpu;
306 min_atoms_per_mpi_rank = min_atoms_per_mpi_thread;
310 if (mtop->natoms/nrank < min_atoms_per_mpi_rank)
314 /* the rank number was chosen automatically, but there are too few
315 atoms per rank, so we need to reduce the rank count */
316 nrank_new = std::max(1, mtop->natoms/min_atoms_per_mpi_rank);
318 /* Avoid partial use of Hyper-Threading */
319 if (gmx_cpuid_x86_smt(hwinfo->cpuid_info) == GMX_CPUID_X86_SMT_ENABLED &&
320 nrank_new > nthreads_hw/2 && nrank_new < nthreads_hw)
322 nrank_new = nthreads_hw/2;
325 /* If the user specified the total thread count, ensure this is
326 * divisible by the number of ranks.
327 * It is quite likely that we have too many total threads compared
328 * to the size of the system, but if the user asked for this many
329 * threads we should respect that.
331 while (hw_opt->nthreads_tot > 0 &&
332 hw_opt->nthreads_tot % nrank_new != 0)
337 /* Avoid large prime numbers in the rank count */
340 /* Use only 6,8,10 with additional factors of 2 */
344 while (3*fac*2 <= nrank_new)
349 nrank_new = (nrank_new/fac)*fac;
353 /* Avoid 5, since small system won't fit 5 domains along
354 * a dimension. This might lead to waisting some cores, but this
355 * will have a small impact in this regime of very small systems.
365 fprintf(stderr, "\n");
366 fprintf(stderr, "NOTE: Parallelization is limited by the small number of atoms,\n");
367 fprintf(stderr, " only starting %d thread-MPI ranks.\n", nrank);
368 fprintf(stderr, " You can use the -nt and/or -ntmpi option to optimize the number of threads.\n\n");
373 #endif /* GMX_THREAD_MPI */
376 void check_resource_division_efficiency(const gmx_hw_info_t *hwinfo,
377 const gmx_hw_opt_t *hw_opt,
382 #if defined GMX_OPENMP && defined GMX_MPI
383 int nth_omp_min, nth_omp_max, ngpu;
385 #ifdef GMX_THREAD_MPI
386 const char *mpi_option = " (option -ntmpi)";
388 const char *mpi_option = "";
391 /* This function should be called after thread-MPI (when configured) and
392 * OpenMP have been initialized. Check that here.
394 #ifdef GMX_THREAD_MPI
395 assert(nthreads_omp_always_faster_default >= nthreads_omp_mpi_ok_max);
396 assert(hw_opt->nthreads_tmpi >= 1);
398 assert(gmx_omp_nthreads_get(emntDefault) >= 1);
400 nth_omp_min = gmx_omp_nthreads_get(emntDefault);
401 nth_omp_max = gmx_omp_nthreads_get(emntDefault);
402 ngpu = hw_opt->gpu_opt.n_dev_use;
404 /* Thread-MPI seems to have a bug with reduce on 1 node, so use a cond. */
405 if (cr->nnodes + cr->npmenodes > 1)
407 int count[3], count_max[3];
409 count[0] = -nth_omp_min;
410 count[1] = nth_omp_max;
413 MPI_Allreduce(count, count_max, 3, MPI_INT, MPI_MAX, cr->mpi_comm_mysim);
415 /* In case of an inhomogeneous run setup we use the maximum counts */
416 nth_omp_min = -count_max[0];
417 nth_omp_max = count_max[1];
421 int nthreads_omp_mpi_ok_min;
425 nthreads_omp_mpi_ok_min = nthreads_omp_mpi_ok_min_cpu;
429 /* With GPUs we set the minimum number of OpenMP threads to 2 to catch
430 * cases where the user specifies #ranks == #cores.
432 nthreads_omp_mpi_ok_min = nthreads_omp_mpi_ok_min_gpu;
435 if (DOMAINDECOMP(cr) && cr->nnodes > 1)
437 if (nth_omp_max < nthreads_omp_mpi_ok_min ||
438 (!(ngpu > 0 && !bGpuSharingSupported) &&
439 nth_omp_max > nthreads_omp_mpi_ok_max))
441 /* Note that we print target_max here, not ok_max */
442 sprintf(buf, "Your choice of number of MPI ranks and amount of resources results in using %d OpenMP threads per rank, which is most likely inefficient. The optimum is usually between %d and %d threads per rank.",
444 nthreads_omp_mpi_ok_min,
445 nthreads_omp_mpi_target_max);
449 md_print_warn(cr, fplog, "NOTE: %s\n", buf);
453 /* This fatal error, and the one below, is nasty, but it's
454 * probably the only way to ensure that all users don't waste
455 * a lot of resources, since many users don't read logs/stderr.
457 gmx_fatal(FARGS, "%s If you want to run with this setup, specify the -ntomp option. But we suggest to change the number of MPI ranks%s.", buf, mpi_option);
463 /* No domain decomposition (or only one domain) */
464 if (!(ngpu > 0 && !bGpuSharingSupported) &&
465 nth_omp_max > nthreads_omp_always_faster(hwinfo->cpuid_info, ngpu > 0))
467 /* To arrive here, the user/system set #ranks and/or #OMPthreads */
471 bEnvSet = (getenv("OMP_NUM_THREADS") != NULL);
473 if (bNTOptSet || bEnvSet)
475 sprintf(buf2, "You requested %d OpenMP threads", nth_omp_max);
479 sprintf(buf2, "Your choice of %d MPI rank%s and the use of %d total threads %sleads to the use of %d OpenMP threads",
480 cr->nnodes + cr->npmenodes,
481 cr->nnodes + cr->npmenodes == 1 ? "" : "s",
482 hw_opt->nthreads_tot > 0 ? hw_opt->nthreads_tot : hwinfo->nthreads_hw_avail,
483 hwinfo->nphysicalnode > 1 ? "on a node " : "",
486 sprintf(buf, "%s, whereas we expect the optimum to be with more MPI ranks with %d to %d OpenMP threads.",
487 buf2, nthreads_omp_mpi_ok_min, nthreads_omp_mpi_target_max);
489 /* We can not quit with a fatal error when OMP_NUM_THREADS is set
490 * with different values per rank or node, since in that case
491 * the user can not set -ntomp to override the error.
493 if (bNTOptSet || (bEnvSet && nth_omp_min != nth_omp_max))
495 md_print_warn(cr, fplog, "NOTE: %s\n", buf);
499 gmx_fatal(FARGS, "%s If you want to run with this many OpenMP threads, specify the -ntomp option. But we suggest to increase the number of MPI ranks%s.", buf, mpi_option);
503 #else /* GMX_OPENMP && GMX_MPI */
504 /* No OpenMP and/or MPI: it doesn't make much sense to check */
505 GMX_UNUSED_VALUE(hw_opt);
506 GMX_UNUSED_VALUE(bNTOptSet);
507 /* Check if we have more than 1 physical core, if detected,
508 * or more than 1 hardware thread if physical cores were not detected.
510 if ((hwinfo->ncore > 1) ||
511 (hwinfo->ncore == 0 && hwinfo->nthreads_hw_avail > 1))
513 md_print_warn(cr, fplog, "NOTE: GROMACS was compiled without OpenMP and (thread-)MPI support, can only use a single CPU core\n");
515 #endif /* GMX_OPENMP && GMX_MPI */
519 static void print_hw_opt(FILE *fp, const gmx_hw_opt_t *hw_opt)
521 fprintf(fp, "hw_opt: nt %d ntmpi %d ntomp %d ntomp_pme %d gpu_id '%s'\n",
522 hw_opt->nthreads_tot,
523 hw_opt->nthreads_tmpi,
524 hw_opt->nthreads_omp,
525 hw_opt->nthreads_omp_pme,
526 hw_opt->gpu_opt.gpu_id != NULL ? hw_opt->gpu_opt.gpu_id : "");
529 /* Checks we can do when we don't (yet) know the cut-off scheme */
530 void check_and_update_hw_opt_1(gmx_hw_opt_t *hw_opt,
531 gmx_bool bIsSimMaster)
533 gmx_omp_nthreads_read_env(&hw_opt->nthreads_omp, bIsSimMaster);
535 #ifndef GMX_THREAD_MPI
536 if (hw_opt->nthreads_tot > 0)
538 gmx_fatal(FARGS, "Setting the total number of threads is only supported with thread-MPI and GROMACS was compiled without thread-MPI");
540 if (hw_opt->nthreads_tmpi > 0)
542 gmx_fatal(FARGS, "Setting the number of thread-MPI threads is only supported with thread-MPI and GROMACS was compiled without thread-MPI");
547 if (hw_opt->nthreads_omp > 1)
549 gmx_fatal(FARGS, "More than 1 OpenMP thread requested, but GROMACS was compiled without OpenMP support");
551 hw_opt->nthreads_omp = 1;
554 if (hw_opt->nthreads_tot > 0 && hw_opt->nthreads_omp_pme <= 0)
556 /* We have the same number of OpenMP threads for PP and PME processes,
557 * thus we can perform several consistency checks.
559 if (hw_opt->nthreads_tmpi > 0 &&
560 hw_opt->nthreads_omp > 0 &&
561 hw_opt->nthreads_tot != hw_opt->nthreads_tmpi*hw_opt->nthreads_omp)
563 gmx_fatal(FARGS, "The total number of threads requested (%d) does not match the thread-MPI threads (%d) times the OpenMP threads (%d) requested",
564 hw_opt->nthreads_tot, hw_opt->nthreads_tmpi, hw_opt->nthreads_omp);
567 if (hw_opt->nthreads_tmpi > 0 &&
568 hw_opt->nthreads_tot % hw_opt->nthreads_tmpi != 0)
570 gmx_fatal(FARGS, "The total number of threads requested (%d) is not divisible by the number of thread-MPI threads requested (%d)",
571 hw_opt->nthreads_tot, hw_opt->nthreads_tmpi);
574 if (hw_opt->nthreads_omp > 0 &&
575 hw_opt->nthreads_tot % hw_opt->nthreads_omp != 0)
577 gmx_fatal(FARGS, "The total number of threads requested (%d) is not divisible by the number of OpenMP threads requested (%d)",
578 hw_opt->nthreads_tot, hw_opt->nthreads_omp);
581 if (hw_opt->nthreads_tmpi > 0 &&
582 hw_opt->nthreads_omp <= 0)
584 hw_opt->nthreads_omp = hw_opt->nthreads_tot/hw_opt->nthreads_tmpi;
589 if (hw_opt->nthreads_omp > 1)
591 gmx_fatal(FARGS, "OpenMP threads are requested, but GROMACS was compiled without OpenMP support");
595 if (hw_opt->nthreads_omp_pme > 0 && hw_opt->nthreads_omp <= 0)
597 gmx_fatal(FARGS, "You need to specify -ntomp in addition to -ntomp_pme");
600 if (hw_opt->nthreads_tot == 1)
602 hw_opt->nthreads_tmpi = 1;
604 if (hw_opt->nthreads_omp > 1)
606 gmx_fatal(FARGS, "You requested %d OpenMP threads with %d total threads",
607 hw_opt->nthreads_tmpi, hw_opt->nthreads_tot);
609 hw_opt->nthreads_omp = 1;
612 if (hw_opt->nthreads_omp_pme <= 0 && hw_opt->nthreads_omp > 0)
614 hw_opt->nthreads_omp_pme = hw_opt->nthreads_omp;
617 /* Parse GPU IDs, if provided.
618 * We check consistency with the tMPI thread count later.
620 gmx_parse_gpu_ids(&hw_opt->gpu_opt);
622 #ifdef GMX_THREAD_MPI
623 if (hw_opt->gpu_opt.n_dev_use > 0 && hw_opt->nthreads_tmpi == 0)
625 /* Set the number of MPI threads equal to the number of GPUs */
626 hw_opt->nthreads_tmpi = hw_opt->gpu_opt.n_dev_use;
628 if (hw_opt->nthreads_tot > 0 &&
629 hw_opt->nthreads_tmpi > hw_opt->nthreads_tot)
631 /* We have more GPUs than total threads requested.
632 * We choose to (later) generate a mismatch error,
633 * instead of launching more threads than requested.
635 hw_opt->nthreads_tmpi = hw_opt->nthreads_tot;
642 print_hw_opt(debug, hw_opt);
646 /* Checks we can do when we know the cut-off scheme */
647 void check_and_update_hw_opt_2(gmx_hw_opt_t *hw_opt,
650 if (cutoff_scheme == ecutsGROUP)
652 /* We only have OpenMP support for PME only nodes */
653 if (hw_opt->nthreads_omp > 1)
655 gmx_fatal(FARGS, "OpenMP threads have been requested with cut-off scheme %s, but these are only supported with cut-off scheme %s",
656 ecutscheme_names[cutoff_scheme],
657 ecutscheme_names[ecutsVERLET]);
659 hw_opt->nthreads_omp = 1;
662 if (hw_opt->nthreads_omp_pme <= 0 && hw_opt->nthreads_omp > 0)
664 hw_opt->nthreads_omp_pme = hw_opt->nthreads_omp;
669 print_hw_opt(debug, hw_opt);