From ba2350625168229dbe61d58885c7f10bfa2a7bf6 Mon Sep 17 00:00:00 2001 From: Jonathan Lifflander Date: Wed, 3 Dec 2025 17:41:34 -0800 Subject: [PATCH 1/3] #14: tests: write some basic comm tests incl. reduce --- tests/unit/comm/test_comm.cc | 186 +++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 tests/unit/comm/test_comm.cc diff --git a/tests/unit/comm/test_comm.cc b/tests/unit/comm/test_comm.cc new file mode 100644 index 0000000..0eab860 --- /dev/null +++ b/tests/unit/comm/test_comm.cc @@ -0,0 +1,186 @@ +/* +//@HEADER +// ***************************************************************************** +// +// test_comm.h +// DARMA/vt-lb => Virtual Transport/Load Balancers +// +// Copyright 2019-2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact darma@sandia.gov +// +// ***************************************************************************** +//@HEADER +*/ + +#include + +#include "test_parallel_harness.h" +#include "test_helpers.h" + +#include +#include +#include +#include +#include + +namespace vt_lb { namespace tests { namespace unit { + +template +struct TestCommBasic : TestParallelHarness { + struct TestObject { + int calls = 0; + + void ping(int v) { calls += v; } + }; + + // Member function pointers for send + static constexpr auto fn_ping = &TestObject::ping; + + using HandleT = typename CommType::template HandleType; + + // helper to register object and return handle + HandleT makeHandle(TestObject* obj) { + return this->comm.template registerInstanceCollective(obj); + } + + // helper to drain progress + void progressUntil(std::function pred, int max_iters = 10000) { + int it = 0; + while (!pred() && it++ < max_iters) { + this->comm.poll(); + } + } +}; + +TYPED_TEST_SUITE(TestCommBasic, CommTypesForTesting, CommNameGenerator); + +TYPED_TEST(TestCommBasic, test_init_finalize_and_basic_props) { + auto& the_comm = this->comm; + + SET_MIN_NUM_NODES_CONSTRAINT(2); + + // Basic size/rank API should work + ASSERT_GE(the_comm.numRanks(), 1); + ASSERT_GE(the_comm.getRank(), 0); + ASSERT_LT(the_comm.getRank(), the_comm.numRanks()); + // Poll should be callable + (void)the_comm.poll(); +} + +TYPED_TEST(TestCommBasic, test_send_and_poll_dispatch) { + auto& the_comm = this->comm; + + SET_MIN_NUM_NODES_CONSTRAINT(2); + + typename TestFixture::TestObject obj{}; + auto handle = this->makeHandle(&obj); + + // Send to neighbor in a ring + int self = the_comm.getRank(); + int n = the_comm.numRanks(); + int dest = (self + 1) % n; + + // Each rank sends to its right neighbor; the left neighbor sends to us with payload (left+1) + the_comm.template send(dest, handle, self + 1); + + // Expected value received from left neighbor + int left = (self - 1 + n) % n; + int expected = left + 1; // equals self for self>0, equals n for self==0 + + // Progress until our local object receives from our left neighbor + this->progressUntil([&] { return obj.calls == expected; }); + EXPECT_EQ(obj.calls, expected); +} + +TYPED_TEST(TestCommBasic, test_reduce_sum_int_single) { + auto& the_comm = this->comm; + + SET_MIN_NUM_NODES_CONSTRAINT(2); + + typename TestFixture::TestObject obj{}; + auto handle = this->makeHandle(&obj); + + int const root = 0; + int send = 1; + int recv = 0; + + handle.reduce(root, MPI_INT, MPI_SUM, &send, &recv, 1); + + if (the_comm.getRank() == root) { + // Sum over all ranks of 1 + EXPECT_EQ(recv, the_comm.numRanks()); + } +} + +TYPED_TEST(TestCommBasic, test_reduce_max_double_array) { + auto& the_comm = this->comm; + + SET_MIN_NUM_NODES_CONSTRAINT(2); + + typename TestFixture::TestObject obj{}; + auto handle = this->makeHandle(&obj); + + int const root = 0; + // each rank contributes two doubles, make max depend on rank + std::array send{{double(the_comm.getRank()), double(the_comm.getRank() + 1)}}; + std::array recv{{0.0, 0.0}}; + + handle.reduce(root, MPI_DOUBLE, MPI_MAX, send.data(), recv.data(), int(send.size())); + + if (the_comm.getRank() == root) { + EXPECT_DOUBLE_EQ(recv[0], double(the_comm.numRanks() - 1)); + EXPECT_DOUBLE_EQ(recv[1], double(the_comm.numRanks())); + } +} + +TYPED_TEST(TestCommBasic, test_reduce_sum_float_array) { + auto& the_comm = this->comm; + + SET_MIN_NUM_NODES_CONSTRAINT(2); + + typename TestFixture::TestObject obj{}; + auto handle = this->makeHandle(&obj); + + int const root = 0; + std::vector send(4, 1.0f); // each rank contributes 4 ones + std::vector recv(4, 0.0f); + + handle.reduce(root, MPI_FLOAT, MPI_SUM, send.data(), recv.data(), int(send.size())); + + if (the_comm.getRank() == root) { + for (auto v : recv) { + EXPECT_FLOAT_EQ(v, float(the_comm.numRanks())); + } + } +} + +}}} // end namespace vt_lb::tests::unit From c0520f72cfa3603bf695a638cd37e5e5d2a51604 Mon Sep 17 00:00:00 2001 From: Jonathan Lifflander Date: Wed, 3 Dec 2025 17:44:35 -0800 Subject: [PATCH 2/3] #14: comm: fix bug in reduce with more than one element --- src/vt-lb/comm/vt/proxy_wrapper.impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vt-lb/comm/vt/proxy_wrapper.impl.h b/src/vt-lb/comm/vt/proxy_wrapper.impl.h index 229e3ed..b10ebd7 100644 --- a/src/vt-lb/comm/vt/proxy_wrapper.impl.h +++ b/src/vt-lb/comm/vt/proxy_wrapper.impl.h @@ -72,7 +72,7 @@ void ProxyWrapper::reduceAnonCb(vt::collective::ReduceTMsg* msg, Redu } else { using ValT = typename T::value_type; static_assert(std::is_trivially_copyable_v || std::is_arithmetic_v, "Reduce value must be trivially copyable"); - std::memcpy(ctx->out_ptr, std::addressof(val), sizeof(ValT) * std::max(1, ctx->count)); + std::memcpy(ctx->out_ptr, std::addressof(val.at(0)), sizeof(ValT) * std::max(1, ctx->count)); } ctx->done.store(true, std::memory_order_release); } From 3ea2c5a2a579fe62ad9f95dc06e9d85c18d3dbff Mon Sep 17 00:00:00 2001 From: Jonathan Lifflander Date: Wed, 3 Dec 2025 17:45:08 -0800 Subject: [PATCH 3/3] #14: test_harness: fix bug--not using this->comm causes failure to compile --- tests/unit/test_helpers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_helpers.h b/tests/unit/test_helpers.h index 87e2a9f..9072b0c 100644 --- a/tests/unit/test_helpers.h +++ b/tests/unit/test_helpers.h @@ -131,7 +131,7 @@ inline std::string getUniqueFilename(const std::string& ext = "") { */ #define SET_MIN_NUM_NODES_CONSTRAINT(min_req_num_nodes) \ { \ - auto const num_nodes = comm.numRanks(); \ + auto const num_nodes = this->comm.numRanks(); \ if (num_nodes < min_req_num_nodes) { \ GTEST_SKIP() << fmt::format( \ "Skipping the run on {} nodes. This test should run on at least {} "\