Generalize constraints on MPI rank counts for tests
[alexxy/gromacs.git] / src / gromacs / mdrunutility / tests / threadaffinity_mpi.cpp
index 9cd90ccd1e549ed281d59eac3bf2c76b54f1687a..1800a34d219a042602ba3a909f47e19606ef1bd7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2019,2020,2021, 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 "threadaffinitytest.h"
 
+namespace gmx
+{
+namespace test
+{
 namespace
 {
 
 using gmx::test::ThreadAffinityTestHelper;
 
+//! Helper to establish test requirements on MPI ranks
+class RequireEvenRankCountWithAtLeastFourRanks
+{
+public:
+    //! Function to require even ranks with at least four ranks
+    static bool conditionSatisfied(const int numRanks)
+    {
+        return (numRanks > 2) && (numRanks % 2 == 0);
+    }
+    //! Text to echo when skipping a test that does not satisfy the requirement
+    inline static const char* s_skipReason = "an even rank count of at least four is required";
+};
+
 TEST(ThreadAffinityMultiRankTest, PinsWholeNode)
 {
-    GMX_MPI_TEST(4);
+    GMX_MPI_TEST(AllowAnyRankCount);
     ThreadAffinityTestHelper helper;
-    helper.setLogicalProcessorCount(4);
+    helper.setLogicalProcessorCount(getNumberOfTestMpiRanks());
     helper.expectPinningMessage(false, 1);
     helper.expectAffinitySet(gmx_node_rank());
     helper.setAffinity(1);
@@ -61,11 +78,11 @@ TEST(ThreadAffinityMultiRankTest, PinsWholeNode)
 
 TEST(ThreadAffinityMultiRankTest, PinsWithOffsetAndStride)
 {
-    GMX_MPI_TEST(4);
+    GMX_MPI_TEST(AllowAnyRankCount);
     ThreadAffinityTestHelper helper;
     helper.setAffinityOption(ThreadAffinity::On);
     helper.setOffsetAndStride(1, 2);
-    helper.setLogicalProcessorCount(8);
+    helper.setLogicalProcessorCount(2 * getNumberOfTestMpiRanks());
     helper.expectWarningMatchingRegex("Applying core pinning offset 1");
     helper.expectPinningMessage(true, 2);
     helper.expectAffinitySet(1 + 2 * gmx_node_rank());
@@ -74,7 +91,7 @@ TEST(ThreadAffinityMultiRankTest, PinsWithOffsetAndStride)
 
 TEST(ThreadAffinityMultiRankTest, PinsTwoNodes)
 {
-    GMX_MPI_TEST(4);
+    GMX_MPI_TEST(RequireEvenRankCountWithAtLeastFourRanks);
     ThreadAffinityTestHelper helper;
     helper.setPhysicalNodeId(gmx_node_rank() / 2);
     helper.setLogicalProcessorCount(2);
@@ -85,30 +102,32 @@ TEST(ThreadAffinityMultiRankTest, PinsTwoNodes)
 
 TEST(ThreadAffinityMultiRankTest, DoesNothingWhenDisabled)
 {
-    GMX_MPI_TEST(4);
+    GMX_MPI_TEST(AllowAnyRankCount);
     ThreadAffinityTestHelper helper;
     helper.setAffinityOption(ThreadAffinity::Off);
-    helper.setLogicalProcessorCount(4);
+    helper.setLogicalProcessorCount(getNumberOfTestMpiRanks());
     helper.setAffinity(1);
 }
 
 TEST(ThreadAffinityMultiRankTest, HandlesTooManyThreadsWithAuto)
 {
-    GMX_MPI_TEST(4);
+    GMX_MPI_TEST(AllowAnyRankCount);
     ThreadAffinityTestHelper helper;
-    helper.setLogicalProcessorCount(6);
+    const int                threadsPerRank = 2;
+    helper.setLogicalProcessorCount(threadsPerRank * getNumberOfTestMpiRanks() - 1);
     helper.expectWarningMatchingRegex("Oversubscribing the CPU");
-    helper.setAffinity(2);
+    helper.setAffinity(threadsPerRank);
 }
 
 TEST(ThreadAffinityMultiRankTest, HandlesTooManyThreadsWithForce)
 {
-    GMX_MPI_TEST(4);
+    GMX_MPI_TEST(AllowAnyRankCount);
     ThreadAffinityTestHelper helper;
+    const int                threadsPerRank = 2;
     helper.setAffinityOption(ThreadAffinity::On);
-    helper.setLogicalProcessorCount(6);
+    helper.setLogicalProcessorCount(threadsPerRank * getNumberOfTestMpiRanks() - 1);
     helper.expectWarningMatchingRegex("Oversubscribing the CPU");
-    helper.setAffinity(2);
+    helper.setAffinity(threadsPerRank);
 }
 
 class ThreadAffinityHeterogeneousNodesTest : public ::testing::Test
@@ -118,11 +137,11 @@ public:
     static int  indexInNode() { return gmx_node_rank() % 2; }
     static bool isMaster() { return gmx_node_rank() == 0; }
 
-    static void setupNodes(ThreadAffinityTestHelper* helper, std::array<int, 2> cores)
+    static void setupNodes(ThreadAffinityTestHelper* helper, int coresOnNodeZero, int coresOnOtherNodes)
     {
         const int node = currentNode();
         helper->setPhysicalNodeId(node);
-        helper->setLogicalProcessorCount(cores[node]);
+        helper->setLogicalProcessorCount(node == 0 ? coresOnNodeZero : coresOnOtherNodes);
     }
     static void expectNodeAffinitySet(ThreadAffinityTestHelper* helper, int node, int core)
     {
@@ -135,10 +154,10 @@ public:
 
 TEST_F(ThreadAffinityHeterogeneousNodesTest, PinsOnMasterOnly)
 {
-    GMX_MPI_TEST(4);
+    GMX_MPI_TEST(RequireEvenRankCountWithAtLeastFourRanks);
     ThreadAffinityTestHelper helper;
     helper.setAffinityOption(ThreadAffinity::On);
-    setupNodes(&helper, { { 2, 1 } });
+    setupNodes(&helper, 2, 1);
     helper.expectWarningMatchingRegexIf("Oversubscribing the CPU", isMaster() || currentNode() == 1);
     if (currentNode() == 0)
     {
@@ -150,25 +169,25 @@ TEST_F(ThreadAffinityHeterogeneousNodesTest, PinsOnMasterOnly)
 
 TEST_F(ThreadAffinityHeterogeneousNodesTest, PinsOnNonMasterOnly)
 {
-    GMX_MPI_TEST(4);
+    GMX_MPI_TEST(RequireEvenRankCountWithAtLeastFourRanks);
     ThreadAffinityTestHelper helper;
     helper.setAffinityOption(ThreadAffinity::On);
-    setupNodes(&helper, { { 1, 2 } });
+    setupNodes(&helper, 1, 2);
     helper.expectWarningMatchingRegexIf("Oversubscribing the CPU", currentNode() == 0);
-    if (currentNode() == 1)
+    if (currentNode() >= 1)
     {
         helper.expectPinningMessage(false, 1);
+        expectNodeAffinitySet(&helper, currentNode(), indexInNode());
     }
-    expectNodeAffinitySet(&helper, 1, indexInNode());
     helper.setAffinity(1);
 }
 
 TEST_F(ThreadAffinityHeterogeneousNodesTest, HandlesUnknownHardwareOnNonMaster)
 {
-    GMX_MPI_TEST(4);
+    GMX_MPI_TEST(RequireEvenRankCountWithAtLeastFourRanks);
     ThreadAffinityTestHelper helper;
     helper.setAffinityOption(ThreadAffinity::On);
-    setupNodes(&helper, { { 2, 0 } });
+    setupNodes(&helper, 2, 0);
     helper.expectWarningMatchingRegexIf("No information on available cores",
                                         isMaster() || currentNode() == 1);
     if (currentNode() == 0)
@@ -181,9 +200,9 @@ TEST_F(ThreadAffinityHeterogeneousNodesTest, HandlesUnknownHardwareOnNonMaster)
 
 TEST_F(ThreadAffinityHeterogeneousNodesTest, PinsAutomaticallyOnMasterOnly)
 {
-    GMX_MPI_TEST(4);
+    GMX_MPI_TEST(RequireEvenRankCountWithAtLeastFourRanks);
     ThreadAffinityTestHelper helper;
-    setupNodes(&helper, { { 2, 1 } });
+    setupNodes(&helper, 2, 1);
     helper.expectWarningMatchingRegexIf("Oversubscribing the CPU", isMaster() || currentNode() == 1);
     if (currentNode() == 0)
     {
@@ -195,27 +214,27 @@ TEST_F(ThreadAffinityHeterogeneousNodesTest, PinsAutomaticallyOnMasterOnly)
 
 TEST_F(ThreadAffinityHeterogeneousNodesTest, PinsAutomaticallyOnNonMasterOnly)
 {
-    GMX_MPI_TEST(4);
+    GMX_MPI_TEST(RequireEvenRankCountWithAtLeastFourRanks);
     ThreadAffinityTestHelper helper;
-    setupNodes(&helper, { { 1, 2 } });
+    setupNodes(&helper, 1, 2);
     helper.expectWarningMatchingRegexIf("Oversubscribing the CPU", currentNode() == 0);
-    if (currentNode() == 1)
+    if (currentNode() >= 1)
     {
         helper.expectPinningMessage(false, 1);
+        expectNodeAffinitySet(&helper, currentNode(), indexInNode());
     }
-    expectNodeAffinitySet(&helper, 1, indexInNode());
     helper.setAffinity(1);
 }
 
 TEST_F(ThreadAffinityHeterogeneousNodesTest, HandlesInvalidOffsetOnNonMasterOnly)
 {
-    GMX_MPI_TEST(4);
+    GMX_MPI_TEST(RequireEvenRankCountWithAtLeastFourRanks);
     ThreadAffinityTestHelper helper;
     helper.setAffinityOption(ThreadAffinity::On);
     helper.setOffsetAndStride(2, 0);
-    setupNodes(&helper, { { 4, 2 } });
+    setupNodes(&helper, 4, 2);
     helper.expectWarningMatchingRegex("Applying core pinning offset 2");
-    helper.expectWarningMatchingRegexIf("Requested offset too large", isMaster() || currentNode() == 1);
+    helper.expectWarningMatchingRegexIf("Requested offset too large", isMaster() || currentNode() >= 1);
     if (currentNode() == 0)
     {
         helper.expectPinningMessage(false, 1);
@@ -226,11 +245,11 @@ TEST_F(ThreadAffinityHeterogeneousNodesTest, HandlesInvalidOffsetOnNonMasterOnly
 
 TEST_F(ThreadAffinityHeterogeneousNodesTest, HandlesInvalidStrideOnNonMasterOnly)
 {
-    GMX_MPI_TEST(4);
+    GMX_MPI_TEST(RequireEvenRankCountWithAtLeastFourRanks);
     ThreadAffinityTestHelper helper;
     helper.setAffinityOption(ThreadAffinity::On);
     helper.setOffsetAndStride(0, 2);
-    setupNodes(&helper, { { 4, 2 } });
+    setupNodes(&helper, 4, 2);
     helper.expectWarningMatchingRegexIf("Requested stride too large", isMaster() || currentNode() == 1);
     if (currentNode() == 0)
     {
@@ -241,3 +260,5 @@ TEST_F(ThreadAffinityHeterogeneousNodesTest, HandlesInvalidStrideOnNonMasterOnly
 }
 
 } // namespace
+} // namespace test
+} // namespace gmx