Refactor to create a GpuTaskAssignments object
authorMark Abraham <mark.j.abraham@gmail.com>
Thu, 19 Sep 2019 14:51:21 +0000 (16:51 +0200)
committerMark Abraham <mark.j.abraham@gmail.com>
Fri, 20 Sep 2019 08:07:56 +0000 (10:07 +0200)
This change is purely reorganization, nothing is reordered or
functionality changed.

This expands on the previous vector of vectors of task assignments
into an object with the invariant of a completed assignment. It has
very complex construction logic, so it has a separate builder class
for the data that describes the decision about which GPUs are assigned
to tasks running on ranks on each physical node.

This refactoring prepares for re-ordering mdrun setup
so that the decision of rank duty by DD can lead to
task assignment, device initialization and the creation
of GPU contexts and streams with which to initialize
the various modules that use them to do their work.

Minor improvements to documentation and naming in a few places.

Change-Id: Idec71a3016212a4125b23b780e1059dbb134d7fb

16 files changed:
src/gromacs/domdec/domdec.cpp
src/gromacs/domdec/domdec.h
src/gromacs/gmxlib/network.cpp
src/gromacs/gmxlib/network.h
src/gromacs/hardware/gpu_hw_info.h
src/gromacs/hardware/hw_info.h
src/gromacs/mdrun/runner.cpp
src/gromacs/mdrunutility/multisim.cpp
src/gromacs/mdrunutility/multisim.h
src/gromacs/taskassignment/findallgputasks.cpp
src/gromacs/taskassignment/findallgputasks.h
src/gromacs/taskassignment/reportgpuusage.cpp
src/gromacs/taskassignment/reportgpuusage.h
src/gromacs/taskassignment/taskassignment.cpp
src/gromacs/taskassignment/taskassignment.h
src/gromacs/taskassignment/usergpuids.h

index 655f475582a2b55dfa0c1c9ef32480fb148654f5..8788804459bdcb7f182a08499d860f6853fea130 100644 (file)
@@ -1165,7 +1165,7 @@ static void make_load_communicator(gmx_domdec_t *dd, int dim_ind, ivec loc)
 }
 #endif
 
-void dd_setup_dlb_resource_sharing(t_commrec            *cr,
+void dd_setup_dlb_resource_sharing(const t_commrec      *cr,
                                    int                   gpu_id)
 {
 #if GMX_MPI
index 825a1918f3e63c3a54e4c4769aed2c197d581d16..d76fe2bd7e29f5f8601ab974b1816790d0e9c779 100644 (file)
@@ -213,7 +213,7 @@ gmx_bool change_dd_cutoff(t_commrec                     *cr,
  * GPU finish. Therefore there wait times need to be averaged over the ranks
  * sharing the same GPU. This function sets up the communication for that.
  */
-void dd_setup_dlb_resource_sharing(t_commrec           *cr,
+void dd_setup_dlb_resource_sharing(const t_commrec     *cr,
                                    int                  gpu_id);
 
 /*! \brief Cycle counter indices used internally in the domain decomposition */
index 729fe66b2169b20481f8226b9997df2b1260884b..f185288c7d18cf81cde61d0840d317bd70333dd3 100644 (file)
@@ -562,3 +562,13 @@ void gmx_fatal_collective(int f_errno, const char *file, int line,
     gmx_fatal_mpi_va(f_errno, file, line, bMaster, bFinalize, fmt, ap);
     va_end(ap);
 }
+
+void simulationBarrier(const t_commrec *cr)
+{
+    if (PAR(cr))
+    {
+#if GMX_MPI
+        MPI_Barrier(cr->mpi_comm_mysim);
+#endif
+    }
+}
index c23a754a5d446e51eb0376190b4367e9b355e042..086f2751b5508a39710b9582a99fc21089328a57 100644 (file)
@@ -120,4 +120,7 @@ gmx_fatal_collective(int f_errno, const char *file, int line,
  * for all processes.
  */
 
+//! Make a barrier across all ranks of this simulation
+void simulationBarrier(const t_commrec *cr);
+
 #endif
index 51e1d74c4e0b2f614e483f69c296c7ac950d6eeb..f7c2cfde5cb155068b33076d437e331b1e1e411a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013,2014,2015,2017,2018, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015,2017,2018,2019, 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 http://www.gromacs.org.
 
 #include "gromacs/utility/basedefinitions.h"
 
-#if 0
-} /* fixes auto-indentation problems */
-#endif
-
 struct gmx_device_info_t;
 
-/* Possible results of the GPU detection/check.
+/*! \brief Possible results of the GPU detection/check.
  *
  * The egpuInsane value means that during the sanity checks an error
  * occurred that indicates malfunctioning of the device, driver, or
@@ -53,17 +49,27 @@ typedef enum
     egpuCompatible = 0,  egpuNonexistent,  egpuIncompatible, egpuIncompatibleClusterSize, egpuInsane, egpuNR
 } e_gpu_detect_res_t;
 
-/* Names of the GPU detection/check results */
+/*! \brief Names of the GPU detection/check results
+ *
+ * \todo Make a proper class enumeration with helper string */
 extern const char * const gpu_detect_res_str[egpuNR];
 
-/* GPU device information -- includes either CUDA or OpenCL devices.
- * The gmx_hardware_detect module initializes it. */
+/*! \brief Information about GPU devices on this physical node.
+ *
+ * Includes either CUDA or OpenCL devices.  The gmx_hardware_detect
+ * module initializes it.
+ *
+ * \todo Use a std::vector */
 struct gmx_gpu_info_t
 {
-    gmx_bool                  bDetectGPUs;      /* Did we try to detect GPUs? */
-    int                       n_dev;            /* total number of GPU devices detected */
-    struct gmx_device_info_t *gpu_dev;          /* GPU devices detected in the system (per node) */
-    int                       n_dev_compatible; /* number of compatible GPUs */
+    //! Did we attempt GPU detection?
+    gmx_bool           bDetectGPUs;
+    //! Total number of GPU devices detected on this physical node
+    int                n_dev;
+    //! Information about each GPU device detected on this physical node
+    gmx_device_info_t *gpu_dev;
+    //! Number of GPU devices detected on this physical node that are compatible.
+    int                n_dev_compatible;
 };
 
 #endif
index b00648ddb751410b6fdecee56cf39259041dd534..567ba3a70787328ae02db34f83f97d26580eae5f 100644 (file)
@@ -60,11 +60,14 @@ struct gmx_hw_info_t
     ~gmx_hw_info_t();
 
     /* Data for our local physical node */
-    struct gmx_gpu_info_t gpu_info;                /* Information about GPUs detected in the system */
-
-    int                   nthreads_hw_avail;       /* Number of hardware threads available; this number
-                                                      is based on the number of CPUs reported as available
-                                                      by the OS at the time of detection. */
+    //! Information about GPUs detected on this physical node
+    gmx_gpu_info_t gpu_info;
+
+    /*! \brief Number of hardware threads available.
+     *
+     * This number is based on the number of CPUs reported as
+     * available by the OS at the time of detection. */
+    int nthreads_hw_avail;
 
 
     std::unique_ptr<gmx::CpuInfo>          cpuInfo;           /* Information about CPU capabilities */
index f2cc132ecbacb9da149263a71dee0c5d5c59c899..3347dd39dd378adca208f6a1d4696196cbf55193 100644 (file)
@@ -899,12 +899,10 @@ int Mdrunner::mdrunner()
                   "The -dd or -npme option request a parallel simulation, "
 #if !GMX_MPI
                   "but %s was compiled without threads or MPI enabled", output_env_get_program_display_name(oenv));
-#else
-#if GMX_THREAD_MPI
+#elif GMX_THREAD_MPI
                   "but the number of MPI-threads (option -ntmpi) is not set or is 1");
 #else
                   "but %s was not started through mpirun/mpiexec or only one rank was requested through mpirun/mpiexec", output_env_get_program_display_name(oenv));
-#endif
 #endif
     }
 
