Skip to content

File kokkos_guard.h

File List > code_source > templat > include > TempLat > parallel > devices > kokkos > session > kokkos_guard.h

Go to the documentation of this file

#ifndef TEMPLAT_PARALLEL_KOKKOS_SESSION_KOKKOSGUARD_H
#define TEMPLAT_PARALLEL_KOKKOS_SESSION_KOKKOSGUARD_H

/* This file is part of TempLat, available at https://cosmolattice.github.io/templat .
   Copyright 2021-2026 The TempLat authors, see AUTHORS.md.
   Released under the MIT license, see LICENSE.md. */

// File info: Main contributor(s): Franz R. Sattler, Year: 2025

#include "TempLat/util/exception.h"
#include "TempLat/parallel/devices/kokkos/kokkos.h"
#include "TempLat/parallel/threadsettings.h"
#include "TempLat/util/log/saycomplete.h"

#ifdef HAVE_MPI
#include <mpi.h>
#endif

namespace TempLat::device_kokkos
{
  MakeException(KokkosDeviceGuardInstantiationException);

  class DeviceGuard
  {
  public:
    // Put public methods here. These should change very little over time.

    DeviceGuard(int argc, char **argv, bool verbose = false)
        : instanceProtectionKey(InstanceCounter(1)), mVerbose(verbose)
    {
      auto threadSettings = ThreadSettings::getInstance();

      Kokkos::InitializationSettings kokkos_settings;
      kokkos_settings.set_print_configuration(this->mVerbose);
      kokkos_settings.set_num_threads(threadSettings.getMaxThreadCount());

// We need to do load-balancing here, if we are using GPU + MPI
#ifdef HAVE_MPI
#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP)
      int num_devices = Kokkos::num_devices();

      // First, make an MPI group for the local machine.
      MPI_Comm shmcomm;
      MPI_Comm_split_type(MPI_COMM_WORLD, MPI_COMM_TYPE_SHARED, 0, MPI_INFO_NULL, &shmcomm);
      sShmComm = shmcomm;
      int shmrank, shmsize;
      MPI_Comm_rank(shmcomm, &shmrank);
      MPI_Comm_size(shmcomm, &shmsize);

      int gpuNoConstrain = 0;
      try {
        if (const char *env_p = std::getenv("GPU_NOCONSTRAIN")) gpuNoConstrain = std::stoi(env_p);
      } catch (...) {
        throw KokkosDeviceGuardInstantiationException(
            "Error parsing GPU_NOCONSTRAIN environment variable. Expected an integer. Got: ",
            std::getenv("GPU_NOCONSTRAIN"));
      }

      // Check if there are more processes on this node than devices.
      if (num_devices < shmsize && gpuNoConstrain == 0)
        throw KokkosDeviceGuardInstantiationException(
            "There are more MPI processes on this node than available GPU devices. This will lead to "
            "problems. Number of devices: ",
            num_devices, ", number of processes on this node: ", shmsize);

      int rank;
      MPI_Comm_rank(MPI_COMM_WORLD, &rank);

      // Assign devices to processes in a round-robin fashion.
      sDeviceId = shmrank % num_devices;
      kokkos_settings.set_device_id(sDeviceId);
      std::cout << "Global process rank " << rank << " (local rank " << shmrank << " on this node) assigned to device "
                << sDeviceId << std::endl;
#endif
#endif

      Kokkos::initialize(kokkos_settings);
    }

    ~DeviceGuard() { Kokkos::finalize(); }

  private:
    /* Put all member variables and private methods here. These may change arbitrarily. */
    int instanceProtectionKey;
    bool mVerbose;
    static inline int InstanceCounter(int delta = 0)
    {
      static int counter = 0;
      counter += delta;
      if (counter > 1)
        throw KokkosDeviceGuardInstantiationException(
            "Per process, Kokkos' DeviceGuard can be instantiated only once. This "
            "should be done in `int main()`. This is wrong. Instances:",
            counter);
      return counter;
    }

  public:
    static int GetInstanceCount() { return InstanceCounter(); }

#ifdef HAVE_MPI
#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP)
    static MPI_Comm getShmComm() { return sShmComm; }
    static int getDeviceId() { return sDeviceId; }

  private:
    static inline MPI_Comm sShmComm = MPI_COMM_NULL;
    static inline int sDeviceId = -1;
#else
  public:
    static MPI_Comm getShmComm() { return MPI_COMM_NULL; }
    static int getDeviceId() { return 0; }
#endif
#else
  public:
    static int getDeviceId() { return 0; }
#endif
  };
} // namespace TempLat::device_kokkos

#endif