From 764eb4c56a11c362d996b2830b349f8fb0cca749 Mon Sep 17 00:00:00 2001 From: Artem Zhmurov Date: Mon, 14 Sep 2020 09:46:19 +0000 Subject: [PATCH] Use common device context header for OpenCL This moves the OpenCL specific definition into under macro in the main header. Makes device stream and context definitions more consistent. --- src/gromacs/ewald/tests/CMakeLists.txt | 5 -- src/gromacs/ewald/tests/pmebsplinetest.cpp | 1 - src/gromacs/ewald/tests/pmegathertest.cpp | 35 +++++---- src/gromacs/ewald/tests/pmesolvetest.cpp | 35 +++++---- src/gromacs/ewald/tests/pmesplinespreadtest.cpp | 36 +++++---- src/gromacs/ewald/tests/pmetestcommon.cpp | 73 ++++++++++++++----- src/gromacs/ewald/tests/pmetestcommon.h | 75 +++++++++++++++---- src/programs/mdrun/tests/pmetest.cpp | 14 +--- src/testutils/CMakeLists.txt | 17 ++++- .../test_device.cpp} | 85 +++++++++++----------- .../test_device.h} | 66 ++++++----------- .../test_hardware_environment.cpp} | 42 +++++------ .../test_hardware_environment.h} | 28 +++---- src/testutils/testinit.cpp | 1 + src/testutils/testinit.h | 13 ---- src/testutils/unittest_main.cpp | 10 +-- 16 files changed, 303 insertions(+), 233 deletions(-) rename src/{gromacs/ewald/tests/testhardwarecontext.cpp => testutils/test_device.cpp} (57%) rename src/{gromacs/ewald/tests/testhardwarecontext.h => testutils/test_device.h} (56%) rename src/{gromacs/ewald/tests/testhardwarecontexts.cpp => testutils/test_hardware_environment.cpp} (74%) rename src/{gromacs/ewald/tests/testhardwarecontexts.h => testutils/test_hardware_environment.h} (82%) diff --git a/src/gromacs/ewald/tests/CMakeLists.txt b/src/gromacs/ewald/tests/CMakeLists.txt index 4ce31f5767..61da015d2d 100644 --- a/src/gromacs/ewald/tests/CMakeLists.txt +++ b/src/gromacs/ewald/tests/CMakeLists.txt @@ -39,9 +39,4 @@ gmx_add_unit_test(EwaldUnitTests ewald-test HARDWARE_DETECTION pmesolvetest.cpp pmesplinespreadtest.cpp pmetestcommon.cpp - testhardwarecontexts.cpp -) - -gmx_add_libgromacs_sources( - testhardwarecontext.cpp ) diff --git a/src/gromacs/ewald/tests/pmebsplinetest.cpp b/src/gromacs/ewald/tests/pmebsplinetest.cpp index f136fabe4f..b6a4e9e35f 100644 --- a/src/gromacs/ewald/tests/pmebsplinetest.cpp +++ b/src/gromacs/ewald/tests/pmebsplinetest.cpp @@ -55,7 +55,6 @@ #include "testutils/testasserts.h" #include "pmetestcommon.h" -#include "testhardwarecontexts.h" namespace gmx { diff --git a/src/gromacs/ewald/tests/pmegathertest.cpp b/src/gromacs/ewald/tests/pmegathertest.cpp index eae6448323..1190caea1d 100644 --- a/src/gromacs/ewald/tests/pmegathertest.cpp +++ b/src/gromacs/ewald/tests/pmegathertest.cpp @@ -50,10 +50,10 @@ #include "gromacs/utility/stringutil.h" #include "testutils/refdata.h" +#include "testutils/test_hardware_environment.h" #include "testutils/testasserts.h" #include "pmetestcommon.h" -#include "testhardwarecontexts.h" namespace gmx { @@ -226,7 +226,7 @@ private: public: PmeGatherTest() = default; - //! Sets the input atom data references once + //! Sets the input atom data references and programs once static void SetUpTestCase() { size_t start = 0; @@ -254,7 +254,9 @@ public: } s_inputAtomDataSets_[atomCount] = atomData; } + s_pmeTestHardwareContexts = createPmeTestHardwareContextList(); } + //! The test void runTest() { @@ -278,11 +280,12 @@ public: inputRec.epsilon_r = 1.0; TestReferenceData refData; - for (const auto& context : getPmeTestEnv()->getHardwareContexts()) + for (const auto& pmeTestHardwareContext : s_pmeTestHardwareContexts) { - CodePath codePath = context->codePath(); - const bool supportedInput = - pmeSupportsInputForMode(*getPmeTestEnv()->hwinfo(), &inputRec, codePath); + pmeTestHardwareContext->activate(); + CodePath codePath = pmeTestHardwareContext->codePath(); + const bool supportedInput = pmeSupportsInputForMode( + *getTestHardwareEnvironment()->hwinfo(), &inputRec, codePath); if (!supportedInput) { /* Testing the failure for the unsupported input */ @@ -293,18 +296,20 @@ public: /* Describing the test uniquely */ SCOPED_TRACE( - formatString("Testing force gathering with %s %sfor PME grid size %d %d %d" + formatString("Testing force gathering on %s for PME grid size %d %d %d" ", order %d, %zu atoms", - codePathToString(codePath), context->description().c_str(), - gridSize[XX], gridSize[YY], gridSize[ZZ], pmeOrder, atomCount)); + pmeTestHardwareContext->description().c_str(), gridSize[XX], + gridSize[YY], gridSize[ZZ], pmeOrder, atomCount)); PmeSafePointer pmeSafe = - pmeInitWrapper(&inputRec, codePath, context->deviceContext(), - context->deviceStream(), context->pmeGpuProgram(), box); + pmeInitWrapper(&inputRec, codePath, pmeTestHardwareContext->deviceContext(), + pmeTestHardwareContext->deviceStream(), + pmeTestHardwareContext->pmeGpuProgram(), box); std::unique_ptr stateGpu = (codePath == CodePath::GPU) - ? makeStatePropagatorDataGpu(*pmeSafe.get(), context->deviceContext(), - context->deviceStream()) + ? makeStatePropagatorDataGpu(*pmeSafe.get(), + pmeTestHardwareContext->deviceContext(), + pmeTestHardwareContext->deviceStream()) : nullptr; pmeInitAtoms(pmeSafe.get(), stateGpu.get(), codePath, inputAtomData.coordinates, @@ -337,8 +342,12 @@ public: forceChecker.checkSequence(forces.begin(), forces.end(), "Forces"); } } + + static std::vector> s_pmeTestHardwareContexts; }; +std::vector> PmeGatherTest::s_pmeTestHardwareContexts; + // An instance of static atom data InputDataByAtomCount PmeGatherTest::s_inputAtomDataSets_; diff --git a/src/gromacs/ewald/tests/pmesolvetest.cpp b/src/gromacs/ewald/tests/pmesolvetest.cpp index 07f31629f6..578fa8caa7 100644 --- a/src/gromacs/ewald/tests/pmesolvetest.cpp +++ b/src/gromacs/ewald/tests/pmesolvetest.cpp @@ -50,10 +50,10 @@ #include "gromacs/utility/stringutil.h" #include "testutils/refdata.h" +#include "testutils/test_hardware_environment.h" #include "testutils/testasserts.h" #include "pmetestcommon.h" -#include "testhardwarecontexts.h" namespace gmx { @@ -75,6 +75,9 @@ class PmeSolveTest : public ::testing::TestWithParam public: PmeSolveTest() = default; + //! Sets the programs once + static void SetUpTestCase() { s_pmeTestHardwareContexts = createPmeTestHardwareContextList(); } + //! The test void runTest() { @@ -107,16 +110,17 @@ public: } TestReferenceData refData; - for (const auto& context : getPmeTestEnv()->getHardwareContexts()) + for (const auto& pmeTestHardwareContext : s_pmeTestHardwareContexts) { - CodePath codePath = context->codePath(); - const bool supportedInput = - pmeSupportsInputForMode(*getPmeTestEnv()->hwinfo(), &inputRec, codePath); + pmeTestHardwareContext->activate(); + CodePath codePath = pmeTestHardwareContext->codePath(); + const bool supportedInput = pmeSupportsInputForMode( + *getTestHardwareEnvironment()->hwinfo(), &inputRec, codePath); if (!supportedInput) { /* Testing the failure for the unsupported input */ - EXPECT_THROW_GMX(pmeInitEmpty(&inputRec, codePath, nullptr, nullptr, nullptr, box, - ewaldCoeff_q, ewaldCoeff_lj), + EXPECT_THROW_GMX(pmeInitWrapper(&inputRec, codePath, nullptr, nullptr, nullptr, box, + ewaldCoeff_q, ewaldCoeff_lj), NotImplementedError); continue; } @@ -133,17 +137,18 @@ public: { /* Describing the test*/ SCOPED_TRACE(formatString( - "Testing solving (%s, %s, %s energy/virial) with %s %sfor PME grid " + "Testing solving (%s, %s, %s energy/virial) on %s for PME grid " "size %d %d %d, Ewald coefficients %g %g", (method == PmeSolveAlgorithm::LennardJones) ? "Lennard-Jones" : "Coulomb", gridOrdering.second.c_str(), computeEnergyAndVirial ? "with" : "without", - codePathToString(codePath), context->description().c_str(), - gridSize[XX], gridSize[YY], gridSize[ZZ], ewaldCoeff_q, ewaldCoeff_lj)); + pmeTestHardwareContext->description().c_str(), gridSize[XX], + gridSize[YY], gridSize[ZZ], ewaldCoeff_q, ewaldCoeff_lj)); /* Running the test */ - PmeSafePointer pmeSafe = pmeInitEmpty( - &inputRec, codePath, context->deviceContext(), context->deviceStream(), - context->pmeGpuProgram(), box, ewaldCoeff_q, ewaldCoeff_lj); + PmeSafePointer pmeSafe = pmeInitWrapper( + &inputRec, codePath, pmeTestHardwareContext->deviceContext(), + pmeTestHardwareContext->deviceStream(), + pmeTestHardwareContext->pmeGpuProgram(), box, ewaldCoeff_q, ewaldCoeff_lj); pmeSetComplexGrid(pmeSafe.get(), codePath, gridOrdering.first, nonZeroGridValues); const real cellVolume = box[0] * box[4] * box[8]; // FIXME - this is box[XX][XX] * box[YY][YY] * box[ZZ][ZZ], should be stored in the PME structure @@ -244,8 +249,12 @@ public: } } } + + static std::vector> s_pmeTestHardwareContexts; }; +std::vector> PmeSolveTest::s_pmeTestHardwareContexts; + /*! \brief Test for PME solving */ TEST_P(PmeSolveTest, ReproducesOutputs) { diff --git a/src/gromacs/ewald/tests/pmesplinespreadtest.cpp b/src/gromacs/ewald/tests/pmesplinespreadtest.cpp index ef975b2d08..4765acbddb 100644 --- a/src/gromacs/ewald/tests/pmesplinespreadtest.cpp +++ b/src/gromacs/ewald/tests/pmesplinespreadtest.cpp @@ -50,10 +50,10 @@ #include "gromacs/utility/stringutil.h" #include "testutils/refdata.h" +#include "testutils/test_hardware_environment.h" #include "testutils/testasserts.h" #include "pmetestcommon.h" -#include "testhardwarecontexts.h" namespace gmx { @@ -83,6 +83,10 @@ class PmeSplineAndSpreadTest : public ::testing::TestWithParam optionsToTest = { { PmeSplineAndSpreadOptions::SplineAndSpreadUnified, "spline computation and charge spreading (fused)" }, @@ -124,11 +126,13 @@ public: bool gridValuesSizeAssigned = false; size_t previousGridValuesSize; - for (const auto& context : getPmeTestEnv()->getHardwareContexts()) + TestReferenceData refData; + for (const auto& pmeTestHardwareContext : s_pmeTestHardwareContexts) { - CodePath codePath = context->codePath(); - const bool supportedInput = - pmeSupportsInputForMode(*getPmeTestEnv()->hwinfo(), &inputRec, codePath); + pmeTestHardwareContext->activate(); + CodePath codePath = pmeTestHardwareContext->codePath(); + const bool supportedInput = pmeSupportsInputForMode( + *getTestHardwareEnvironment()->hwinfo(), &inputRec, codePath); if (!supportedInput) { /* Testing the failure for the unsupported input */ @@ -142,20 +146,22 @@ public: /* Describing the test uniquely in case it fails */ SCOPED_TRACE(formatString( - "Testing %s with %s %sfor PME grid size %d %d %d" + "Testing %s on %s for PME grid size %d %d %d" ", order %d, %zu atoms", - option.second.c_str(), codePathToString(codePath), context->description().c_str(), + option.second.c_str(), pmeTestHardwareContext->description().c_str(), gridSize[XX], gridSize[YY], gridSize[ZZ], pmeOrder, atomCount)); /* Running the test */ PmeSafePointer pmeSafe = - pmeInitWrapper(&inputRec, codePath, context->deviceContext(), - context->deviceStream(), context->pmeGpuProgram(), box); + pmeInitWrapper(&inputRec, codePath, pmeTestHardwareContext->deviceContext(), + pmeTestHardwareContext->deviceStream(), + pmeTestHardwareContext->pmeGpuProgram(), box); std::unique_ptr stateGpu = (codePath == CodePath::GPU) - ? makeStatePropagatorDataGpu(*pmeSafe.get(), context->deviceContext(), - context->deviceStream()) + ? makeStatePropagatorDataGpu(*pmeSafe.get(), + pmeTestHardwareContext->deviceContext(), + pmeTestHardwareContext->deviceStream()) : nullptr; pmeInitAtoms(pmeSafe.get(), stateGpu.get(), codePath, coordinates, charges); @@ -262,8 +268,12 @@ public: } } } + + static std::vector> s_pmeTestHardwareContexts; }; +std::vector> PmeSplineAndSpreadTest::s_pmeTestHardwareContexts; + /*! \brief Test for spline parameter computation and charge spreading. */ TEST_P(PmeSplineAndSpreadTest, ReproducesOutputs) diff --git a/src/gromacs/ewald/tests/pmetestcommon.cpp b/src/gromacs/ewald/tests/pmetestcommon.cpp index 2e28fb4aa2..c3f5bd2514 100644 --- a/src/gromacs/ewald/tests/pmetestcommon.cpp +++ b/src/gromacs/ewald/tests/pmetestcommon.cpp @@ -59,7 +59,7 @@ #include "gromacs/ewald/pme_solve.h" #include "gromacs/ewald/pme_spread.h" #include "gromacs/fft/parallel_3dfft.h" -#include "gromacs/gpu_utils/device_stream_manager.h" +#include "gromacs/gpu_utils/device_context.h" #include "gromacs/gpu_utils/gpu_utils.h" #include "gromacs/math/invertmatrix.h" #include "gromacs/mdtypes/commrec.h" @@ -70,10 +70,9 @@ #include "gromacs/utility/logger.h" #include "gromacs/utility/stringutil.h" +#include "testutils/test_hardware_environment.h" #include "testutils/testasserts.h" -#include "testhardwarecontexts.h" - namespace gmx { namespace test @@ -151,21 +150,6 @@ PmeSafePointer pmeInitWrapper(const t_inputrec* inputRec, return pme; } -//! Simple PME initialization based on input, no atom data -PmeSafePointer pmeInitEmpty(const t_inputrec* inputRec, - const CodePath mode, - const DeviceContext* deviceContext, - const DeviceStream* deviceStream, - const PmeGpuProgram* pmeGpuProgram, - const Matrix3x3& box, - const real ewaldCoeff_q, - const real ewaldCoeff_lj) -{ - return pmeInitWrapper(inputRec, mode, deviceContext, deviceStream, pmeGpuProgram, box, - ewaldCoeff_q, ewaldCoeff_lj); - // hiding the fact that PME actually needs to know the number of atoms in advance -} - PmeSafePointer pmeInitEmpty(const t_inputrec* inputRec) { const Matrix3x3 defaultBox = { { 1.0F, 0.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 0.0F, 1.0F } }; @@ -889,5 +873,58 @@ PmeOutput pmeGetReciprocalEnergyAndVirial(const gmx_pme_t* pme, CodePath mode, P return output; } +const char* codePathToString(CodePath codePath) +{ + switch (codePath) + { + case CodePath::CPU: return "CPU"; + case CodePath::GPU: return "GPU"; + default: GMX_THROW(NotImplementedError("This CodePath should support codePathToString")); + } +} + +PmeTestHardwareContext::PmeTestHardwareContext() : codePath_(CodePath::CPU) {} + +PmeTestHardwareContext::PmeTestHardwareContext(TestDevice* testDevice) : + codePath_(CodePath::CPU), + testDevice_(testDevice) +{ + setActiveDevice(testDevice_->deviceInfo()); + pmeGpuProgram_ = buildPmeGpuProgram(testDevice_->deviceContext()); +} + +//! Returns a human-readable context description line +std::string PmeTestHardwareContext::description() const +{ + switch (codePath_) + { + case CodePath::CPU: return "CPU"; + case CodePath::GPU: return "GPU (" + testDevice_->description() + ")"; + default: return "Unknown code path."; + } +} + +void PmeTestHardwareContext::activate() const +{ + if (codePath_ == CodePath::GPU) + { + setActiveDevice(testDevice_->deviceInfo()); + } +} + +std::vector> createPmeTestHardwareContextList() +{ + std::vector> pmeTestHardwareContextList; + // Add CPU + pmeTestHardwareContextList.emplace_back(std::make_unique()); + // Add GPU devices + const auto& testDeviceList = getTestHardwareEnvironment()->getTestDeviceList(); + for (const auto& testDevice : testDeviceList) + { + pmeTestHardwareContextList.emplace_back(std::make_unique(testDevice.get())); + } + return pmeTestHardwareContextList; +} + } // namespace test } // namespace gmx diff --git a/src/gromacs/ewald/tests/pmetestcommon.h b/src/gromacs/ewald/tests/pmetestcommon.h index 8e6e135409..6b1e6d136c 100644 --- a/src/gromacs/ewald/tests/pmetestcommon.h +++ b/src/gromacs/ewald/tests/pmetestcommon.h @@ -52,18 +52,30 @@ #include "gromacs/mdtypes/state_propagator_data_gpu.h" #include "gromacs/utility/unique_cptr.h" +#include "testutils/test_device.h" + namespace gmx { -class DeviceStreamManager; template class ArrayRef; namespace test { -// Forward declaration -enum class CodePath; +//! Hardware code path being tested +enum class CodePath : int +{ + //! CPU code path + CPU, + //! GPU code path + GPU, + //! Total number of code paths + Count +}; + +//! Return a string useful for human-readable messages describing a \c codePath. +const char* codePathToString(CodePath codePath); // Convenience typedefs //! A safe pointer type for PME. @@ -101,10 +113,14 @@ typedef SparseGridValuesOutput SparseComplexGridValuesOutput; //! TODO: make proper C++ matrix for the whole Gromacs, get rid of this typedef std::array Matrix3x3; //! PME solver type -enum class PmeSolveAlgorithm +enum class PmeSolveAlgorithm : int { + //! Coulomb electrostatics Coulomb, + //! Lennard-Jones LennardJones, + //! Total number of solvers + Count }; // Misc. @@ -130,15 +146,6 @@ PmeSafePointer pmeInitWrapper(const t_inputrec* inputRec, const Matrix3x3& box, real ewaldCoeff_q = 1.0F, real ewaldCoeff_lj = 1.0F); -//! Simple PME initialization (no atom data) -PmeSafePointer pmeInitEmpty(const t_inputrec* inputRec, - CodePath mode, - const DeviceContext* deviceContext, - const DeviceStream* deviceStream, - const PmeGpuProgram* pmeGpuProgram, - const Matrix3x3& box, - real ewaldCoeff_q, - real ewaldCoeff_lj); //! Simple PME initialization based on inputrec only PmeSafePointer pmeInitEmpty(const t_inputrec* inputRec); @@ -199,7 +206,47 @@ SparseRealGridValuesOutput pmeGetRealGrid(const gmx_pme_t* pme, CodePath mode); SparseComplexGridValuesOutput pmeGetComplexGrid(const gmx_pme_t* pme, CodePath mode, GridOrdering gridOrdering); //! Getting the reciprocal energy and virial PmeOutput pmeGetReciprocalEnergyAndVirial(const gmx_pme_t* pme, CodePath mode, PmeSolveAlgorithm method); + +struct PmeTestHardwareContext +{ + //! Hardware path for the code being tested. + CodePath codePath_; + //! Returns a human-readable context description line + std::string description() const; + //! Pointer to the global test hardware device (if on GPU) + TestDevice* testDevice_ = nullptr; + //! PME GPU program if needed + PmeGpuProgramStorage pmeGpuProgram_ = nullptr; + // Constructor for CPU context + PmeTestHardwareContext(); + // Constructor for GPU context + explicit PmeTestHardwareContext(TestDevice* testDevice); + + //! Get the code path + CodePath codePath() const { return codePath_; } + //! Get the PME GPU program + const PmeGpuProgram* pmeGpuProgram() const + { + return codePath() == CodePath::GPU ? pmeGpuProgram_.get() : nullptr; + } + + const DeviceContext* deviceContext() const + { + return codePath() == CodePath::GPU ? &testDevice_->deviceContext() : nullptr; + } + + const DeviceStream* deviceStream() const + { + return codePath() == CodePath::GPU ? &testDevice_->deviceStream() : nullptr; + } + + //! Activate the context (set the device) + void activate() const; +}; + +std::vector> createPmeTestHardwareContextList(); + } // namespace test } // namespace gmx -#endif +#endif // GMX_EWALD_PME_TEST_COMMON_H diff --git a/src/programs/mdrun/tests/pmetest.cpp b/src/programs/mdrun/tests/pmetest.cpp index fbc0e231cf..4aa1219cca 100644 --- a/src/programs/mdrun/tests/pmetest.cpp +++ b/src/programs/mdrun/tests/pmetest.cpp @@ -57,6 +57,7 @@ #include "gromacs/ewald/pme.h" #include "gromacs/hardware/detecthardware.h" #include "gromacs/hardware/device_management.h" +#include "gromacs/hardware/hw_info.h" #include "gromacs/trajectory/energyframe.h" #include "gromacs/utility/cstringutil.h" #include "gromacs/utility/gmxmpi.h" @@ -83,23 +84,12 @@ namespace class PmeTest : public MdrunTestFixture { public: - //! Before any test is run, work out whether any compatible GPUs exist. - static void SetUpTestCase(); - //! Store whether any compatible GPUs exist. - static bool s_hasCompatibleGpus; //! Convenience typedef using RunModesList = std::map>; //! Runs the test with the given inputs void runTest(const RunModesList& runModes); }; -bool PmeTest::s_hasCompatibleGpus = false; - -void PmeTest::SetUpTestCase() -{ - s_hasCompatibleGpus = canComputeOnDevice(); -} - void PmeTest::runTest(const RunModesList& runModes) { const std::string inputFile = "spc-and-methanol"; @@ -127,7 +117,7 @@ void PmeTest::runTest(const RunModesList& runModes) { SCOPED_TRACE("mdrun " + joinStrings(mode.second, " ")); auto modeTargetsGpus = (mode.first.find("Gpu") != std::string::npos); - if (modeTargetsGpus && !s_hasCompatibleGpus) + if (modeTargetsGpus && getCompatibleDevices(hardwareInfo_->deviceInfoList).empty()) { // This run mode will cause a fatal error from mdrun when // it can't find GPUs, which is not something we're trying diff --git a/src/testutils/CMakeLists.txt b/src/testutils/CMakeLists.txt index 6ac9aacbe4..1928150068 100644 --- a/src/testutils/CMakeLists.txt +++ b/src/testutils/CMakeLists.txt @@ -54,6 +54,8 @@ set(TESTUTILS_SOURCES testasserts.cpp testfilemanager.cpp testfileredirector.cpp + test_device.cpp + test_hardware_environment.cpp testinit.cpp testmatchers.cpp testoptions.cpp @@ -66,7 +68,20 @@ if(NOT HAVE_TINYXML2) list(APPEND TESTUTILS_SOURCES ../external/tinyxml2/tinyxml2.cpp) endif() -add_library(testutils STATIC ${UNITTEST_TARGET_OPTIONS} ${TESTUTILS_SOURCES}) +if (GMX_GPU_CUDA) + # Work around FindCUDA that prevents using target_link_libraries() + # with keywords otherwise... + set(CUDA_LIBRARIES PRIVATE ${CUDA_LIBRARIES}) + if (NOT GMX_CLANG_CUDA) + gmx_cuda_add_library(testutils ${TESTUTILS_SOURCES}) + else() + add_library(testutils STATIC ${TESTUTILS_SOURCES}) + endif() + target_link_libraries(testutils PRIVATE ${CUDA_CUFFT_LIBRARIES}) +else() + add_library(testutils STATIC ${UNITTEST_TARGET_OPTIONS} ${TESTUTILS_SOURCES}) +endif() + gmx_target_compile_options(testutils) target_compile_definitions(testutils PRIVATE HAVE_CONFIG_H) target_include_directories(testutils SYSTEM BEFORE PRIVATE ${PROJECT_SOURCE_DIR}/src/external/thread_mpi/include) diff --git a/src/gromacs/ewald/tests/testhardwarecontext.cpp b/src/testutils/test_device.cpp similarity index 57% rename from src/gromacs/ewald/tests/testhardwarecontext.cpp rename to src/testutils/test_device.cpp index 6e2c455efa..8d3865eee0 100644 --- a/src/gromacs/ewald/tests/testhardwarecontext.cpp +++ b/src/testutils/test_device.cpp @@ -39,19 +39,18 @@ * \author Aleksei Iupinov * \author Artem Zhmurov * - * \ingroup module_ewald + * \ingroup module_testutils */ - #include "gmxpre.h" -#include "testhardwarecontext.h" +#include "test_device.h" #include -#include "gromacs/ewald/pme.h" #include "gromacs/gpu_utils/device_context.h" #include "gromacs/gpu_utils/device_stream.h" #include "gromacs/gpu_utils/gpu_utils.h" +#include "gromacs/gpu_utils/gputraits.h" #include "gromacs/hardware/detecthardware.h" #include "gromacs/hardware/hw_info.h" #include "gromacs/utility/basenetwork.h" @@ -64,60 +63,64 @@ namespace gmx namespace test { -TestHardwareContext::TestHardwareContext(CodePath codePath, const char* description) : - codePath_(codePath), - description_(description) +class TestDevice::Impl { - GMX_RELEASE_ASSERT(codePath == CodePath::CPU, - "A GPU code path should provide DeviceInformation to the " - "TestHerdwareContext constructor."); - deviceContext_ = nullptr; - deviceStream_ = nullptr; -} +public: + Impl(const char* description); + Impl(const char* description, const DeviceInformation& deviceInfo); + ~Impl(); + //! Returns a human-readable context description line + std::string description() const { return description_; } + //! Returns the device info pointer + const DeviceInformation& deviceInfo() const { return deviceContext_.deviceInfo(); } + //! Get the device context + const DeviceContext& deviceContext() const { return deviceContext_; } + //! Get the device stream + const DeviceStream& deviceStream() const { return deviceStream_; } + +private: + //! Readable description + std::string description_; + //! Device context + DeviceContext deviceContext_; + //! Device stream + DeviceStream deviceStream_; +}; -TestHardwareContext::TestHardwareContext(CodePath codePath, - const char* description, - const DeviceInformation& deviceInfo) : - codePath_(codePath), - description_(description) +TestDevice::Impl::Impl(const char* description, const DeviceInformation& deviceInfo) : + description_(description), + deviceContext_(deviceInfo), + deviceStream_(deviceContext_, DeviceStreamPriority::Normal, false) { - GMX_RELEASE_ASSERT(codePath == CodePath::GPU, - "TestHardwareContext tries to construct DeviceContext and PmeGpuProgram " - "in CPU build."); - deviceContext_ = new DeviceContext(deviceInfo); - deviceStream_ = new DeviceStream(*deviceContext_, DeviceStreamPriority::Normal, false); - program_ = buildPmeGpuProgram(*deviceContext_); } -TestHardwareContext::~TestHardwareContext() +TestDevice::Impl::~Impl() = default; + +TestDevice::TestDevice(const char* description, const DeviceInformation& deviceInfo) : + impl_(new Impl(description, deviceInfo)) { - delete (deviceStream_); - delete (deviceContext_); } -const DeviceInformation* TestHardwareContext::deviceInfo() const +TestDevice::~TestDevice() = default; + +std::string TestDevice::description() const { - return &deviceContext_->deviceInfo(); + return impl_->description(); } -const DeviceContext* TestHardwareContext::deviceContext() const +const DeviceInformation& TestDevice::deviceInfo() const { - return deviceContext_; + return impl_->deviceInfo(); } -//! Get the device stream -const DeviceStream* TestHardwareContext::deviceStream() const + +const DeviceContext& TestDevice::deviceContext() const { - return deviceStream_; + return impl_->deviceContext(); } -const char* codePathToString(CodePath codePath) +const DeviceStream& TestDevice::deviceStream() const { - switch (codePath) - { - case CodePath::CPU: return "CPU"; - case CodePath::GPU: return "GPU"; - default: GMX_THROW(NotImplementedError("This CodePath should support codePathToString")); - } + return impl_->deviceStream(); } } // namespace test diff --git a/src/gromacs/ewald/tests/testhardwarecontext.h b/src/testutils/test_device.h similarity index 56% rename from src/gromacs/ewald/tests/testhardwarecontext.h rename to src/testutils/test_device.h index fa5ebd9da8..3fa7915fcf 100644 --- a/src/gromacs/ewald/tests/testhardwarecontext.h +++ b/src/testutils/test_device.h @@ -32,23 +32,24 @@ * To help us fund GROMACS development, we humbly ask that you cite * the research papers on the package. Check out http://www.gromacs.org. */ -#ifndef GMX_EWALD_TEST_HARDWARE_CONTEXT_H -#define GMX_EWALD_TEST_HARDWARE_CONTEXT_H +#ifndef GMX_TESTUTILS_TEST_DEVICE_H +#define GMX_TESTUTILS_TEST_DEVICE_H /*! \internal \file * \brief - * Describes test environment class which performs hardware enumeration for unit tests. + * Describes test environment class which performs GPU device enumeration for unit tests. * * \author Aleksei Iupinov * \author Artem Zhmurov - * \ingroup module_ewald + * + * \ingroup module_testutils */ #include #include #include -#include "gromacs/ewald/pme_gpu_program.h" +#include "gromacs/utility/classhelpers.h" #include "gromacs/utility/gmxassert.h" class DeviceContext; @@ -59,54 +60,35 @@ namespace gmx { namespace test { -//! Hardware code path being tested -enum class CodePath -{ - CPU, - GPU -}; - -//! Return a string useful for human-readable messages describing a \c codePath. -const char* codePathToString(CodePath codePath); /*! \internal \brief - * A structure to describe a hardware context that persists over the lifetime - * of the test binary - an abstraction over PmeGpuProgram with a human-readable string. + * A structure to describe a hardware context that persists over the lifetime + * of the test binary. */ -struct TestHardwareContext +class TestDevice { - //! Hardware path for the code being tested. - CodePath codePath_; - //! Readable description - std::string description_; - //! Device context - DeviceContext* deviceContext_ = nullptr; - //! Device stream - DeviceStream* deviceStream_ = nullptr; - //! Persistent compiled GPU kernels for PME. - PmeGpuProgramStorage program_; - public: - //! Retuns the code path for this context. - CodePath codePath() const { return codePath_; } //! Returns a human-readable context description line - std::string description() const { return description_; } + std::string description() const; //! Returns the device info pointer - const DeviceInformation* deviceInfo() const; + const DeviceInformation& deviceInfo() const; //! Get the device context - const DeviceContext* deviceContext() const; + const DeviceContext& deviceContext() const; //! Get the device stream - const DeviceStream* deviceStream() const; - //! Returns the persistent PME GPU kernels - const PmeGpuProgram* pmeGpuProgram() const { return program_.get(); } - //! Constructs the context for CPU builds - TestHardwareContext(CodePath codePath, const char* description); - //! Constructs the context for GPU builds - TestHardwareContext(CodePath codePath, const char* description, const DeviceInformation& deviceInfo); + const DeviceStream& deviceStream() const; + //! Creates the device context and stream for tests on the GPU + TestDevice(const char* description, const DeviceInformation& deviceInfo); //! Destructor - ~TestHardwareContext(); + ~TestDevice(); + +private: + //! Implementation type. + class Impl; + //! Implementation object. + PrivateImplPointer impl_; }; } // namespace test } // namespace gmx -#endif + +#endif // GMX_TESTUTILS_TEST_DEVICE_H diff --git a/src/gromacs/ewald/tests/testhardwarecontexts.cpp b/src/testutils/test_hardware_environment.cpp similarity index 74% rename from src/gromacs/ewald/tests/testhardwarecontexts.cpp rename to src/testutils/test_hardware_environment.cpp index 5b7cb05327..4164527f9d 100644 --- a/src/gromacs/ewald/tests/testhardwarecontexts.cpp +++ b/src/testutils/test_hardware_environment.cpp @@ -37,16 +37,18 @@ * Implements test environment class which performs hardware enumeration for unit tests. * * \author Aleksei Iupinov - * \ingroup module_ewald + * \author Artem Zhmurov + * + * \ingroup module_testutils */ #include "gmxpre.h" -#include "testhardwarecontexts.h" +#include "test_hardware_environment.h" #include -#include "gromacs/ewald/pme.h" +#include "gromacs/gpu_utils/gpu_utils.h" #include "gromacs/hardware/detecthardware.h" #include "gromacs/hardware/device_management.h" #include "gromacs/hardware/hw_info.h" @@ -70,21 +72,21 @@ namespace test * so there is no deinitialization issue. See * https://isocpp.org/wiki/faq/ctors for discussion of alternatives * and trade-offs. */ -const PmeTestEnvironment* getPmeTestEnv() +const TestHardwareEnvironment* getTestHardwareEnvironment() { - static PmeTestEnvironment* pmeTestEnvironment = nullptr; - if (pmeTestEnvironment == nullptr) + static TestHardwareEnvironment* testHardwareEnvironment = nullptr; + if (testHardwareEnvironment == nullptr) { // Ownership of the TestEnvironment is taken by GoogleTest, so nothing can leak - pmeTestEnvironment = static_cast( - ::testing::AddGlobalTestEnvironment(new PmeTestEnvironment)); + testHardwareEnvironment = static_cast( + ::testing::AddGlobalTestEnvironment(new TestHardwareEnvironment)); } - return pmeTestEnvironment; + return testHardwareEnvironment; } void callAddGlobalTestEnvironment() { - getPmeTestEnv(); + getTestHardwareEnvironment(); } //! Simple hardware initialization @@ -94,30 +96,22 @@ static gmx_hw_info_t* hardwareInit() return gmx_detect_hardware(MDLogger{}, physicalNodeComm); } -void PmeTestEnvironment::SetUp() +void TestHardwareEnvironment::SetUp() { - hardwareContexts_.emplace_back(std::make_unique(CodePath::CPU, "(CPU) ")); - + testDeviceList_.clear(); hardwareInfo_ = hardwareInit(); - if (!pme_gpu_supports_build(nullptr) || !pme_gpu_supports_hardware(*hardwareInfo_, nullptr)) - { - // PME can only run on the CPU, so don't make any more test contexts. - return; - } // Constructing contexts for all compatible GPUs - will be empty on non-GPU builds for (const DeviceInformation& compatibleDeviceInfo : getCompatibleDevices(hardwareInfo_->deviceInfoList)) { setActiveDevice(compatibleDeviceInfo); - std::string deviceDescription = getDeviceInformationString(compatibleDeviceInfo); - std::string description = "(GPU " + deviceDescription + ") "; - hardwareContexts_.emplace_back(std::make_unique( - CodePath::GPU, description.c_str(), compatibleDeviceInfo)); + std::string description = getDeviceInformationString(compatibleDeviceInfo); + testDeviceList_.emplace_back(std::make_unique(description.c_str(), compatibleDeviceInfo)); } } -void PmeTestEnvironment::TearDown() +void TestHardwareEnvironment::TearDown() { - hardwareContexts_.clear(); + testDeviceList_.clear(); } } // namespace test diff --git a/src/gromacs/ewald/tests/testhardwarecontexts.h b/src/testutils/test_hardware_environment.h similarity index 82% rename from src/gromacs/ewald/tests/testhardwarecontexts.h rename to src/testutils/test_hardware_environment.h index 42d7245a8e..d7b5292259 100644 --- a/src/gromacs/ewald/tests/testhardwarecontexts.h +++ b/src/testutils/test_hardware_environment.h @@ -32,15 +32,17 @@ * To help us fund GROMACS development, we humbly ask that you cite * the research papers on the package. Check out http://www.gromacs.org. */ -#ifndef GMX_EWALD_TEST_HARDWARE_CONTEXTS_H -#define GMX_EWALD_TEST_HARDWARE_CONTEXTS_H +#ifndef GMX_TESTUTILS_TEST_HARDWARE_ENVIRONMENT_H +#define GMX_TESTUTILS_TEST_HARDWARE_ENVIRONMENT_H /*! \internal \file * \brief * Describes test environment class which performs hardware enumeration for unit tests. * * \author Aleksei Iupinov - * \ingroup module_ewald + * \author Artem Zhmurov + * + * \ingroup module_testutils */ #include @@ -48,11 +50,9 @@ #include -#include "gromacs/ewald/pme_gpu_program.h" -#include "gromacs/hardware/device_management.h" #include "gromacs/utility/gmxassert.h" -#include "testhardwarecontext.h" +#include "testutils/test_device.h" struct gmx_hw_info_t; @@ -61,19 +61,16 @@ namespace gmx namespace test { -//! A container of handles to hardware contexts -typedef std::vector> TestHardwareContexts; - /*! \internal \brief * This class performs one-time test initialization (enumerating the hardware) */ -class PmeTestEnvironment : public ::testing::Environment +class TestHardwareEnvironment : public ::testing::Environment { private: //! General hardware info gmx_hw_info_t* hardwareInfo_; //! Storage of hardware contexts - TestHardwareContexts hardwareContexts_; + std::vector> testDeviceList_; public: //! This is called by GTest framework once to query the hardware @@ -81,13 +78,16 @@ public: //! This is called by GTest framework once release the hardware void TearDown() override; //! Get available hardware contexts. - const TestHardwareContexts& getHardwareContexts() const { return hardwareContexts_; } + const std::vector>& getTestDeviceList() const + { + return testDeviceList_; + } //! Get available hardware information. const gmx_hw_info_t* hwinfo() const { return hardwareInfo_; } }; //! Get the test environment -const PmeTestEnvironment* getPmeTestEnv(); +const TestHardwareEnvironment* getTestHardwareEnvironment(); /*! \brief This constructs the test environment during setup of the * unit test so that they can use the hardware context. */ @@ -95,4 +95,4 @@ void callAddGlobalTestEnvironment(); } // namespace test } // namespace gmx -#endif +#endif // GMX_TESTUTILS_TEST_HARDWARE_ENVIRONMENT_H diff --git a/src/testutils/testinit.cpp b/src/testutils/testinit.cpp index 0e16df6fdf..753b814a0f 100644 --- a/src/testutils/testinit.cpp +++ b/src/testutils/testinit.cpp @@ -70,6 +70,7 @@ #include "testutils/mpi_printer.h" #include "testutils/refdata.h" +#include "testutils/test_hardware_environment.h" #include "testutils/testfilemanager.h" #include "testutils/testoptions.h" diff --git a/src/testutils/testinit.h b/src/testutils/testinit.h index c1bd19d86e..4f7e3b40aa 100644 --- a/src/testutils/testinit.h +++ b/src/testutils/testinit.h @@ -89,19 +89,6 @@ void initTestUtils(const char* dataPath, void finalizeTestUtils(); //! \endcond -/*! \brief Declare a function that all unit test implementations can use - * to set up any environment that they need. - * - * When registering the unit test in CMake, the HARDWARE_DETECTION - * flag requires that the code for that unit test implements this - * function. Otherwise, a default stub implementation is provided. - * - * This approach conforms to the recommendation by GoogleTest to - * arrange for the code that sets up the global test environment to be - * called from main, rather than potentially rely on brittle static - * initialization order. */ -void callAddGlobalTestEnvironment(); - } // namespace test } // namespace gmx diff --git a/src/testutils/unittest_main.cpp b/src/testutils/unittest_main.cpp index 1faf7979dd..e5bb19f76a 100644 --- a/src/testutils/unittest_main.cpp +++ b/src/testutils/unittest_main.cpp @@ -2,7 +2,7 @@ * This file is part of the GROMACS molecular simulation package. * * Copyright (c) 2010-2017, The GROMACS development team. - * Copyright (c) 2019, by the GROMACS development team, led by + * Copyright (c) 2019,2020, 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. @@ -64,14 +64,6 @@ #ifndef TEST_USES_HARDWARE_DETECTION //! Whether the test expects/supports running with knowledge of the hardware. # define TEST_USES_HARDWARE_DETECTION false -namespace gmx -{ -namespace test -{ -//! Implement a stub definition for tests that don't ask for a real one. -void callAddGlobalTestEnvironment(){}; -} // namespace test -} // namespace gmx #endif /*! \brief -- 2.11.4.GIT