* Copyright (c) 2012,2013,2014,2015, by the GROMACS development team, led by
* Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
* and including many others, as listed in the AUTHORS file in the
- * top-level source directory and at h=ttp://www.gromacs.org.
+ * top-level source directory and at http://www.gromacs.org.
*
* GROMACS is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* To help us fund GROMACS development, we humbly ask that you cite
* the research papers on the package. Check out http://www.gromacs.org.
*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
+#include "gmxpre.h"
+
+#include "gromacs/legacyheaders/gmx_detect_hardware.h"
+
+#include "config.h"
-#include <stdlib.h>
#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
#include <string.h>
+#include <string>
+#include <vector>
+
#ifdef HAVE_UNISTD_H
/* For sysconf */
#include <unistd.h>
#endif
-
-#include "types/enums.h"
-#include "types/hw_info.h"
-#include "types/commrec.h"
-#include "gmx_fatal.h"
-#include "gmx_fatal_collective.h"
-#include "md_logging.h"
-#include "gmx_cpuid.h"
-#include "gromacs/utility/smalloc.h"
-#include "gpu_utils.h"
-#include "copyrite.h"
-#include "gmx_detect_hardware.h"
-#include "main.h"
-#include "md_logging.h"
-#include "gromacs/utility/gmxomp.h"
-#include "gromacs/utility/cstringutil.h"
-
-#include "thread_mpi/threads.h"
-
#ifdef GMX_NATIVE_WINDOWS
#include <windows.h>
#endif
+#include "thread_mpi/threads.h"
+
+#include "gromacs/gmxlib/gpu_utils/gpu_utils.h"
+#include "gromacs/legacyheaders/copyrite.h"
+#include "gromacs/legacyheaders/gmx_cpuid.h"
+#include "gromacs/legacyheaders/md_logging.h"
+#include "gromacs/legacyheaders/network.h"
+#include "gromacs/legacyheaders/types/commrec.h"
+#include "gromacs/legacyheaders/types/enums.h"
+#include "gromacs/legacyheaders/types/hw_info.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basenetwork.h"
+#include "gromacs/utility/cstringutil.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/gmxomp.h"
+#include "gromacs/utility/smalloc.h"
+#include "gromacs/utility/stringutil.h"
+#include "gromacs/utility/sysinfo.h"
+
+
#ifdef GMX_GPU
const gmx_bool bGPUBinary = TRUE;
#else
}
}
-static void print_gpu_use_stats(FILE *fplog,
- const gmx_gpu_info_t *gpu_info,
- const gmx_gpu_opt_t *gpu_opt,
- const t_commrec *cr)
+/*! \brief Helper function for reporting GPU usage information
+ * in the mdrun log file
+ *
+ * \param[in] gpu_info Pointer to per-node GPU info struct
+ * \param[in] gpu_opt Pointer to per-node GPU options struct
+ * \param[in] numPpRanks Number of PP ranks per node
+ * \return String to write to the log file
+ * \throws std::bad_alloc if out of memory */
+static std::string
+makeGpuUsageReport(const gmx_gpu_info_t *gpu_info,
+ const gmx_gpu_opt_t *gpu_opt,
+ size_t numPpRanks)
{
- char sbuf[STRLEN], stmp[STRLEN];
- int i, ngpu_comp, ngpu_use;
-
- ngpu_comp = gpu_info->ncuda_dev_compatible;
- ngpu_use = gpu_opt->ncuda_dev_use;
+ int ngpu_use = gpu_opt->ncuda_dev_use;
+ int ngpu_comp = gpu_info->ncuda_dev_compatible;
/* Issue a note if GPUs are available but not used */
if (ngpu_comp > 0 && ngpu_use < 1)
{
- sprintf(sbuf,
- "%d compatible GPU%s detected in the system, but none will be used.\n"
- "Consider trying GPU acceleration with the Verlet scheme!",
- ngpu_comp, (ngpu_comp > 1) ? "s" : "");
+ return gmx::formatString("%d compatible GPU%s detected in the system, but none will be used.\n"
+ "Consider trying GPU acceleration with the Verlet scheme!\n",
+ ngpu_comp, (ngpu_comp > 1) ? "s" : "");
}
- else
- {
- int ngpu_use_uniq;
-
- ngpu_use_uniq = gmx_count_gpu_dev_unique(gpu_info, gpu_opt);
- sprintf(sbuf, "%d GPU%s %sselected for this run.\n"
- "Mapping of GPU%s to the %d PP rank%s in this node: ",
- ngpu_use_uniq, (ngpu_use_uniq > 1) ? "s" : "",
- gpu_opt->bUserSet ? "user-" : "auto-",
- (ngpu_use > 1) ? "s" : "",
- cr->nrank_pp_intranode,
- (cr->nrank_pp_intranode > 1) ? "s" : "");
+ std::string output;
+ if (!gpu_opt->bUserSet)
+ {
+ // gpu_opt->cuda_dev_compatible is only populated during auto-selection
+ std::string gpuIdsString =
+ formatAndJoin(gmx::constArrayRefFromArray(gpu_opt->cuda_dev_compatible,
+ gpu_opt->ncuda_dev_compatible),
+ ",", gmx::StringFormatter("%d"));
+ bool bPluralGpus = gpu_opt->ncuda_dev_compatible > 1;
+ output += gmx::formatString("%d compatible GPU%s %s present, with ID%s %s\n",
+ gpu_opt->ncuda_dev_compatible,
+ bPluralGpus ? "s" : "",
+ bPluralGpus ? "are" : "is",
+ bPluralGpus ? "s" : "",
+ gpuIdsString.c_str());
+ }
- for (i = 0; i < ngpu_use; i++)
+ {
+ std::vector<int> gpuIdsInUse;
+ for (int i = 0; i < ngpu_use; i++)
{
- sprintf(stmp, "#%d", get_gpu_device_id(gpu_info, gpu_opt, i));
- if (i < ngpu_use - 1)
- {
- strcat(stmp, ", ");
- }
- strcat(sbuf, stmp);
+ gpuIdsInUse.push_back(get_gpu_device_id(gpu_info, gpu_opt, i));
}
- }
- md_print_info(cr, fplog, "%s\n\n", sbuf);
+ std::string gpuIdsString =
+ formatAndJoin(gpuIdsInUse, ",", gmx::StringFormatter("%d"));
+ int numGpusInUse = gmx_count_gpu_dev_unique(gpu_info, gpu_opt);
+ bool bPluralGpus = numGpusInUse > 1;
+
+ output += gmx::formatString("%d GPU%s %sselected for this run.\n"
+ "Mapping of GPU ID%s to the %d PP rank%s in this node: %s\n",
+ numGpusInUse, bPluralGpus ? "s" : "",
+ gpu_opt->bUserSet ? "user-" : "auto-",
+ bPluralGpus ? "s" : "",
+ numPpRanks,
+ (numPpRanks > 1) ? "s" : "",
+ gpuIdsString.c_str());
+ }
+
+ return output;
}
/* Give a suitable fatal error or warning if the build configuration
const gmx_hw_opt_t *hw_opt,
gmx_bool bUseGPU)
{
- int npppn, ntmpi_pp;
- char sbuf[STRLEN], th_or_proc[STRLEN], th_or_proc_plural[STRLEN], pernode[STRLEN];
+ int npppn;
+ char th_or_proc[STRLEN], th_or_proc_plural[STRLEN], pernode[STRLEN];
gmx_bool btMPI, bMPI, bMaxMpiThreadsSet, bNthreadsAuto, bEmulateGPU;
assert(hwinfo);
return;
}
- btMPI = bMPI = FALSE;
- bNthreadsAuto = FALSE;
#if defined(GMX_THREAD_MPI)
+ bMPI = FALSE;
btMPI = TRUE;
bNthreadsAuto = (hw_opt->nthreads_tmpi < 1);
#elif defined(GMX_LIB_MPI)
- bMPI = TRUE;
+ bMPI = TRUE;
+ btMPI = FALSE;
+ bNthreadsAuto = FALSE;
+#else
+ bMPI = FALSE;
+ btMPI = FALSE;
+ bNthreadsAuto = FALSE;
#endif
/* GPU emulation detection is done later, but we need here as well
if (hwinfo->gpu_info.ncuda_dev_compatible > 0)
{
+ std::string gpuUseageReport;
+ try
+ {
+ gpuUseageReport = makeGpuUsageReport(&hwinfo->gpu_info,
+ &hw_opt->gpu_opt,
+ cr->nrank_pp_intranode);
+ }
+ GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
+
/* NOTE: this print is only for and on one physical node */
- print_gpu_use_stats(fplog, &hwinfo->gpu_info, &hw_opt->gpu_opt, cr);
+ md_print_info(cr, fplog, gpuUseageReport.c_str());
}
/* Need to ensure that we have enough GPUs:
gmx_hw_info_t *gmx_detect_hardware(FILE *fplog, const t_commrec *cr,
gmx_bool bDetectGPUs)
{
- gmx_hw_info_t *hw;
int ret;
/* make sure no one else is doing the same thing */
gmx_gpu_opt_t *gpu_opt)
{
int i;
- const char *env;
char sbuf[STRLEN], stmp[STRLEN];
/* Bail if binary is not compiled with GPU acceleration, but this is either
else
{
pick_compatible_gpus(&hwinfo_g->gpu_info, gpu_opt);
-
- if (gpu_opt->ncuda_dev_use > cr->nrank_pp_intranode)
- {
- /* We picked more GPUs than we can use: limit the number.
- * We print detailed messages about this later in
- * gmx_check_hw_runconf_consistency.
- */
- limit_num_gpus_used(gpu_opt, cr->nrank_pp_intranode);
- }
-
- gpu_opt->bUserSet = FALSE;
+ limit_num_gpus_used(gpu_opt, cr->nrank_pp_intranode);
}
/* If the user asked for a GPU, check whether we have a GPU */
}
}
-static void limit_num_gpus_used(gmx_gpu_opt_t *gpu_opt, int count)
+/* If we detected more compatible GPUs than we can use, limit the
+ * number. We print detailed messages about this later in
+ * gmx_check_hw_runconf_consistency.
+ */
+static void limit_num_gpus_used(gmx_gpu_opt_t *gpu_opt, int maxNumberToUse)
{
- int ndev_use;
-
- assert(gpu_opt);
+ GMX_RELEASE_ASSERT(gpu_opt, "Invalid gpu_opt pointer passed");
+ GMX_RELEASE_ASSERT(maxNumberToUse >= 1,
+ gmx::formatString("Invalid limit (%d) for the number of GPUs (detected %d compatible GPUs)",
+ maxNumberToUse, gpu_opt->ncuda_dev_compatible).c_str());
- ndev_use = gpu_opt->ncuda_dev_use;
-
- if (count > ndev_use)
- {
- /* won't increase the # of GPUs */
- return;
- }
-
- if (count < 1)
+ /* Don't increase the number of GPUs used beyond (e.g.) the number
+ of PP ranks */
+ gpu_opt->ncuda_dev_use = std::min(gpu_opt->ncuda_dev_compatible, maxNumberToUse);
+ snew(gpu_opt->cuda_dev_use, gpu_opt->ncuda_dev_use);
+ for (int i = 0; i != gpu_opt->ncuda_dev_use; ++i)
{
- char sbuf[STRLEN];
- sprintf(sbuf, "Limiting the number of GPUs to <1 doesn't make sense (detected %d, %d requested)!",
- ndev_use, count);
- gmx_incons(sbuf);
+ /* TODO: improve this implementation: either sort GPUs or remove the weakest here */
+ gpu_opt->cuda_dev_use[i] = gpu_opt->cuda_dev_compatible[i];
}
-
- /* TODO: improve this implementation: either sort GPUs or remove the weakest here */
- gpu_opt->ncuda_dev_use = count;
}
void gmx_hardware_info_free(gmx_hw_info_t *hwinfo)