/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2016,2017,2019, 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);
TEST(ThreadAffinityMultiRankTest, PinsWithOffsetAndStride)
{
- GMX_MPI_TEST(4);
+ GMX_MPI_TEST(AllowAnyRankCount);
ThreadAffinityTestHelper helper;
- helper.setAffinityOption(threadaffON);
+ 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());
+ helper.expectAffinitySet(1 + 2 * gmx_node_rank());
helper.setAffinity(1);
}
TEST(ThreadAffinityMultiRankTest, PinsTwoNodes)
{
- GMX_MPI_TEST(4);
+ GMX_MPI_TEST(RequireEvenRankCountWithAtLeastFourRanks);
ThreadAffinityTestHelper helper;
- helper.setPhysicalNodeId(gmx_node_rank()/2);
+ helper.setPhysicalNodeId(gmx_node_rank() / 2);
helper.setLogicalProcessorCount(2);
helper.expectPinningMessage(false, 1);
- helper.expectAffinitySet(gmx_node_rank()%2);
+ helper.expectAffinitySet(gmx_node_rank() % 2);
helper.setAffinity(1);
}
TEST(ThreadAffinityMultiRankTest, DoesNothingWhenDisabled)
{
- GMX_MPI_TEST(4);
+ GMX_MPI_TEST(AllowAnyRankCount);
ThreadAffinityTestHelper helper;
- helper.setAffinityOption(threadaffOFF);
- helper.setLogicalProcessorCount(4);
+ helper.setAffinityOption(ThreadAffinity::Off);
+ 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;
- helper.setAffinityOption(threadaffON);
- helper.setLogicalProcessorCount(6);
+ const int threadsPerRank = 2;
+ helper.setAffinityOption(ThreadAffinity::On);
+ helper.setLogicalProcessorCount(threadsPerRank * getNumberOfTestMpiRanks() - 1);
helper.expectWarningMatchingRegex("Oversubscribing the CPU");
- helper.setAffinity(2);
+ helper.setAffinity(threadsPerRank);
}
class ThreadAffinityHeterogeneousNodesTest : public ::testing::Test
{
- public:
- int currentNode() const { return gmx_node_rank() / 2; }
- int indexInNode() const { return gmx_node_rank() % 2; }
- bool isMaster() const { return gmx_node_rank() == 0; }
+public:
+ static int currentNode() { return gmx_node_rank() / 2; }
+ static int indexInNode() { return gmx_node_rank() % 2; }
+ static bool isMaster() { return gmx_node_rank() == 0; }
- void setupNodes(ThreadAffinityTestHelper *helper, std::array<int, 2> cores)
- {
- const int node = currentNode();
- helper->setPhysicalNodeId(node);
- helper->setLogicalProcessorCount(cores[node]);
- }
- void expectNodeAffinitySet(ThreadAffinityTestHelper *helper, int node, int core)
+ static void setupNodes(ThreadAffinityTestHelper* helper, int coresOnNodeZero, int coresOnOtherNodes)
+ {
+ const int node = currentNode();
+ helper->setPhysicalNodeId(node);
+ helper->setLogicalProcessorCount(node == 0 ? coresOnNodeZero : coresOnOtherNodes);
+ }
+ static void expectNodeAffinitySet(ThreadAffinityTestHelper* helper, int node, int core)
+ {
+ if (currentNode() == node)
{
- if (currentNode() == node)
- {
- helper->expectAffinitySet(core);
- }
+ helper->expectAffinitySet(core);
}
+ }
};
TEST_F(ThreadAffinityHeterogeneousNodesTest, PinsOnMasterOnly)
{
- GMX_MPI_TEST(4);
+ GMX_MPI_TEST(RequireEvenRankCountWithAtLeastFourRanks);
ThreadAffinityTestHelper helper;
- helper.setAffinityOption(threadaffON);
- setupNodes(&helper, {{2, 1}});
+ helper.setAffinityOption(ThreadAffinity::On);
+ setupNodes(&helper, 2, 1);
helper.expectWarningMatchingRegexIf("Oversubscribing the CPU", isMaster() || currentNode() == 1);
if (currentNode() == 0)
{
TEST_F(ThreadAffinityHeterogeneousNodesTest, PinsOnNonMasterOnly)
{
- GMX_MPI_TEST(4);
+ GMX_MPI_TEST(RequireEvenRankCountWithAtLeastFourRanks);
ThreadAffinityTestHelper helper;
- helper.setAffinityOption(threadaffON);
- setupNodes(&helper, {{1, 2}});
+ helper.setAffinityOption(ThreadAffinity::On);
+ 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(threadaffON);
- setupNodes(&helper, {{2, 0}});
- helper.expectWarningMatchingRegexIf("No information on available cores", isMaster() || currentNode() == 1);
+ helper.setAffinityOption(ThreadAffinity::On);
+ setupNodes(&helper, 2, 0);
+ helper.expectWarningMatchingRegexIf("No information on available cores",
+ isMaster() || currentNode() == 1);
if (currentNode() == 0)
{
helper.expectPinningMessage(false, 1);
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)
{
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(threadaffON);
+ 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);
}
- expectNodeAffinitySet(&helper, 0, indexInNode()+2);
+ expectNodeAffinitySet(&helper, 0, indexInNode() + 2);
helper.setAffinity(1);
}
TEST_F(ThreadAffinityHeterogeneousNodesTest, HandlesInvalidStrideOnNonMasterOnly)
{
- GMX_MPI_TEST(4);
+ GMX_MPI_TEST(RequireEvenRankCountWithAtLeastFourRanks);
ThreadAffinityTestHelper helper;
- helper.setAffinityOption(threadaffON);
+ 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)
{
helper.expectPinningMessage(true, 2);
}
- expectNodeAffinitySet(&helper, 0, 2*indexInNode());
+ expectNodeAffinitySet(&helper, 0, 2 * indexInNode());
helper.setAffinity(1);
}
} // namespace
+} // namespace test
+} // namespace gmx