GMX_ASSERT(pmeGpuProgram != nullptr, "GPU kernels must be already compiled");
pmeGpu->programHandle_ = pmeGpuProgram;
+ pmeGpu->initializedClfftLibrary_ = std::make_unique<gmx::ClfftInitializer>();
+
pme_gpu_init_internal(pmeGpu);
pme_gpu_alloc_energy_virial(pmeGpu);
#include "gromacs/ewald/pme.h"
#include "gromacs/ewald/pme_gpu_program.h"
+#include "gromacs/gpu_utils/clfftinitializer.h"
#include "gromacs/gpu_utils/gpu_utils.h" // for GpuApiCallBehavior
#include "gromacs/gpu_utils/hostallocator.h"
#include "gromacs/math/vectypes.h"
//! A handle to the program created by buildPmeGpuProgram()
PmeGpuProgramHandle programHandle_;
+ //! Handle that ensures the clFFT library has been initialized once per process.
+ std::unique_ptr<gmx::ClfftInitializer> initializedClfftLibrary_;
+
/*! \brief The settings. */
PmeGpuSettings settings;
#include "config.h"
-#include <memory>
-
+#include "gromacs/utility/basedefinitions.h"
#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/mutex.h"
#include "gromacs/utility/stringutil.h"
#if GMX_GPU == GMX_GPU_OPENCL
namespace gmx
{
+namespace
+{
+
+/*! \brief The clFFT library may only be initialized once per process,
+ * and this is orchestrated by this shared value and mutex.
+ *
+ * This ensures that thread-MPI and OpenMP builds can't accidentally
+ * initialize it more than once. */
+//! @{
+gmx_unused bool g_clfftInitialized = false;
+gmx::Mutex g_clfftMutex;
+//! @}
+
+} // namespace
+
ClfftInitializer::ClfftInitializer()
{
#if GMX_GPU == GMX_GPU_OPENCL
- clfftSetupData fftSetup;
- int initErrorCode = clfftInitSetupData(&fftSetup);
+ gmx::lock_guard<gmx::Mutex> guard(g_clfftMutex);
+ clfftSetupData fftSetup;
+ int initErrorCode = clfftInitSetupData(&fftSetup);
if (initErrorCode != 0)
{
GMX_THROW(InternalError(formatString("Failed to initialize the clFFT library, error code %d", initErrorCode)));
{
GMX_THROW(InternalError(formatString("Failed to initialize the clFFT library, error code %d", initErrorCode)));
}
+ g_clfftInitialized = true;
+#else
+ GMX_UNUSED_VALUE(g_clfftInitialized);
#endif
}
ClfftInitializer::~ClfftInitializer()
{
#if GMX_GPU == GMX_GPU_OPENCL
- clfftTeardown();
- // TODO: log non-0 return values (errors)
+ gmx::lock_guard<gmx::Mutex> guard(g_clfftMutex);
+ if (g_clfftInitialized)
+ {
+ clfftTeardown();
+ // TODO: log non-zero return values (errors)
+ }
+ g_clfftInitialized = false;
#endif
}
-std::unique_ptr<ClfftInitializer> initializeClfftLibrary()
-{
- return std::make_unique<ClfftInitializer>();
-}
-
} // namespace gmx
/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2018, by the GROMACS development team, led by
+ * Copyright (c) 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.
{
/*! \libinternal
- * \brief Handle clFFT library init and tear down in RAII style. */
-class ClfftInitializer
-{
- public:
- ClfftInitializer();
- ~ClfftInitializer();
-
- GMX_DISALLOW_COPY_AND_ASSIGN(ClfftInitializer);
-};
-
-/*! \brief This routine should be called during setup for running
- * an FFT task on an OpenCL device.
- *
- * It should be called once per process with such a task.
+ * \brief Handle clFFT library init and tear down in RAII style also
+ * with mutual exclusion.
*
- * It implements lazy initialization, so that we can have a lifetime
- * that begins when we know that PME on an OpenCL device will run an
- * FFT task on the device, and should continue until we know that the
- * last such task has completed. Any time required for this
- * initialization or tear down should not be accrued to per-MD-step
- * counters.
+ * Only one thread per process needs to attempt to set up and tear
+ * down the clFFT library, but this wrapper object ensures that it is
+ * safe to do so more than once, or from any thread. It is the
+ * responsibility of the caller to ensure that no use of the clFFT
+ * library is made before making an object of this type, or after the
+ * first such object is destroyed. If no more objects remain to be
+ * destroyed, then it is safe to create another and resume clFFT work.
*
* \todo Consider making a composite object that also handles
* on-demand compilation, managing lifetime of PME FFT kernel programs
* counter idiom so that both static initialization and
* deinitialization can work in a fast, leak-free, and thread-safe way
* without imposing constraints on the calling code.
- * See Redmine #2535.
- */
-std::unique_ptr<ClfftInitializer> initializeClfftLibrary();
+ * See Redmine #2535. */
+class ClfftInitializer
+{
+ public:
+ /*! \brief Constructor
+ *
+ * This initializers the clFFT library if there is a
+ * possibility of an FFT task on the device, and preserves it
+ * until destruction. Any time required for this
+ * initialization or tear down should not be accrued to
+ * per-MD-step counters. */
+ ClfftInitializer();
+ //! Destructor
+ ~ClfftInitializer();
+
+ GMX_DISALLOW_COPY_AND_ASSIGN(ClfftInitializer);
+};
} // namespace gmx
# Always compiled as plain C++
file(GLOB SOURCES_FROM_CXX
+ clfftinitializer.cpp
hostallocator.cpp
)
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * 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.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Tests for clFFT initialization logic
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ */
+#include "gmxpre.h"
+
+#include "gromacs/gpu_utils/clfftinitializer.h"
+
+#include <gtest/gtest.h>
+
+namespace gmx
+{
+
+namespace test
+{
+
+TEST(ClfftInitializer, SingleInitializationWorks)
+{
+ ClfftInitializer clfft;
+}
+
+TEST(ClfftInitializer, TwoInitializationsWork)
+{
+ // If the mutual exclusion was wrong, this would trigger any check
+ // that clFFT might have for double initialization.
+ ClfftInitializer first, second;
+}
+
+} // namespace test
+} // namespace gmx
#include "gromacs/fileio/tpxio.h"
#include "gromacs/gmxlib/network.h"
#include "gromacs/gmxlib/nrnb.h"
-#include "gromacs/gpu_utils/clfftinitializer.h"
#include "gromacs/gpu_utils/gpu_utils.h"
#include "gromacs/hardware/cpuinfo.h"
#include "gromacs/hardware/detecthardware.h"
}
}
- std::unique_ptr<ClfftInitializer> initializedClfftLibrary;
-
gmx_device_info_t *pmeDeviceInfo = nullptr;
// Later, this program could contain kernels that might be later
// re-used as auto-tuning progresses, or subsequent simulations
pmeDeviceInfo = getDeviceInfo(hwinfo->gpu_info, pmeGpuTaskMapping->deviceId_);
init_gpu(pmeDeviceInfo);
pmeGpuProgram = buildPmeGpuProgram(pmeDeviceInfo);
- // TODO It would be nice to move this logic into the factory
- // function. See Redmine #2535.
- bool isMasterThread = !GMX_THREAD_MPI || MASTER(cr);
- if (pmeRunMode == PmeRunMode::GPU && !initializedClfftLibrary && isMasterThread)
- {
- initializedClfftLibrary = initializeClfftLibrary();
- }
}
/* getting number of PP/PME threads on this MPI / tMPI rank.