/* check whether an 'nst'-style parameter p is a multiple of nst, and set it to be one if not, with a warning. */ static void check_nst_param(const gmx::MDLogger &mdlog, const char *desc_nst, int nst, const char *desc_p, int *p) { if (*p > 0 && *p % nst != 0) { /* Round up to the next multiple of nst */ *p = ((*p)/nst + 1)*nst; GMX_LOG(mdlog.warning).asParagraph().appendTextFormatted( "NOTE: %s changes %s to %d", desc_nst, desc_p, *p); } }
void gmx_omp_nthreads_read_env(const gmx::MDLogger &mdlog, int *nthreads_omp) { char *env; gmx_bool bCommandLineSetNthreadsOMP = *nthreads_omp > 0; char buffer[STRLEN]; GMX_RELEASE_ASSERT(nthreads_omp, "nthreads_omp must be a non-NULL pointer"); if ((env = getenv("OMP_NUM_THREADS")) != nullptr) { int nt_omp; sscanf(env, "%d", &nt_omp); if (nt_omp <= 0) { gmx_fatal(FARGS, "OMP_NUM_THREADS is invalid: '%s'", env); } if (bCommandLineSetNthreadsOMP && nt_omp != *nthreads_omp) { gmx_fatal(FARGS, "Environment variable OMP_NUM_THREADS (%d) and the number of threads requested on the command line (%d) have different values. Either omit one, or set them both to the same value.", nt_omp, *nthreads_omp); } /* Setting the number of OpenMP threads. */ *nthreads_omp = nt_omp; /* Output the results */ sprintf(buffer, "\nThe number of OpenMP threads was set by environment variable OMP_NUM_THREADS to %d%s\n\n", nt_omp, bCommandLineSetNthreadsOMP ? " (and the command-line setting agreed with that)" : ""); /* This prints once per simulation for multi-simulations, * which might help diagnose issues with inhomogenous * cluster setups. */ GMX_LOG(mdlog.info).appendTextFormatted("%s", buffer); if (debug) { /* This prints once per process for real MPI (i.e. once * per debug file), and once per simulation for thread MPI * (because of logic in the calling function). */ fputs(buffer, debug); } } }
int check_nstglobalcomm(const gmx::MDLogger &mdlog, int nstglobalcomm, t_inputrec *ir) { if (!EI_DYNAMICS(ir->eI)) { nstglobalcomm = 1; } if (nstglobalcomm == -1) { // Set up the default behaviour if (!(ir->nstcalcenergy > 0 || ir->nstlist > 0 || ir->etc != etcNO || ir->epc != epcNO)) { /* The user didn't choose the period for anything important, so we just make sure we can send signals and write output suitably. */ nstglobalcomm = 10; if (ir->nstenergy > 0 && ir->nstenergy < nstglobalcomm) { nstglobalcomm = ir->nstenergy; } } else { /* The user has made a choice (perhaps implicitly), so we * ensure that we do timely intra-simulation communication * for (possibly) each of the four parts that care. * * TODO Does the Verlet scheme (+ DD) need any * communication at nstlist steps? Is the use of nstlist * here a leftover of the twin-range scheme? Can we remove * nstlist when we remove the group scheme? */ nstglobalcomm = lcd4(ir->nstcalcenergy, ir->nstlist, ir->etc != etcNO ? ir->nsttcouple : 0, ir->epc != epcNO ? ir->nstpcouple : 0); } } else { // Check that the user's choice of mdrun -gcom will work if (ir->nstlist > 0 && nstglobalcomm > ir->nstlist && nstglobalcomm % ir->nstlist != 0) { nstglobalcomm = (nstglobalcomm / ir->nstlist)*ir->nstlist; GMX_LOG(mdlog.warning).asParagraph().appendTextFormatted( "WARNING: nstglobalcomm is larger than nstlist, but not a multiple, setting it to %d", nstglobalcomm); } if (ir->nstcalcenergy > 0) { check_nst_param(mdlog, "-gcom", nstglobalcomm, "nstcalcenergy", &ir->nstcalcenergy); } if (ir->etc != etcNO && ir->nsttcouple > 0) { check_nst_param(mdlog, "-gcom", nstglobalcomm, "nsttcouple", &ir->nsttcouple); } if (ir->epc != epcNO && ir->nstpcouple > 0) { check_nst_param(mdlog, "-gcom", nstglobalcomm, "nstpcouple", &ir->nstpcouple); } check_nst_param(mdlog, "-gcom", nstglobalcomm, "nstenergy", &ir->nstenergy); check_nst_param(mdlog, "-gcom", nstglobalcomm, "nstlog", &ir->nstlog); } if (ir->comm_mode != ecmNO && ir->nstcomm < nstglobalcomm) { GMX_LOG(mdlog.warning).asParagraph().appendTextFormatted( "WARNING: Changing nstcomm from %d to %d", ir->nstcomm, nstglobalcomm); ir->nstcomm = nstglobalcomm; } GMX_LOG(mdlog.info).appendTextFormatted( "Intra-simulation communication will occur every %d steps.\n", nstglobalcomm); return nstglobalcomm; }
/*! \brief Report on the OpenMP settings that will be used */ static void reportOpenmpSettings(const gmx::MDLogger &mdlog, const t_commrec *cr, gmx_bool bOMP, gmx_bool bFullOmpSupport, gmx_bool bSepPME) { #if GMX_THREAD_MPI const char *mpi_str = "per tMPI thread"; #else const char *mpi_str = "per MPI process"; #endif int nth_min, nth_max, nth_pme_min, nth_pme_max; /* inform the user about the settings */ if (!bOMP) { return; } #if GMX_MPI if (cr->nnodes + cr->npmenodes > 1) { /* Get the min and max thread counts over the MPI ranks */ int buf_in[4], buf_out[4]; buf_in[0] = -modth.gnth; buf_in[1] = modth.gnth; buf_in[2] = -modth.gnth_pme; buf_in[3] = modth.gnth_pme; MPI_Allreduce(buf_in, buf_out, 4, MPI_INT, MPI_MAX, cr->mpi_comm_mysim); nth_min = -buf_out[0]; nth_max = buf_out[1]; nth_pme_min = -buf_out[2]; nth_pme_max = buf_out[3]; } else #endif { nth_min = modth.gnth; nth_max = modth.gnth; nth_pme_min = modth.gnth_pme; nth_pme_max = modth.gnth_pme; } /* for group scheme we print PME threads info only */ if (bFullOmpSupport) { if (nth_max == nth_min) { GMX_LOG(mdlog.warning).appendTextFormatted( "Using %d OpenMP thread%s %s", nth_min, nth_min > 1 ? "s" : "", cr->nnodes > 1 ? mpi_str : ""); } else { GMX_LOG(mdlog.warning).appendTextFormatted( "Using %d - %d OpenMP threads %s", nth_min, nth_max, mpi_str); } } if (bSepPME && (nth_pme_min != nth_min || nth_pme_max != nth_max)) { if (nth_pme_max == nth_pme_min) { GMX_LOG(mdlog.warning).appendTextFormatted( "Using %d OpenMP thread%s %s for PME", nth_pme_min, nth_pme_min > 1 ? "s" : "", cr->nnodes > 1 ? mpi_str : ""); } else { GMX_LOG(mdlog.warning).appendTextFormatted( "Using %d - %d OpenMP threads %s for PME", nth_pme_min, nth_pme_max, mpi_str); } } GMX_LOG(mdlog.warning); }
/** Determine the number of threads for module \p mod. * * \p m takes values form the module_nth_t enum and maps these to the * corresponding value in modth_env_var. * * Each number of threads per module takes the default value unless * GMX_*_NUM_THERADS env var is set, case in which its value overrides * the deafult. * * The "group" scheme supports OpenMP only in PME and in thise case all but * the PME nthread values default to 1. */ static void pick_module_nthreads(const gmx::MDLogger &mdlog, int m, gmx_bool bFullOmpSupport, gmx_bool bSepPME) { char *env; int nth; const bool bOMP = GMX_OPENMP; /* The default should never be set through a GMX_*_NUM_THREADS env var * as it's always equal with gnth. */ if (m == emntDefault) { return; } /* check the environment variable */ if ((env = getenv(modth_env_var[m])) != nullptr) { sscanf(env, "%d", &nth); if (!bOMP) { gmx_warning("%s=%d is set, but %s is compiled without OpenMP!", modth_env_var[m], nth, gmx::getProgramContext().displayName()); } /* with the verlet codepath, when any GMX_*_NUM_THREADS env var is set, * OMP_NUM_THREADS also has to be set */ if (bFullOmpSupport && getenv("OMP_NUM_THREADS") == nullptr) { gmx_warning("%s=%d is set, the default number of threads also " "needs to be set with OMP_NUM_THREADS!", modth_env_var[m], nth); } /* with the group scheme warn if any env var except PME is set */ if (!bFullOmpSupport) { if (m != emntPME) { gmx_warning("%s=%d is set, but OpenMP multithreading is not " "supported in %s!", modth_env_var[m], nth, mod_name[m]); nth = 1; } } /* only babble if we are really overriding with a different value */ if ((bSepPME && m == emntPME && nth != modth.gnth_pme) || (nth != modth.gnth)) { GMX_LOG(mdlog.warning).asParagraph().appendTextFormatted( "%s=%d set, overriding the default number of %s threads", modth_env_var[m], nth, mod_name[m]); } } else { /* pick the global PME node nthreads if we are setting the number * of threads in separate PME nodes */ nth = (bSepPME && m == emntPME) ? modth.gnth_pme : modth.gnth; } gmx_omp_nthreads_set(m, nth); }