2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2015,2016,2017, 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"
47 #include "gromacs/hardware/cpuinfo.h"
48 #include "gromacs/hardware/detecthardware.h"
49 #include "gromacs/hardware/hardwareassign.h"
50 #include "gromacs/hardware/hardwaretopology.h"
51 #include "gromacs/hardware/hw_info.h"
52 #include "gromacs/mdlib/gmx_omp_nthreads.h"
53 #include "gromacs/mdtypes/commrec.h"
54 #include "gromacs/mdtypes/inputrec.h"
55 #include "gromacs/mdtypes/md_enums.h"
56 #include "gromacs/topology/topology.h"
57 #include "gromacs/utility/baseversion.h"
58 #include "gromacs/utility/fatalerror.h"
59 #include "gromacs/utility/gmxassert.h"
60 #include "gromacs/utility/logger.h"
61 #include "gromacs/utility/stringutil.h"
64 /* DISCLAIMER: All the atom count and thread numbers below are heuristic.
65 * The real switching points will depend on the system simulation,
66 * the algorithms used and the hardware it's running on, as well as if there
67 * are other jobs running on the same machine. We try to take into account
68 * factors that have a large influence, such as recent Intel CPUs being
69 * much better at wide multi-threading. The remaining factors should
70 * (hopefully) have a small influence, such that the performance just before
71 * and after a switch point doesn't change too much.
74 //! Constant used to help minimize preprocessed code
75 static const bool bHasOmpSupport
= GMX_OPENMP
;
78 /* The minimum number of atoms per tMPI thread. With fewer atoms than this,
79 * the number of threads will get lowered.
81 static const int min_atoms_per_mpi_thread
= 90;
82 static const int min_atoms_per_gpu
= 900;
83 #endif /* GMX_THREAD_MPI */
85 /* TODO choose nthreads_omp based on hardware topology
86 when we have a hardware topology detection library */
87 /* First we consider the case of no MPI (1 MPI rank).
88 * In general, when running up to 8 threads, OpenMP should be faster.
89 * Note: on AMD Bulldozer we should avoid running OpenMP over two dies.
90 * On Intel>=Nehalem running OpenMP on a single CPU is always faster,
91 * even on two CPUs it's usually faster (but with many OpenMP threads
92 * it could be faster not to use HT, currently we always use HT).
93 * On Nehalem/Westmere we want to avoid running 16 threads over
94 * two CPUs with HT, so we need a limit<16; thus we use 12.
95 * A reasonable limit for Intel Sandy and Ivy bridge,
96 * not knowing the topology, is 16 threads.
97 * Below we check for Intel and AVX, which for now includes
98 * Sandy/Ivy Bridge, Has/Broadwell. By checking for AVX instead of
99 * model numbers we ensure also future Intel CPUs are covered.
101 const int nthreads_omp_faster_default
= 8;
102 const int nthreads_omp_faster_Nehalem
= 12;
103 const int nthreads_omp_faster_Intel_AVX
= 16;
104 const int nthreads_omp_faster_AMD_Ryzen
= 16;
105 /* For CPU only runs the fastest options are usually MPI or OpenMP only.
106 * With one GPU, using MPI only is almost never optimal, so we need to
107 * compare running pure OpenMP with combined MPI+OpenMP. This means higher
108 * OpenMP threads counts can still be ok. Multiplying the numbers above
109 * by a factor of 2 seems to be a good estimate.
111 const int nthreads_omp_faster_gpu_fac
= 2;
113 /* This is the case with MPI (2 or more MPI PP ranks).
114 * By default we will terminate with a fatal error when more than 8
115 * OpenMP thread are (indirectly) requested, since using less threads
116 * nearly always results in better performance.
117 * With thread-mpi and multiple GPUs or one GPU and too many threads
118 * we first try 6 OpenMP threads and then less until the number of MPI ranks
119 * is divisible by the number of GPUs.
121 #if GMX_OPENMP && GMX_MPI
122 const int nthreads_omp_mpi_ok_max
= 8;
123 const int nthreads_omp_mpi_ok_min_cpu
= 1;
125 const int nthreads_omp_mpi_ok_min_gpu
= 2;
126 const int nthreads_omp_mpi_target_max
= 6;
129 /* Returns the maximum OpenMP thread count for which using a single MPI rank
130 * should be faster than using multiple ranks with the same total thread count.
132 static int nthreads_omp_faster(const gmx::CpuInfo
&cpuInfo
, gmx_bool bUseGPU
)
136 if (cpuInfo
.vendor() == gmx::CpuInfo::Vendor::Intel
&&
137 cpuInfo
.feature(gmx::CpuInfo::Feature::X86_Avx
))
139 nth
= nthreads_omp_faster_Intel_AVX
;
141 else if (gmx::cpuIsX86Nehalem(cpuInfo
))
144 nth
= nthreads_omp_faster_Nehalem
;
146 else if (cpuInfo
.vendor() == gmx::CpuInfo::Vendor::Amd
&& cpuInfo
.family() >= 23)
149 nth
= nthreads_omp_faster_AMD_Ryzen
;
153 nth
= nthreads_omp_faster_default
;
158 nth
*= nthreads_omp_faster_gpu_fac
;
161 nth
= std::min(nth
, GMX_OPENMP_MAX_THREADS
);
166 /* Returns that maximum OpenMP thread count that passes the efficiency check */
167 static int nthreads_omp_efficient_max(int gmx_unused nrank
,
168 const gmx::CpuInfo
&cpuInfo
,
171 #if GMX_OPENMP && GMX_MPI
174 return nthreads_omp_mpi_ok_max
;
179 return nthreads_omp_faster(cpuInfo
, bUseGPU
);
183 /* Return the number of thread-MPI ranks to use.
184 * This is chosen such that we can always obey our own efficiency checks.
186 static int get_tmpi_omp_thread_division(const gmx_hw_info_t
*hwinfo
,
187 const gmx_hw_opt_t
&hw_opt
,
192 const gmx::CpuInfo
&cpuInfo
= *hwinfo
->cpuInfo
;
194 GMX_RELEASE_ASSERT(nthreads_tot
> 0, "There must be at least one thread per rank");
196 /* There are no separate PME nodes here, as we ensured in
197 * check_and_update_hw_opt that nthreads_tmpi>0 with PME nodes
198 * and a conditional ensures we would not have ended up here.
199 * Note that separate PME nodes might be switched on later.
205 /* When the user sets nthreads_omp, we can end up oversubscribing CPU cores
206 * if we simply start as many ranks as GPUs. To avoid this, we start as few
207 * tMPI ranks as necessary to avoid oversubscription and instead leave GPUs idle.
208 * If the user does not set the number of OpenMP threads, nthreads_omp==0 and
209 * this code has no effect.
211 GMX_RELEASE_ASSERT(hw_opt
.nthreads_omp
>= 0, "nthreads_omp is negative, but previous checks should have prevented this");
212 while (nrank
*hw_opt
.nthreads_omp
> hwinfo
->nthreads_hw_avail
&& nrank
> 1)
217 if (nthreads_tot
< nrank
)
219 /* #thread < #gpu is very unlikely, but if so: waste gpu(s) */
220 nrank
= nthreads_tot
;
222 else if (nthreads_tot
> nthreads_omp_faster(cpuInfo
, ngpu
> 0) ||
223 (ngpu
> 1 && nthreads_tot
/ngpu
> nthreads_omp_mpi_target_max
))
225 /* The high OpenMP thread count will likely result in sub-optimal
226 * performance. Increase the rank count to reduce the thread count
227 * per rank. This will lead to GPU sharing by MPI ranks/threads.
231 /* Increase the rank count as long as have we more than 6 OpenMP
232 * threads per rank or the number of hardware threads is not
233 * divisible by the rank count. Don't go below 2 OpenMP threads.
241 while (nthreads_tot
/nrank
> nthreads_omp_mpi_target_max
||
242 (nthreads_tot
/(ngpu
*(nshare
+ 1)) >= nthreads_omp_mpi_ok_min_gpu
&& nthreads_tot
% nrank
!= 0));
245 else if (hw_opt
.nthreads_omp
> 0)
247 /* Here we could oversubscribe, when we do, we issue a warning later */
248 nrank
= std::max(1, nthreads_tot
/hw_opt
.nthreads_omp
);
252 if (nthreads_tot
<= nthreads_omp_faster(cpuInfo
, ngpu
> 0))
254 /* Use pure OpenMP parallelization */
259 /* Don't use OpenMP parallelization */
260 nrank
= nthreads_tot
;
268 static int getMaxGpuUsable(const gmx_hw_info_t
*hwinfo
,
271 /* This code relies on the fact that GPU are not detected when GPU
272 * acceleration was disabled at run time by the user, either with
273 * -nb cpu or setting GMX_EMULATE_GPU. */
274 if (cutoff_scheme
== ecutsVERLET
&&
275 hwinfo
->gpu_info
.n_dev_compatible
> 0)
277 return hwinfo
->gpu_info
.n_dev_compatible
;
289 gmxSmtIsEnabled(const gmx::HardwareTopology
&hwTop
)
291 return (hwTop
.supportLevel() >= gmx::HardwareTopology::SupportLevel::Basic
&& hwTop
.machine().sockets
[0].cores
[0].hwThreads
.size() > 1);
297 class SingleRankChecker
301 SingleRankChecker() : value_(false), reasons_() {}
302 /*! \brief Call this function for each possible condition
303 under which a single rank is required, along with a string
304 describing the constraint when it is applied. */
305 void applyConstraint(bool condition
, const char *description
)
310 reasons_
.push_back(gmx::formatString("%s only supports a single rank.", description
));
313 //! After applying any conditions, is a single rank required?
314 bool mustUseOneRank() const
318 /*! \brief Return a formatted string to use when writing a
319 message when a single rank is required, (or empty if no
320 constraint exists.) */
321 std::string
getMessage() const
323 return formatAndJoin(reasons_
, "\n", gmx::IdentityFormatter());
327 std::vector
<std::string
> reasons_
;
332 /* Get the number of MPI ranks to use for thread-MPI based on how many
333 * were requested, which algorithms we're using,
334 * and how many particles there are.
335 * At the point we have already called check_and_update_hw_opt.
336 * Thus all options should be internally consistent and consistent
337 * with the hardware, except that ntmpi could be larger than #GPU.
339 int get_nthreads_mpi(const gmx_hw_info_t
*hwinfo
,
340 gmx_hw_opt_t
*hw_opt
,
341 const t_inputrec
*inputrec
,
342 const gmx_mtop_t
*mtop
,
343 const gmx::MDLogger
&mdlog
,
346 int nthreads_hw
, nthreads_tot_max
, nrank
, ngpu
;
347 int min_atoms_per_mpi_rank
;
349 const gmx::CpuInfo
&cpuInfo
= *hwinfo
->cpuInfo
;
350 const gmx::HardwareTopology
&hwTop
= *hwinfo
->hardwareTopology
;
352 /* If the user made a GPU task assignment, that sets the number of thread-MPI ranks. */
353 auto userGpuTaskAssignment
= gmx::parseGpuTaskAssignment(hw_opt
->gpuIdTaskAssignment
);
354 int numGpuIdsSupplied
= static_cast<int>(userGpuTaskAssignment
.size());
356 /* TODO Here we handle the case where the user set GPU IDs, and
357 further below we handle the case where the algorithm does not
358 support multiple ranks. We need also to handle the case where
359 the user set multiple GPU IDs for an algorithm that cannot
360 handle multiple ranks. */
361 if (hw_opt
->nthreads_tmpi
< 1 && numGpuIdsSupplied
> 0)
363 /* If the user chose both mdrun -nt -gpu_id, is that consistent? */
364 if (hw_opt
->nthreads_tot
> 0 &&
365 (hw_opt
->nthreads_tot
% numGpuIdsSupplied
) != 0)
367 gmx_fatal(FARGS
, "Cannot run %d total threads with %d GPU ranks. Choose the total number of threads to be a multiple of the number of GPU ranks.", hw_opt
->nthreads_tot
, numGpuIdsSupplied
);
370 return numGpuIdsSupplied
;
374 /* Check if an algorithm does not support parallel simulation. */
375 // TODO This might work better if e.g. implemented algorithms
376 // had to define a function that returns such requirements,
377 // and a description string.
378 SingleRankChecker checker
;
379 checker
.applyConstraint(inputrec
->eI
== eiLBFGS
, "L-BFGS minimization");
380 checker
.applyConstraint(inputrec
->coulombtype
== eelEWALD
, "Plain Ewald electrostatics");
381 checker
.applyConstraint(doMembed
, "Membrane embedding");
382 if (checker
.mustUseOneRank())
384 std::string message
= checker
.getMessage();
385 if (hw_opt
->nthreads_tmpi
> 1)
387 gmx_fatal(FARGS
, "%s However, you asked for more than 1 thread-MPI rank, so mdrun cannot continue. Choose a single rank, or a different algorithm.", message
.c_str());
389 GMX_LOG(mdlog
.warning
).asParagraph().appendTextFormatted("%s Choosing to use only a single thread-MPI rank.", message
.c_str());
391 if (numGpuIdsSupplied
> 1)
393 gmx_fatal(FARGS
, "You supplied %d GPU IDs but only 1 rank can be used "
394 "by this simulation. Supply only one GPU ID.", numGpuIdsSupplied
);
400 if (hw_opt
->nthreads_tmpi
> 0)
402 if ((numGpuIdsSupplied
> 0) &&
403 (numGpuIdsSupplied
!= hw_opt
->nthreads_tmpi
))
405 gmx_fatal(FARGS
, "Cannot run %d thread-MPI ranks with %d GPU IDs supplied. "
406 "The number of ranks and the number of GPU IDs must match.",
407 hw_opt
->nthreads_tmpi
, numGpuIdsSupplied
);
410 /* Trivial, return the user's choice right away */
411 return hw_opt
->nthreads_tmpi
;
413 GMX_RELEASE_ASSERT(numGpuIdsSupplied
== 0,
414 "If mdrun -gpu_id had information, the number of ranks should have already been chosen");
416 // Now implement automatic selection of number of thread-MPI ranks
417 nthreads_hw
= hwinfo
->nthreads_hw_avail
;
419 if (nthreads_hw
<= 0)
421 /* This should normally not happen, but if it does, we handle it */
422 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");
425 /* How many total (#tMPI*#OpenMP) threads can we start? */
426 if (hw_opt
->nthreads_tot
> 0)
428 nthreads_tot_max
= hw_opt
->nthreads_tot
;
432 nthreads_tot_max
= nthreads_hw
;
435 ngpu
= getMaxGpuUsable(hwinfo
, inputrec
->cutoff_scheme
);
437 if (inputrec
->cutoff_scheme
== ecutsGROUP
)
439 /* We checked this before, but it doesn't hurt to do it once more */
440 GMX_RELEASE_ASSERT(hw_opt
->nthreads_omp
== 1, "The group scheme only supports one OpenMP thread per rank");
444 get_tmpi_omp_thread_division(hwinfo
, *hw_opt
, nthreads_tot_max
, ngpu
);
446 if (inputrec
->eI
== eiNM
|| EI_TPI(inputrec
->eI
))
448 /* Dims/steps are divided over the nodes iso splitting the atoms.
449 * With NM we can't have more ranks than #atoms*#dim. With TPI it's
450 * unlikely we have fewer atoms than ranks, and if so, communication
451 * would become a bottleneck, so we set the limit to 1 atom/rank.
453 min_atoms_per_mpi_rank
= 1;
459 min_atoms_per_mpi_rank
= min_atoms_per_gpu
;
463 min_atoms_per_mpi_rank
= min_atoms_per_mpi_thread
;
467 if (mtop
->natoms
/nrank
< min_atoms_per_mpi_rank
)
471 /* the rank number was chosen automatically, but there are too few
472 atoms per rank, so we need to reduce the rank count */
473 nrank_new
= std::max(1, mtop
->natoms
/min_atoms_per_mpi_rank
);
475 /* Avoid partial use of Hyper-Threading */
476 if (gmxSmtIsEnabled(hwTop
) &&
477 nrank_new
> nthreads_hw
/2 && nrank_new
< nthreads_hw
)
479 nrank_new
= nthreads_hw
/2;
482 /* If the user specified the total thread count, ensure this is
483 * divisible by the number of ranks.
484 * It is quite likely that we have too many total threads compared
485 * to the size of the system, but if the user asked for this many
486 * threads we should respect that.
488 while (hw_opt
->nthreads_tot
> 0 &&
489 hw_opt
->nthreads_tot
% nrank_new
!= 0)
494 /* Avoid large prime numbers in the rank count */
497 /* Use only 6,8,10 with additional factors of 2 */
501 while (3*fac
*2 <= nrank_new
)
506 nrank_new
= (nrank_new
/fac
)*fac
;
510 /* Avoid 5, since small system won't fit 5 domains along
511 * a dimension. This might lead to waisting some cores, but this
512 * will have a small impact in this regime of very small systems.
522 /* We reduced the number of tMPI ranks, which means we might violate
523 * our own efficiency checks if we simply use all hardware threads.
525 if (bHasOmpSupport
&& hw_opt
->nthreads_omp
<= 0 && hw_opt
->nthreads_tot
<= 0)
527 /* The user set neither the total nor the OpenMP thread count,
528 * we should use all hardware threads, unless we will violate
529 * our own efficiency limitation on the thread count.
533 nt_omp_max
= nthreads_omp_efficient_max(nrank
, cpuInfo
, ngpu
>= 1);
535 if (nrank
*nt_omp_max
< hwinfo
->nthreads_hw_avail
)
537 /* Limit the number of OpenMP threads to start */
538 hw_opt
->nthreads_omp
= nt_omp_max
;
542 fprintf(stderr
, "\n");
543 fprintf(stderr
, "NOTE: Parallelization is limited by the small number of atoms,\n");
544 fprintf(stderr
, " only starting %d thread-MPI ranks.\n", nrank
);
545 fprintf(stderr
, " You can use the -nt and/or -ntmpi option to optimize the number of threads.\n\n");
550 #endif /* GMX_THREAD_MPI */
553 void check_resource_division_efficiency(const gmx_hw_info_t
*hwinfo
,
555 bool willUsePhysicalGpu
,
556 gmx_bool bNtOmpOptionSet
,
558 const gmx::MDLogger
&mdlog
)
560 #if GMX_OPENMP && GMX_MPI
561 int nth_omp_min
, nth_omp_max
;
564 const char *mpi_option
= " (option -ntmpi)";
566 const char *mpi_option
= "";
569 /* This function should be called after thread-MPI (when configured) and
570 * OpenMP have been initialized. Check that here.
573 GMX_RELEASE_ASSERT(nthreads_omp_faster_default
>= nthreads_omp_mpi_ok_max
, "Inconsistent OpenMP thread count default values");
575 GMX_RELEASE_ASSERT(gmx_omp_nthreads_get(emntDefault
) >= 1, "Must have at least one OpenMP thread");
577 nth_omp_min
= gmx_omp_nthreads_get(emntDefault
);
578 nth_omp_max
= gmx_omp_nthreads_get(emntDefault
);
580 bool anyRankIsUsingGpus
= willUsePhysicalGpu
;
581 /* Thread-MPI seems to have a bug with reduce on 1 node, so use a cond. */
582 if (cr
->nnodes
+ cr
->npmenodes
> 1)
584 int count
[3], count_max
[3];
586 count
[0] = -nth_omp_min
;
587 count
[1] = nth_omp_max
;
588 count
[2] = willUsePhysicalGpu
;
590 MPI_Allreduce(count
, count_max
, 3, MPI_INT
, MPI_MAX
, cr
->mpi_comm_mysim
);
592 /* In case of an inhomogeneous run setup we use the maximum counts */
593 nth_omp_min
= -count_max
[0];
594 nth_omp_max
= count_max
[1];
595 anyRankIsUsingGpus
= count_max
[2] > 0;
598 int nthreads_omp_mpi_ok_min
;
600 if (!anyRankIsUsingGpus
)
602 nthreads_omp_mpi_ok_min
= nthreads_omp_mpi_ok_min_cpu
;
606 /* With GPUs we set the minimum number of OpenMP threads to 2 to catch
607 * cases where the user specifies #ranks == #cores.
609 nthreads_omp_mpi_ok_min
= nthreads_omp_mpi_ok_min_gpu
;
612 if (DOMAINDECOMP(cr
) && cr
->nnodes
> 1)
614 if (nth_omp_max
< nthreads_omp_mpi_ok_min
||
615 nth_omp_max
> nthreads_omp_mpi_ok_max
)
617 /* Note that we print target_max here, not ok_max */
618 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.",
620 nthreads_omp_mpi_ok_min
,
621 nthreads_omp_mpi_target_max
);
625 GMX_LOG(mdlog
.warning
).asParagraph().appendTextFormatted("NOTE: %s", buf
);
629 /* This fatal error, and the one below, is nasty, but it's
630 * probably the only way to ensure that all users don't waste
631 * a lot of resources, since many users don't read logs/stderr.
633 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
);
639 const gmx::CpuInfo
&cpuInfo
= *hwinfo
->cpuInfo
;
641 /* No domain decomposition (or only one domain) */
642 if (nth_omp_max
> nthreads_omp_faster(cpuInfo
, anyRankIsUsingGpus
))
644 /* To arrive here, the user/system set #ranks and/or #OMPthreads */
648 bEnvSet
= (getenv("OMP_NUM_THREADS") != nullptr);
650 if (bNtOmpOptionSet
|| bEnvSet
)
652 sprintf(buf2
, "You requested %d OpenMP threads", nth_omp_max
);
656 sprintf(buf2
, "Your choice of %d MPI rank%s and the use of %d total threads %sleads to the use of %d OpenMP threads",
657 cr
->nnodes
+ cr
->npmenodes
,
658 cr
->nnodes
+ cr
->npmenodes
== 1 ? "" : "s",
659 numTotalThreads
> 0 ? numTotalThreads
: hwinfo
->nthreads_hw_avail
,
660 hwinfo
->nphysicalnode
> 1 ? "on a node " : "",
663 sprintf(buf
, "%s, whereas we expect the optimum to be with more MPI ranks with %d to %d OpenMP threads.",
664 buf2
, nthreads_omp_mpi_ok_min
, nthreads_omp_mpi_target_max
);
666 /* We can not quit with a fatal error when OMP_NUM_THREADS is set
667 * with different values per rank or node, since in that case
668 * the user can not set -ntomp to override the error.
670 if (bNtOmpOptionSet
|| (bEnvSet
&& nth_omp_min
!= nth_omp_max
))
672 GMX_LOG(mdlog
.warning
).asParagraph().appendTextFormatted("NOTE: %s", buf
);
676 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
);
680 #else /* GMX_OPENMP && GMX_MPI */
681 /* No OpenMP and/or MPI: it doesn't make much sense to check */
682 GMX_UNUSED_VALUE(bNtOmpOptionSet
);
683 GMX_UNUSED_VALUE(numTotalThreads
);
684 GMX_UNUSED_VALUE(willUsePhysicalGpu
);
685 GMX_UNUSED_VALUE(cr
);
686 /* Check if we have more than 1 physical core, if detected,
687 * or more than 1 hardware thread if physical cores were not detected.
689 if (!GMX_OPENMP
&& !GMX_MPI
&& hwinfo
->hardwareTopology
->numberOfCores() > 1)
691 GMX_LOG(mdlog
.warning
).asParagraph().appendText("NOTE: GROMACS was compiled without OpenMP and (thread-)MPI support, can only use a single CPU core");
693 #endif /* GMX_OPENMP && GMX_MPI */
697 static void print_hw_opt(FILE *fp
, const gmx_hw_opt_t
*hw_opt
)
699 fprintf(fp
, "hw_opt: nt %d ntmpi %d ntomp %d ntomp_pme %d gpu_id '%s'\n",
700 hw_opt
->nthreads_tot
,
701 hw_opt
->nthreads_tmpi
,
702 hw_opt
->nthreads_omp
,
703 hw_opt
->nthreads_omp_pme
,
704 hw_opt
->gpuIdTaskAssignment
.c_str());
707 /* Checks we can do when we don't (yet) know the cut-off scheme */
708 void check_and_update_hw_opt_1(gmx_hw_opt_t
*hw_opt
,
712 /* Currently hw_opt only contains default settings or settings supplied
713 * by the user on the command line.
715 if (hw_opt
->nthreads_omp
< 0)
717 gmx_fatal(FARGS
, "The number of OpenMP threads supplied on the command line is %d, which is negative and not allowed", hw_opt
->nthreads_omp
);
720 /* Check for OpenMP settings stored in environment variables, which can
721 * potentially be different on different MPI ranks.
723 gmx_omp_nthreads_read_env(&hw_opt
->nthreads_omp
, SIMMASTER(cr
));
725 /* Check restrictions on the user supplied options before modifying them.
726 * TODO: Put the user values in a const struct and preserve them.
729 if (hw_opt
->nthreads_tot
> 0)
731 gmx_fatal(FARGS
, "Setting the total number of threads is only supported with thread-MPI and GROMACS was compiled without thread-MPI");
733 if (hw_opt
->nthreads_tmpi
> 0)
735 gmx_fatal(FARGS
, "Setting the number of thread-MPI ranks is only supported with thread-MPI and GROMACS was compiled without thread-MPI");
741 /* Check restrictions on PME thread related options set by the user */
743 if (hw_opt
->nthreads_omp_pme
> 0 && hw_opt
->nthreads_omp
<= 0)
745 gmx_fatal(FARGS
, "You need to specify -ntomp in addition to -ntomp_pme");
748 if (hw_opt
->nthreads_omp_pme
>= 1 &&
749 hw_opt
->nthreads_omp_pme
!= hw_opt
->nthreads_omp
&&
752 /* This can result in a fatal error on many MPI ranks,
753 * but since the thread count can differ per rank,
754 * we can't easily avoid this.
756 gmx_fatal(FARGS
, "You need to explicitly specify the number of PME ranks (-npme) when using different number of OpenMP threads for PP and PME ranks");
761 /* GROMACS was configured without OpenMP support */
763 if (hw_opt
->nthreads_omp
> 1 || hw_opt
->nthreads_omp_pme
> 1)
765 gmx_fatal(FARGS
, "More than 1 OpenMP thread requested, but GROMACS was compiled without OpenMP support");
767 hw_opt
->nthreads_omp
= 1;
768 hw_opt
->nthreads_omp_pme
= 1;
771 if (hw_opt
->nthreads_tot
> 0 && hw_opt
->nthreads_omp_pme
<= 0)
773 /* We have the same number of OpenMP threads for PP and PME ranks,
774 * thus we can perform several consistency checks.
776 if (hw_opt
->nthreads_tmpi
> 0 &&
777 hw_opt
->nthreads_omp
> 0 &&
778 hw_opt
->nthreads_tot
!= hw_opt
->nthreads_tmpi
*hw_opt
->nthreads_omp
)
780 gmx_fatal(FARGS
, "The total number of threads requested (%d) does not match the thread-MPI ranks (%d) times the OpenMP threads (%d) requested",
781 hw_opt
->nthreads_tot
, hw_opt
->nthreads_tmpi
, hw_opt
->nthreads_omp
);
784 if (hw_opt
->nthreads_tmpi
> 0 &&
785 hw_opt
->nthreads_tot
% hw_opt
->nthreads_tmpi
!= 0)
787 gmx_fatal(FARGS
, "The total number of threads requested (%d) is not divisible by the number of thread-MPI ranks requested (%d)",
788 hw_opt
->nthreads_tot
, hw_opt
->nthreads_tmpi
);
791 if (hw_opt
->nthreads_omp
> 0 &&
792 hw_opt
->nthreads_tot
% hw_opt
->nthreads_omp
!= 0)
794 gmx_fatal(FARGS
, "The total number of threads requested (%d) is not divisible by the number of OpenMP threads requested (%d)",
795 hw_opt
->nthreads_tot
, hw_opt
->nthreads_omp
);
799 if (hw_opt
->nthreads_tot
> 0)
801 if (hw_opt
->nthreads_omp
> hw_opt
->nthreads_tot
)
803 gmx_fatal(FARGS
, "You requested %d OpenMP threads with %d total threads. Choose a total number of threads that is a multiple of the number of OpenMP threads.",
804 hw_opt
->nthreads_omp
, hw_opt
->nthreads_tot
);
807 if (hw_opt
->nthreads_tmpi
> hw_opt
->nthreads_tot
)
809 gmx_fatal(FARGS
, "You requested %d thread-MPI ranks with %d total threads. Choose a total number of threads that is a multiple of the number of thread-MPI ranks.",
810 hw_opt
->nthreads_tmpi
, hw_opt
->nthreads_tot
);
816 print_hw_opt(debug
, hw_opt
);
819 /* Asserting this simplifies the hardware resource division later
821 GMX_RELEASE_ASSERT(!(hw_opt
->nthreads_omp_pme
>= 1 && hw_opt
->nthreads_omp
<= 0),
822 "PME thread count should only be set when the normal thread count is also set");
825 /* Checks we can do when we know the cut-off scheme */
826 void check_and_update_hw_opt_2(gmx_hw_opt_t
*hw_opt
,
829 if (cutoff_scheme
== ecutsGROUP
)
831 /* We only have OpenMP support for PME only nodes */
832 if (hw_opt
->nthreads_omp
> 1)
834 gmx_fatal(FARGS
, "OpenMP threads have been requested with cut-off scheme %s, but these are only supported with cut-off scheme %s",
835 ecutscheme_names
[cutoff_scheme
],
836 ecutscheme_names
[ecutsVERLET
]);
838 hw_opt
->nthreads_omp
= 1;
842 /* Checks we can do when we know the thread-MPI rank count */
843 void check_and_update_hw_opt_3(gmx_hw_opt_t
*hw_opt
)
846 GMX_RELEASE_ASSERT(hw_opt
->nthreads_tmpi
>= 1, "Must have at least one thread-MPI rank");
848 /* If the user set the total number of threads on the command line
849 * and did not specify the number of OpenMP threads, set the latter here.
851 if (hw_opt
->nthreads_tot
> 0 && hw_opt
->nthreads_omp
<= 0)
853 hw_opt
->nthreads_omp
= hw_opt
->nthreads_tot
/hw_opt
->nthreads_tmpi
;
855 if (!bHasOmpSupport
&& hw_opt
->nthreads_omp
> 1)
857 gmx_fatal(FARGS
, "You (indirectly) asked for OpenMP threads by setting -nt > -ntmpi, but GROMACS was compiled without OpenMP support");
862 GMX_RELEASE_ASSERT(bHasOmpSupport
|| hw_opt
->nthreads_omp
== 1, "Without OpenMP support, only one thread per rank can be used");
864 /* We are done with updating nthreads_omp, we can set nthreads_omp_pme */
865 if (hw_opt
->nthreads_omp_pme
<= 0 && hw_opt
->nthreads_omp
> 0)
867 hw_opt
->nthreads_omp_pme
= hw_opt
->nthreads_omp
;
872 print_hw_opt(debug
, hw_opt
);