@@ -1117,12 +1115,6 @@ int Mdrunner::mdrunner()
         gmx_feenableexcept();
     }
 
-    // Build a data structure that expresses which kinds of non-bonded
-    // task are handled by this rank.
-    //
-    // TODO Later, this might become a loop over all registered modules
-    // relevant to the mdp inputs, to find those that have such tasks.
-    //
     // TODO This could move before init_domain_decomposition() as part
     // of refactoring that separates the responsibility for duty
     // assignment from setup for communication between tasks, and
@@ -1133,128 +1125,59 @@ int Mdrunner::mdrunner()
     // that is inconsistent with the presence of actual GPUs on any
     // rank, and that is not known to be a problem until the
     // duty of the ranks on a node become known.
-    //
-    // TODO Later we might need the concept of computeTasksOnThisRank,
-    // from which we construct gpuTasksOnThisRank.
-    //
-    // Currently the DD code assigns duty to ranks that can
-    // include PP work that currently can be executed on a single
-    // GPU, if present and compatible.  This has to be coordinated
-    // across PP ranks on a node, with possible multiple devices
-    // or sharing devices on a node, either from the user
-    // selection, or automatically.
-    auto                 haveGpus = !gpuIdsToUse.empty();
-    std::vector<GpuTask> gpuTasksOnThisRank;
-    if (thisRankHasDuty(cr, DUTY_PP))
-    {
-        if (useGpuForNonbonded)
-        {
-            // Note that any bonded tasks on a GPU always accompany a
-            // non-bonded task.
-            if (haveGpus)
-            {
-                gpuTasksOnThisRank.push_back(GpuTask::Nonbonded);
-            }
-            else if (nonbondedTarget == TaskTarget::Gpu)
-            {
-                gmx_fatal(FARGS, "Cannot run short-ranged nonbonded interactions on a GPU because no GPU is detected.");
-            }
-            else if (bondedTarget == TaskTarget::Gpu)
-            {
-                gmx_fatal(FARGS, "Cannot run bonded interactions on a GPU because no GPU is detected.");
-            }
-        }
-    }
-    // TODO cr->duty & DUTY_PME should imply that a PME algorithm is active, but currently does not.
-    if (EEL_PME(inputrec->coulombtype) && (thisRankHasDuty(cr, DUTY_PME)))
-    {
-        if (useGpuForPme)
-        {
-            if (haveGpus)
-            {
-                gpuTasksOnThisRank.push_back(GpuTask::Pme);
-            }
-            else if (pmeTarget == TaskTarget::Gpu)
-            {
-                gmx_fatal(FARGS, "Cannot run PME on a GPU because no GPU is detected.");
-            }
-        }
-    }
 
