Merge branch release-5-0
[alexxy/gromacs.git] / src / gromacs / gmxlib / gmx_detect_hardware.cpp
similarity index 82%
rename from src/gromacs/gmxlib/gmx_detect_hardware.c
rename to src/gromacs/gmxlib/gmx_detect_hardware.cpp
index 8846cb3242957338cb6444af00c4a3114c3c4c84..490bd87327ca2353d63805fbf186662fad0d2b10 100644 (file)
@@ -4,7 +4,7 @@
  * 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
@@ -145,50 +154,69 @@ static void print_gpu_detection_stats(FILE                 *fplog,
     }
 }
 
-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
@@ -231,8 +259,8 @@ void gmx_check_hw_runconf_consistency(FILE                *fplog,
                                       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);
@@ -247,13 +275,18 @@ void gmx_check_hw_runconf_consistency(FILE                *fplog,
         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
@@ -274,8 +307,17 @@ void gmx_check_hw_runconf_consistency(FILE                *fplog,
 
     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:
@@ -627,7 +669,6 @@ static void gmx_detect_gpus(FILE *fplog, const t_commrec *cr)
 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 */
@@ -725,7 +766,6 @@ void gmx_select_gpu_ids(FILE *fplog, const t_commrec *cr,
                         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
@@ -770,17 +810,7 @@ void gmx_select_gpu_ids(FILE *fplog, const t_commrec *cr,
     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 */
@@ -790,30 +820,26 @@ void gmx_select_gpu_ids(FILE *fplog, const t_commrec *cr,
     }
 }
 
-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)