-    GpuTaskAssignment gpuTaskAssignment;
-    try
-    {
-        // Produce the task assignment for this rank.
-        gpuTaskAssignment = runTaskAssignment(gpuIdsToUse, userGpuTaskAssignment, *hwinfo,
-                                              mdlog, cr, ms, physicalNodeComm, gpuTasksOnThisRank,
-                                              useGpuForBonded, pmeRunMode);
-    }
-    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
-
-    /* Prevent other ranks from continuing after an issue was found
-     * and reported as a fatal error.
-     *
-     * TODO This function implements a barrier so that MPI runtimes
-     * can organize an orderly shutdown if one of the ranks has had to
-     * issue a fatal error in various code already run. When we have
-     * MPI-aware error handling and reporting, this should be
-     * improved. */
-#if GMX_MPI
-    if (PAR(cr))
-    {
-        MPI_Barrier(cr->mpi_comm_mysim);
-    }
-    if (isMultiSim(ms))
-    {
-        if (SIMMASTER(cr))
-        {
-            MPI_Barrier(ms->mpi_comm_masters);
-        }
-        /* We need another barrier to prevent non-master ranks from contiuing
-         * when an error occured in a different simulation.
-         */
-        MPI_Barrier(cr->mpi_comm_mysim);
+    // Produce the task assignment for this rank.
+    GpuTaskAssignmentsBuilder gpuTaskAssignmentsBuilder;
+    GpuTaskAssignments        gpuTaskAssignments =
+        gpuTaskAssignmentsBuilder.build(gpuIdsToUse,
+                                        userGpuTaskAssignment,
+                                        *hwinfo,
+                                        cr,
+                                        ms,
+                                        physicalNodeComm,
+                                        nonbondedTarget,
+                                        pmeTarget,
+                                        bondedTarget,
+                                        updateTarget,
+                                        useGpuForNonbonded,
+                                        useGpuForPme,
+                                        thisRankHasDuty(cr, DUTY_PP),
+                                        // TODO cr->duty & DUTY_PME should imply that a PME
+                                        // algorithm is active, but currently does not.
+                                        EEL_PME(inputrec->coulombtype) &&
+                                        thisRankHasDuty(cr, DUTY_PME));
+
+    const bool printHostName = (cr->nnodes > 1);
+    gpuTaskAssignments.reportGpuUsage(mdlog, printHostName, useGpuForBonded, pmeRunMode);
+
+    // If the user chose a task assignment, give them some hints
+    // where appropriate.
+    if (!userGpuTaskAssignment.empty())
+    {
+        gpuTaskAssignments.logPerformanceHints(mdlog,
+                                               ssize(gpuIdsToUse));
     }
-#endif
 
     /* Now that we know the setup is consistent, check for efficiency */
-    check_resource_division_efficiency(hwinfo, !gpuTaskAssignment.empty(), mdrunOptions.ntompOptionIsSet,
-                                       cr, mdlog);
-
-    gmx_device_info_t *nonbondedDeviceInfo = nullptr;
-
-    if (thisRankHasDuty(cr, DUTY_PP))
-    {
-        // This works because only one task of each type is currently permitted.
-        auto nbGpuTaskMapping = std::find_if(gpuTaskAssignment.begin(), gpuTaskAssignment.end(),
-                                             hasTaskType<GpuTask::Nonbonded>);
-        if (nbGpuTaskMapping != gpuTaskAssignment.end())
-        {
-            int nonbondedDeviceId = nbGpuTaskMapping->deviceId_;
-            nonbondedDeviceInfo = getDeviceInfo(hwinfo->gpu_info, nonbondedDeviceId);
-            init_gpu(nonbondedDeviceInfo);
-
-            if (DOMAINDECOMP(cr))
-            {
-                /* When we share GPUs over ranks, we need to know this for the DLB */
-                dd_setup_dlb_resource_sharing(cr, nonbondedDeviceId);
-            }
-
-        }
-    }
-
-    gmx_device_info_t                *pmeDeviceInfo = nullptr;
+    check_resource_division_efficiency(hwinfo,
+                                       gpuTaskAssignments.thisRankHasAnyGpuTask(),
+                                       mdrunOptions.ntompOptionIsSet,
+                                       cr,
+                                       mdlog);
+
+    // Get the device handles for the modules, nullptr when no task is assigned.
+    gmx_device_info_t *nonbondedDeviceInfo   = gpuTaskAssignments.initNonbondedDevice(cr);
+    gmx_device_info_t *pmeDeviceInfo         = gpuTaskAssignments.initPmeDevice();
+    const bool         thisRankHasPmeGpuTask = gpuTaskAssignments.thisRankHasPmeGpuTask();
+
+    // TODO should live in ewald module once its testing is improved
+    //
     // Later, this program could contain kernels that might be later
     // re-used as auto-tuning progresses, or subsequent simulations
     // are invoked.
     PmeGpuProgramStorage pmeGpuProgram;
-    // This works because only one task of each type is currently permitted.
-    auto                 pmeGpuTaskMapping     = std::find_if(gpuTaskAssignment.begin(), gpuTaskAssignment.end(), hasTaskType<GpuTask::Pme>);
-    const bool           thisRankHasPmeGpuTask = (pmeGpuTaskMapping != gpuTaskAssignment.end());
     if (thisRankHasPmeGpuTask)
     {
-        pmeDeviceInfo = getDeviceInfo(hwinfo->gpu_info, pmeGpuTaskMapping->deviceId_);
-        init_gpu(pmeDeviceInfo);
         pmeGpuProgram = buildPmeGpuProgram(pmeDeviceInfo);
     }
 
index 9742cbe09fa53abd467b47c63bf0c4353df7bc0c..21f8f69c46ea80fd0d4a7a57af9225daf51ffaad 100644 (file)
@@ -396,3 +396,16 @@ bool isMasterSimMasterRank(const gmx_multisim_t *ms,
 {
     return (isMaster && isMasterSim(ms));
 }
+
+void multiSimBarrier(const gmx_multisim_t *ms)
+{
+    if (isMultiSim(ms))
+    {
+#if GMX_MPI
+        if (ms->mpi_comm_masters != MPI_COMM_NULL)
+        {
+            MPI_Barrier(ms->mpi_comm_masters);
+        }
+#endif
+    }
+}
index c885d64b11548abef1e65579b58c8189aff5779f..8c109fe8c43f7a11361c479fb10380add89bf171 100644 (file)
@@ -146,4 +146,7 @@ bool isMasterSim(const gmx_multisim_t *ms);
 bool isMasterSimMasterRank(const gmx_multisim_t *ms,
                            bool                  isMaster);
 
+//! Make a barrier across all multi-simulation master ranks
+void multiSimBarrier(const gmx_multisim_t *ms);
+
 #endif
index 10716733ff78112967bd415d1f43665e6c93748a..4e12785aedef2d0e4a1252eaa31001bb4e45a0ba 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019, 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 http://www.gromacs.org.
 #include <numeric>
 #include <vector>
 
+#include "gromacs/taskassignment/decidegpuusage.h"
+#include "gromacs/taskassignment/taskassignment.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/gmxmpi.h"
 #include "gromacs/utility/physicalnodecommunicator.h"
 namespace gmx
 {
 
+std::vector<GpuTask>
+findGpuTasksOnThisRank(const bool       haveGpusOnThisPhysicalNode,
+                       const TaskTarget nonbondedTarget,
+                       const TaskTarget pmeTarget,
+                       const TaskTarget bondedTarget,
+                       const TaskTarget updateTarget,
+                       const bool       useGpuForNonbonded,
+                       const bool       useGpuForPme,
+                       const bool       rankHasPpTask,
+                       const bool       rankHasPmeTask)
+{
+    std::vector<GpuTask> gpuTasksOnThisRank;
+    if (rankHasPpTask)
+    {
+        if (useGpuForNonbonded)
+        {
+            // Note that any bonded tasks on a GPU always accompany a
+            // non-bonded task.
+            if (haveGpusOnThisPhysicalNode)
+            {
+                gpuTasksOnThisRank.push_back(GpuTask::Nonbonded);
+            }
+            else if (nonbondedTarget == TaskTarget::Gpu)
+            {
+                gmx_fatal(FARGS, "Cannot run short-ranged nonbonded interactions on a GPU because no GPU is detected.");
+            }
+            else if (bondedTarget == TaskTarget::Gpu)
+            {
+                gmx_fatal(FARGS, "Cannot run bonded interactions on a GPU because no GPU is detected.");
+            }
+            else if (updateTarget == TaskTarget::Gpu)
+            {
+                gmx_fatal(FARGS, "Cannot run coordinate update on a GPU because no GPU is detected.");
+            }
+        }
+    }
+    if (rankHasPmeTask)
+    {
+        if (useGpuForPme)
+        {
+            if (haveGpusOnThisPhysicalNode)
+            {
+                gpuTasksOnThisRank.push_back(GpuTask::Pme);
+            }
+            else if (pmeTarget == TaskTarget::Gpu)
+            {
+                gmx_fatal(FARGS, "Cannot run PME on a GPU because no GPU is detected.");
+            }
+        }
+    }
+    return gpuTasksOnThisRank;
+}
+
 namespace
 {
 
index 090c1c800bc8c714e5b7da5174debaba02765123..b3067d4157e5ba575acddbcb1aaa519916776ffd 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019, 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 http://www.gromacs.org.
 #ifndef GMX_TASKASSIGNMENT_FINDALLGPUTASKS_H
 #define GMX_TASKASSIGNMENT_FINDALLGPUTASKS_H
 
-#include "gromacs/taskassignment/taskassignment.h"
-#include "gromacs/utility/arrayref.h"
+#include <vector>
 
 namespace gmx
 {
 
+enum class GpuTask;
+enum class TaskTarget;
 class PhysicalNodeCommunicator;
+template <typename T> class ArrayRef;
+//! Container of compute tasks suitable to run on a GPU e.g. on each rank of a node.
+using GpuTasksOnRanks = std::vector< std::vector<GpuTask> >;
+
+/*! \brief Returns container of all tasks on this rank
+ * that are eligible for GPU execution.
+ *
+ * \param[in]  haveGpusOnThisPhysicalNode Whether there are any GPUs on this physical node.
+ * \param[in]  nonbondedTarget            The user's choice for mdrun -nb for where to assign
+ *                                        short-ranged nonbonded interaction tasks.
+ * \param[in]  pmeTarget                  The user's choice for mdrun -pme for where to assign
+ *                                        long-ranged PME nonbonded interaction tasks.
+ * \param[in]  bondedTarget               The user's choice for mdrun -bonded for where to assign tasks.
+ * \param[in]  updateTarget               The user's choice for mdrun -update for where to assign tasks.
+ * \param[in]  useGpuForNonbonded         Whether GPUs will be used for nonbonded interactions.
+ * \param[in]  useGpuForPme               Whether GPUs will be used for PME interactions.
+ * \param[in]  rankHasPpTask              Whether this rank has a PP task
+ * \param[in]  rankHasPmeTask             Whether this rank has a PME task
+ */
+std::vector<GpuTask>
+findGpuTasksOnThisRank(bool       haveGpusOnThisPhysicalNode,
+                       TaskTarget nonbondedTarget,
+                       TaskTarget pmeTarget,
+                       TaskTarget bondedTarget,
+                       TaskTarget updateTarget,
+                       bool       useGpuForNonbonded,
+                       bool       useGpuForPme,
+                       bool       rankHasPpTask,
+                       bool       rankHasPmeTask);
 
 /*! \brief Returns container of all tasks on all ranks of this node
  * that are eligible for GPU execution.
index afc0dde4348b135da1efde87e607305d7c07db5c..830a69212b9673f31573ef9dec96cf84ac81aa5d 100644 (file)
@@ -47,6 +47,8 @@
 
 #include "gromacs/ewald/pme.h"
 #include "gromacs/gpu_utils/gpu_utils.h"
+#include "gromacs/taskassignment/taskassignment.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/logger.h"
 #include "gromacs/utility/stringutil.h"
@@ -64,7 +66,8 @@ namespace
  * GPUs used (per node) can be different from the number of GPU IDs
  * used.
  */
-size_t countUniqueGpuIdsUsed(const GpuTaskAssignments &gpuTaskAssignmentOnRanksOfThisNode)
+size_t
+countUniqueGpuIdsUsed(ArrayRef<const GpuTaskAssignment> gpuTaskAssignmentOnRanksOfThisNode)
 {
     std::set<int> uniqueIds;
     for (const auto &assignmentsOnRank : gpuTaskAssignmentOnRanksOfThisNode)
@@ -80,13 +83,13 @@ size_t countUniqueGpuIdsUsed(const GpuTaskAssignments &gpuTaskAssignmentOnRanksO
 }   // namespace
 
 void
-reportGpuUsage(const MDLogger                &mdlog,
-               const GpuTaskAssignments      &gpuTaskAssignmentOnRanksOfThisNode,
-               size_t                         numGpuTasksOnThisNode,
-               size_t                         numRanks,
-               bool                           bPrintHostName,
-               bool                           useGpuForBonded,
-               PmeRunMode                     pmeRunMode)
+reportGpuUsage(const MDLogger                   &mdlog,
+               ArrayRef<const GpuTaskAssignment> gpuTaskAssignmentOnRanksOfThisNode,
+               size_t                            numGpuTasksOnThisNode,
+               size_t                            numRanks,
+               bool                              printHostName,
+               bool                              useGpuForBonded,
+               PmeRunMode                        pmeRunMode)
 {
     size_t numGpusInUse = countUniqueGpuIdsUsed(gpuTaskAssignmentOnRanksOfThisNode);
     if (numGpusInUse == 0)
@@ -120,7 +123,7 @@ reportGpuUsage(const MDLogger                &mdlog,
         }
         bool        bPluralGpus  = numGpusInUse > 1;
 
-        if (bPrintHostName)
+        if (printHostName)
         {
             char host[STRLEN];
             gmx_gethostname(host, STRLEN);
index 2dd3228e10525bffc7b38e88e1b360857c43f3e4..9de34dd20f888663c5aedc32e9afa4f99c9afff3 100644 (file)
@@ -48,7 +48,7 @@
 
 #include <cstdlib>
 
-#include "gromacs/taskassignment/taskassignment.h"
+#include <vector>
 
 enum class PmeRunMode;
 
@@ -56,6 +56,9 @@ namespace gmx
 {
 
 class MDLogger;
+struct GpuTaskMapping;
+template <typename T> class ArrayRef;
+using GpuTaskAssignment = std::vector <GpuTaskMapping>;
 
 /*! \brief Log a report on how GPUs are being used on
  * the ranks of the physical node of rank 0 of the simulation.
@@ -67,19 +70,19 @@ class MDLogger;
  * \param[in]  gpuTaskAssignmentOnRanksOfThisNode  The selected GPU IDs.
  * \param[in]  numGpuTasksOnThisNode               The number of GPU tasks on this node.
  * \param[in]  numPpRanks                          Number of PP ranks on this node
- * \param[in]  bPrintHostName                      Print the hostname in the usage information
+ * \param[in]  printHostName                       Print the hostname in the usage information
  * \param[in]  useGpuForBonded                     Whether GPU PP tasks will do bonded work on the GPU
  * \param[in]  pmeRunMode                          Describes the execution of PME tasks
  *
  * \throws     std::bad_alloc if out of memory */
 void
-reportGpuUsage(const MDLogger                &mdlog,
-               const GpuTaskAssignments      &gpuTaskAssignmentOnRanksOfThisNode,
-               size_t                         numGpuTasksOnThisNode,
-               size_t                         numPpRanks,
-               bool                           bPrintHostName,
-               bool                           useGpuForBonded,
-               PmeRunMode                     pmeRunMode);
+reportGpuUsage(const MDLogger                   &mdlog,
+               ArrayRef<const GpuTaskAssignment> gpuTaskAssignmentOnRanksOfThisNode,
+               size_t                            numGpuTasksOnThisNode,
+               size_t                            numPpRanks,
+               bool                              printHostName,
+               bool                              useGpuForBonded,
+               PmeRunMode                        pmeRunMode);
 
 }  // namespace gmx
 
index 5b77152fd3642c656ceb5a09bca1282c8a0345df..62071fe19fb914cc525833596785b2e26ab72a4a 100644 (file)
 
 #include "taskassignment.h"
 
-#include "config.h"
-
+#include <algorithm>
 #include <string>
 #include <vector>
 
+#include "gromacs/domdec/domdec.h"
+#include "gromacs/gmxlib/network.h"
+#include "gromacs/gpu_utils/gpu_utils.h"
 #include "gromacs/hardware/hw_info.h"
 #include "gromacs/mdrunutility/multisim.h"
 #include "gromacs/mdtypes/commrec.h"
@@ -87,11 +89,11 @@ namespace
  *                                         that are eligible to run on GPUs.
  * \param[in]   gpuIds                     The user-supplied GPU IDs.
  */
-GpuTaskAssignments
+std::vector<GpuTaskAssignment>
 buildTaskAssignment(const GpuTasksOnRanks  &gpuTasksOnRanksOfThisNode,
                     ArrayRef<const int>     gpuIds)
 {
-    GpuTaskAssignments gpuTaskAssignmentOnRanksOfThisNode(gpuTasksOnRanksOfThisNode.size());
+    std::vector<GpuTaskAssignment> gpuTaskAssignmentOnRanksOfThisNode(gpuTasksOnRanksOfThisNode.size());
 
     // Loop over the ranks on this node, and the tasks on each
     // rank. For each task, take the next device ID from those
@@ -121,7 +123,7 @@ buildTaskAssignment(const GpuTasksOnRanks  &gpuTasksOnRanksOfThisNode,
  *
  * Sharing GPUs among multiple ranks is possible via either user or
  * automated selection. */
-bool isAnyGpuSharedBetweenRanks(const GpuTaskAssignments &gpuTaskAssignments)
+bool isAnyGpuSharedBetweenRanks(ArrayRef<const GpuTaskAssignment> gpuTaskAssignments)
 {
     // Loop over all ranks i, looking on all higher ranks j whether
     // any tasks on them share GPU device IDs.
@@ -147,13 +149,13 @@ bool isAnyGpuSharedBetweenRanks(const GpuTaskAssignments &gpuTaskAssignments)
     return false;
 }
 
-//! Logs to \c mdlog information that may help a user learn how to let mdrun make a task assignment that runs faster.
-void logPerformanceHints(const MDLogger           &mdlog,
-                         size_t                    numCompatibleGpus,
-                         size_t                    numGpuTasksOnThisNode,
-                         const GpuTaskAssignments &gpuTaskAssignments)
+}   // namespace
+
+void
+GpuTaskAssignments::logPerformanceHints(const MDLogger &mdlog,
+                                        size_t          numCompatibleGpusOnThisNode)
 {
-    if (numCompatibleGpus > numGpuTasksOnThisNode)
+    if (numCompatibleGpusOnThisNode > numGpuTasksOnThisNode_)
     {
         /* TODO In principle, this warning could be warranted only on
          * some nodes, but we lack the infrastructure to do a good job
@@ -163,13 +165,16 @@ void logPerformanceHints(const MDLogger           &mdlog,
                        "available on that node are unused, which might not be optimal.");
     }
 
-    if (isAnyGpuSharedBetweenRanks(gpuTaskAssignments))
+    if (isAnyGpuSharedBetweenRanks(assignmentForAllRanksOnThisNode_))
     {
         GMX_LOG(mdlog.warning).asParagraph().
             appendText("NOTE: You assigned the same GPU ID(s) to multiple ranks, which is a good idea if you have measured the performance of alternatives.");
     }
 }
 
+namespace
+{
+
 //! Counts all the GPU tasks on this node.
 size_t countGpuTasksOnThisNode(const GpuTasksOnRanks &gpuTasksOnRanksOfThisNode)
 {
@@ -183,25 +188,41 @@ size_t countGpuTasksOnThisNode(const GpuTasksOnRanks &gpuTasksOnRanksOfThisNode)
 
 }   // namespace
 
-GpuTaskAssignments::value_type
-runTaskAssignment(const std::vector<int>         &gpuIdsToUse,
-                  const std::vector<int>         &userGpuTaskAssignment,
-                  const gmx_hw_info_t            &hardwareInfo,
-                  const MDLogger                 &mdlog,
-                  const t_commrec                *cr,
-                  const gmx_multisim_t           *ms,
-                  const PhysicalNodeCommunicator &physicalNodeComm,
-                  const std::vector<GpuTask>     &gpuTasksOnThisRank,
-                  bool                            useGpuForBonded,
-                  PmeRunMode                      pmeRunMode)
+GpuTaskAssignmentsBuilder::GpuTaskAssignmentsBuilder() = default;
+
+GpuTaskAssignments
+GpuTaskAssignmentsBuilder::build(const std::vector<int>         &gpuIdsToUse,
+                                 const std::vector<int>         &userGpuTaskAssignment,
+                                 const gmx_hw_info_t            &hardwareInfo,
+                                 const t_commrec                *cr,
+                                 const gmx_multisim_t           *ms,
+                                 const PhysicalNodeCommunicator &physicalNodeComm,
+                                 const TaskTarget                nonbondedTarget,
+                                 const TaskTarget                pmeTarget,
+                                 const TaskTarget                bondedTarget,
+                                 const TaskTarget                updateTarget,
+                                 const bool                      useGpuForNonbonded,
+                                 const bool                      useGpuForPme,
+                                 bool                            rankHasPpTask,
+                                 bool                            rankHasPmeTask)
 {
+    size_t               numRanksOnThisNode = physicalNodeComm.size_;
+    std::vector<GpuTask> gpuTasksOnThisRank = findGpuTasksOnThisRank(!gpuIdsToUse.empty(),
+                                                                     nonbondedTarget,
+                                                                     pmeTarget,
+                                                                     bondedTarget,
+                                                                     updateTarget,
+                                                                     useGpuForNonbonded,
+                                                                     useGpuForPme,
+                                                                     rankHasPpTask,
+                                                                     rankHasPmeTask);
     /* Communicate among ranks on this node to find each task that can
      * be executed on a GPU, on each rank. */
-    auto               gpuTasksOnRanksOfThisNode = findAllGpuTasksOnThisNode(gpuTasksOnThisRank,
-                                                                             physicalNodeComm);
-    auto               numGpuTasksOnThisNode = countGpuTasksOnThisNode(gpuTasksOnRanksOfThisNode);
+    auto   gpuTasksOnRanksOfThisNode = findAllGpuTasksOnThisNode(gpuTasksOnThisRank,
+                                                                 physicalNodeComm);
+    size_t numGpuTasksOnThisNode = countGpuTasksOnThisNode(gpuTasksOnRanksOfThisNode);
 
-    GpuTaskAssignments taskAssignmentOnRanksOfThisNode;
+    std::vector<GpuTaskAssignment> taskAssignmentOnRanksOfThisNode;
     try
     {
         // Use the GPU IDs from the user if they supplied
@@ -227,7 +248,7 @@ runTaskAssignment(const std::vector<int>         &gpuIdsToUse,
             ArrayRef<const int> compatibleGpusToUse = gpuIdsToUse;
 
             // enforce the single device/rank restriction
-            if (physicalNodeComm.size_ == 1 && !compatibleGpusToUse.empty())
+            if (numRanksOnThisNode == 1 && !compatibleGpusToUse.empty())
             {
                 compatibleGpusToUse = compatibleGpusToUse.subArray(0, 1);
             }
@@ -293,40 +314,115 @@ runTaskAssignment(const std::vector<int>         &gpuIdsToUse,
             printFatalErrorMessage(stderr, ex);
         }
 
-        if (PAR(cr))
-        {
-#if GMX_MPI
-            MPI_Barrier(cr->mpi_comm_mysim);
-#endif
-        }
-        if (isMultiSim(ms))
+        gmx_exit_on_fatal_error(ExitType_Abort, 1);
+    }
+    // TODO This implements a global barrier so that MPI runtimes can
+    // organize an orderly shutdown if one of the ranks has had to
+    // issue a fatal error after an exception detected only on one
+    // rank. When we have MPI-aware error handling and reporting, this
+    // should be improved.
+    multiSimBarrier(ms);
+    simulationBarrier(cr);
+
+    // TODO There is no check that mdrun -nb gpu or -pme gpu or
+    // -gpu_id is actually being implemented such that nonbonded tasks
+    // are being run on compatible GPUs, on all applicable ranks. That
+    // would require communication.
+
+    GpuTaskAssignments gpuTaskAssignments(hardwareInfo);
+    gpuTaskAssignments.assignmentForAllRanksOnThisNode_ = taskAssignmentOnRanksOfThisNode;
+    gpuTaskAssignments.indexOfThisRank_                 = physicalNodeComm.rank_;
+    gpuTaskAssignments.numGpuTasksOnThisNode_           = numGpuTasksOnThisNode;
+    gpuTaskAssignments.numRanksOnThisNode_              = numRanksOnThisNode;
+    return gpuTaskAssignments;
+}
+
+GpuTaskAssignments::GpuTaskAssignments(const gmx_hw_info_t &hardwareInfo)
+    : hardwareInfo_(hardwareInfo)
+{
+}
+
+void
+GpuTaskAssignments::reportGpuUsage(const MDLogger &mdlog,
+                                   bool            printHostName,
+                                   bool            useGpuForBonded,
+                                   PmeRunMode      pmeRunMode)
+{
+    gmx::reportGpuUsage(mdlog,
+                        assignmentForAllRanksOnThisNode_,
+                        numGpuTasksOnThisNode_,
+                        numRanksOnThisNode_,
+                        printHostName,
+                        useGpuForBonded,
+                        pmeRunMode);
+}
+
+gmx_device_info_t *
+GpuTaskAssignments::initNonbondedDevice(const t_commrec *cr) const
+{
+    gmx_device_info_t       *deviceInfo        = nullptr;
+    const GpuTaskAssignment &gpuTaskAssignment =
+        assignmentForAllRanksOnThisNode_[indexOfThisRank_];
+
+    // This works because only one task of each type per rank is currently permitted.
+    auto nbGpuTaskMapping = std::find_if(gpuTaskAssignment.begin(), gpuTaskAssignment.end(),
+                                         hasTaskType<GpuTask::Nonbonded>);
+    if (nbGpuTaskMapping != gpuTaskAssignment.end())
+    {
+        int deviceId = nbGpuTaskMapping->deviceId_;
+        deviceInfo = getDeviceInfo(hardwareInfo_.gpu_info, deviceId);
+        init_gpu(deviceInfo);
+
+        // TODO Setting up this sharing should probably part of
+        // init_domain_decomposition after further refactoring.
+        if (DOMAINDECOMP(cr))
         {
-#if GMX_MPI
-            MPI_Barrier(ms->mpi_comm_masters);
-#endif
+            /* When we share GPUs over ranks, we need to know this for the DLB */
+            dd_setup_dlb_resource_sharing(cr, deviceId);
         }
-
-        gmx_exit_on_fatal_error(ExitType_Abort, 1);
     }
+    return deviceInfo;
+}
 
-    reportGpuUsage(mdlog, taskAssignmentOnRanksOfThisNode,
-                   numGpuTasksOnThisNode, physicalNodeComm.size_, cr->nnodes > 1,
-                   useGpuForBonded, pmeRunMode);
+gmx_device_info_t *
+GpuTaskAssignments::initPmeDevice() const
+{
+    gmx_device_info_t       *deviceInfo        = nullptr;
+    const GpuTaskAssignment &gpuTaskAssignment =
+        assignmentForAllRanksOnThisNode_[indexOfThisRank_];
 
-    // If the user chose a task assignment, give them some hints where appropriate.
-    if (!userGpuTaskAssignment.empty())
+    // This works because only one task of each type is currently permitted.
+    auto       pmeGpuTaskMapping     = std::find_if(gpuTaskAssignment.begin(), gpuTaskAssignment.end(),
+                                                    hasTaskType<GpuTask::Pme>);
+    const bool thisRankHasPmeGpuTask = (pmeGpuTaskMapping != gpuTaskAssignment.end());
+    if (thisRankHasPmeGpuTask)
     {
-        logPerformanceHints(mdlog, gpuIdsToUse.size(),
-                            numGpuTasksOnThisNode,
-                            taskAssignmentOnRanksOfThisNode);
+        deviceInfo = getDeviceInfo(hardwareInfo_.gpu_info, pmeGpuTaskMapping->deviceId_);
+        init_gpu(deviceInfo);
     }
+    return deviceInfo;
+}
 
-    return taskAssignmentOnRanksOfThisNode[physicalNodeComm.rank_];
+bool
+GpuTaskAssignments::thisRankHasPmeGpuTask() const
+{
+    const GpuTaskAssignment &gpuTaskAssignment =
+        assignmentForAllRanksOnThisNode_[indexOfThisRank_];
 
-    // TODO There is no check that mdrun -nb gpu or -pme gpu or
-    // -gpu_id is actually being implemented such that nonbonded tasks
-    // are being run on compatible GPUs, on all applicable ranks. That
-    // would require communication.
+    auto       pmeGpuTaskMapping     = std::find_if(gpuTaskAssignment.begin(), gpuTaskAssignment.end(), hasTaskType<GpuTask::Pme>);
+    const bool thisRankHasPmeGpuTask = (pmeGpuTaskMapping != gpuTaskAssignment.end());
+
+    return thisRankHasPmeGpuTask;
+}
+
+bool
+GpuTaskAssignments::thisRankHasAnyGpuTask() const
+{
+    const GpuTaskAssignment &gpuTaskAssignment =
+        assignmentForAllRanksOnThisNode_[indexOfThisRank_];
+
+    const bool thisRankHasAnyGpuTask = !gpuTaskAssignment.empty();
+    return thisRankHasAnyGpuTask;
 }
 
 }  // namespace gmx
index 0c57951d0512bcd25bda2ed097b8dbd1f0b1c016..28400bdccfcf6262f5d385d9f1e15c5e11b20aeb 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2017,2018, by the GROMACS development team, led by
+ * Copyright (c) 2017,2018,2019, 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 http://www.gromacs.org.
@@ -52,6 +52,9 @@
 
 #include <vector>
 
+#include "gromacs/utility/basedefinitions.h"
+
+struct gmx_device_info_t;
 struct gmx_hw_info_t;
 struct gmx_multisim_t;
 struct t_commrec;
@@ -61,6 +64,7 @@ enum class PmeRunMode;
 namespace gmx
 {
 
+enum class TaskTarget;
 class MDLogger;
 class PhysicalNodeCommunicator;
 
@@ -88,47 +92,167 @@ struct GpuTaskMapping
 
 //! Container of GPU tasks on a rank, specifying the task type and GPU device ID, e.g. potentially ready for consumption by the modules on that rank.
 using GpuTaskAssignment = std::vector <GpuTaskMapping>;
-//! Container of compute tasks suitable to run on a GPU e.g. on each rank of a node.
-using GpuTasksOnRanks = std::vector< std::vector<GpuTask> >;
-//! Container of RankGpuTaskAssignments e.g. for all ranks on a node.
-using GpuTaskAssignments = std::vector<GpuTaskAssignment>;
 
-/*! \brief Coordinate the final stages of task assignment and
- * reporting, and return the assignment for this rank.
+class GpuTaskAssignments;
+
+/*! \libinternal
+ * \brief Builder for the GpuTaskAssignments for all ranks on this
+ * node.
+ *
+ * This will coordinate the final stages of task assignment and
+ * reporting, and build the GpuTaskAssignments object used to
+ * configure the modules that might run tasks on GPUs.
  *
  * Communicates between ranks on a node to coordinate task assignment
  * between them onto available hardware, e.g. accelerators.
  *
- * Releases the taskAssigner once its work is complete.
+ * \todo Later, this might become a loop over all registered modules
+ * relevant to the mdp inputs, to find those that have such tasks.
  *
- * \param[in]  gpuIdsToUse                The compatible GPUs that the user permitted us to use.
- * \param[in]  userGpuTaskAssignment      The user-specified assignment of GPU tasks to device IDs.
- * \param[in]  hardwareInfo               The detected hardware
- * \param[in]  mdlog                      Logging object to write to.
- * \param[in]  cr                         Communication object.
- * \param[in]  ms                         Multi-simulation handler.
- * \param[in]  physicalNodeComm           Communication object for this physical node.
- * \param[in]  gpuTasksOnThisRank         Information about what GPU tasks
- *                                        exist on this rank.
- * \param[in]  useGpuForBonded            Whether GPU PP tasks will do bonded work on the GPU
- * \param[in]  pmeRunMode                 Describes the execution of PME tasks
+ * \todo Later we might need the concept of computeTasksOnThisRank,
+ * from which we construct gpuTasksOnThisRank.
  *
- * \returns  A GPU task assignment for this rank.
+ * Currently the DD code assigns duty to ranks that can
+ * include PP work that currently can be executed on a single
+ * GPU, if present and compatible.  This has to be coordinated
+ * across PP ranks on a node, with possible multiple devices
+ * or sharing devices on a node, either from the user
+ * selection, or automatically. */
+class GpuTaskAssignmentsBuilder
+{
+    public:
+        //! Constructor
+        GpuTaskAssignmentsBuilder();
+
+        /*! \brief Builds a GpuTaskAssignments
+         *
+         * This method reconciles
+         *
+         *   - user mdrun command-line options,
+         *   - the results of hardware detection
+         *   - the duty assigned by the DD setup,
+         *   - the requested simulation modules, and
+         *   - the possible existence of multi-simulations
+         *
+         * to assign the GPUs on each physical node to the tasks on
+         * the ranks of that node.
+         *
+         * \param[in]  gpuIdsToUse            The compatible GPUs that the user permitted us to use.
+         * \param[in]  userGpuTaskAssignment  The user-specified assignment of GPU tasks to device IDs.
+         * \param[in]  hardwareInfo           The detected hardware
+         * \param[in]  cr                     Communication object.
+         * \param[in]  ms                     Multi-simulation handler.
+         * \param[in]  physicalNodeComm       Communication object for this physical node.
+         * \param[in]  nonbondedTarget        The user's choice for mdrun -nb for where to assign
+         *                                    short-ranged nonbonded interaction tasks.
+         * \param[in]  pmeTarget              The user's choice for mdrun -pme for where to assign
+         *                                    long-ranged PME nonbonded interaction tasks.
+         * \param[in]  bondedTarget           The user's choice for mdrun -bonded for where to assign tasks.
+         * \param[in]  updateTarget           The user's choice for mdrun -update for where to assign tasks.
+         * \param[in]  useGpuForNonbonded     Whether GPUs will be used for nonbonded interactions.
+         * \param[in]  useGpuForPme           Whether GPUs will be used for PME interactions.
+         * \param[in]  rankHasPpTask          Whether this rank has a PP task
+         * \param[in]  rankHasPmeTask         Whether this rank has a PME task
+         *
+         * \throws   std::bad_alloc          If out of memory.
+         *           InconsistentInputError  If user and/or detected inputs are inconsistent.
+         */
+        GpuTaskAssignments build(const std::vector<int>         &gpuIdsToUse,
+                                 const std::vector<int>         &userGpuTaskAssignment,
+                                 const gmx_hw_info_t            &hardwareInfo,
+                                 const t_commrec                *cr,
+                                 const gmx_multisim_t           *ms,
+                                 const PhysicalNodeCommunicator &physicalNodeComm,
+                                 TaskTarget                      nonbondedTarget,
+                                 TaskTarget                      pmeTarget,
+                                 TaskTarget                      bondedTarget,
+                                 TaskTarget                      updateTarget,
+                                 bool                            useGpuForNonbonded,
+                                 bool                            useGpuForPme,
+                                 bool                            rankHasPpTask,
+                                 bool                            rankHasPmeTask);
+};
+
+/*! \libinternal
+ * \brief Contains the GPU task assignment for all ranks on this
+ * physical node.
  *
- * \throws   std::bad_alloc          If out of memory.
- *           InconsistentInputError  If user and/or detected inputs are inconsistent.
- */
-GpuTaskAssignments::value_type
-runTaskAssignment(const std::vector<int>         &gpuIdsToUse,
-                  const std::vector<int>         &userGpuTaskAssignment,
-                  const gmx_hw_info_t            &hardwareInfo,
-                  const MDLogger                 &mdlog,
-                  const t_commrec                *cr,
-                  const gmx_multisim_t           *ms,
-                  const PhysicalNodeCommunicator &physicalNodeComm,
-                  const std::vector<GpuTask>     &gpuTasksOnThisRank,
-                  bool                            useGpuForBonded,
-                  PmeRunMode                      pmeRunMode);
+ * This can be used to configure the modules that might run tasks on
+ * GPUs.
+ *
+ * This assignment is made by a GpuTaskAssignmentsBuilder object. */
+class GpuTaskAssignments
+{
+    public:
+        //! Public move constructor to use with the builder
+        GpuTaskAssignments(GpuTaskAssignments &&source) noexcept = default;
+    private:
+        // Let the builder handle construction
+        friend class GpuTaskAssignmentsBuilder;
+        //! Private constructor so only the builder can construct
+        GpuTaskAssignments(const gmx_hw_info_t &hardwareInfo);
+        /*! \brief Information about hardware on this physical node
+         *
+         * The lifetime of the object referred to must exceed that
+         * of this object. */
+        const gmx_hw_info_t           &hardwareInfo_;
+        //! The GPU task assignment for all ranks on this node
+        std::vector<GpuTaskAssignment> assignmentForAllRanksOnThisNode_;
+        /*! \brief The index of this rank within those on this node.
+         *
+         * This is useful for indexing into \c
+         * assignmentForAllRanksOnThisNode_. */
+        index  indexOfThisRank_ = -1;
+        //! Number of GPU tasks on this node.
+        size_t numGpuTasksOnThisNode_ = 0;
+        //! Number of ranks on this physical node.
+        size_t numRanksOnThisNode_ = 0;
+    public:
+        /*! \brief Log a report on how GPUs are being used on
+         * the ranks of the physical node of rank 0 of the simulation.
+         *
+         * \todo It could be useful to report also whether any nodes differed,
+         * and in what way.
+         *
+         * \param[in]  mdlog           Logging object.
+         * \param[in]  printHostName   Print the hostname in the usage information
+         * \param[in]  useGpuForBonded Whether GPU PP tasks will do bonded work on the GPU
+         * \param[in]  pmeRunMode      Describes the execution of PME tasks
+         *
+         * \throws     std::bad_alloc if out of memory */
+        void
+        reportGpuUsage(const MDLogger &mdlog,
+                       bool            printHostName,
+                       bool            useGpuForBonded,
+                       PmeRunMode      pmeRunMode);
+        /*! \brief Logs to \c mdlog information that may help a user
+         * learn how to let mdrun make a task assignment that runs
+         * faster.
+         *
+         * \param[in]  mdlog                        Logging object.
+         * \param[in]  numCompatibleGpusOnThisNode  The number of compatible GPUs on this node.
+         * */
+        void logPerformanceHints(const MDLogger &mdlog,
+                                 size_t          numCompatibleGpusOnThisNode);
+        /*! \brief Return handle to the initialized GPU to use for the
+         * nonbonded task on this rank, if any.
+         *
+         * Returns nullptr if no such task is assigned to this rank.
+         *
+         * \todo This also sets up DLB for device sharing, where
+         * appropriate, but that responsbility should move
+         * elsewhere. */
+        gmx_device_info_t *initNonbondedDevice(const t_commrec *cr) const;
+        /*! \brief Return handle to the initialized GPU to use for the
+         * PME task on this rank, if any.
+         *
+         * Returns nullptr if no such task is assigned to this rank. */
+        gmx_device_info_t *initPmeDevice() const;
+        //! Return whether this rank has a PME task running on a GPU
+        bool thisRankHasPmeGpuTask() const;
+        //! Return whether this rank has any task running on a GPU
+        bool thisRankHasAnyGpuTask() const;
+};
 
 //! Function for whether the task of \c mapping has value \c TaskType.
 template<GpuTask TaskType>
index 4b496e77fa3b26286023b56dc5caa7498e006d00..ec74c3c1be9920a719691ae19ae53df401508090 100644 (file)
@@ -78,13 +78,13 @@ std::vector<int>
 parseUserGpuIdString(const std::string &gpuIdString);
 
 /*! \brief Implement GPU ID selection by returning the available GPU
- * IDs that are compatible.
+ * IDs on this physical node that are compatible.
  *
  * If the string supplied by the user is empty, then return the IDs of
- * all compatible GPUs. Otherwise, check the user specified compatible
- * GPUs and return their IDs.
+ * all compatible GPUs on this physical node. Otherwise, check the
+ * user specified compatible GPUs and return their IDs.
  *
- * \param[in]  gpuInfo                Information detected about GPUs
+ * \param[in]  gpuInfo                Information detected about GPUs on this physical node
  * \param[in]  gpuIdsAvailableString  String like "013" or "0,1,3" typically
  *                                    supplied by the user to mdrun -gpu_id.
  *                                    Must contain only unique decimal digits, or only decimal
@@ -92,7 +92,7 @@ parseUserGpuIdString(const std::string &gpuIdString);
  *                                    comma is accceptable (and required to specify a
  *                                    single ID that is larger than 9).
  *
- * \returns  A vector of unique compatible GPU IDs.
+ * \returns  A vector of unique compatible GPU IDs on this physical node.
  *
  * \throws   std::bad_alloc     If out of memory.
  *           InvalidInputError  If an invalid character is found (ie not a digit or ',') or if