From 27fda7592f307dc4bd4f1f3e0597c8270e033cc4 Mon Sep 17 00:00:00 2001 From: Gabriel Dos Santos Date: Thu, 2 Apr 2026 11:57:53 +0200 Subject: [PATCH 01/10] refactor(test): rework + add helpers for handling MD-rank Views --- unit_tests/CMakeLists.txt | 2 +- unit_tests/test_send_recv.cpp | 74 +++++++++++++++++++++ unit_tests/test_sendrecv.cpp | 121 ---------------------------------- unit_tests/view_builder.hpp | 33 ---------- unit_tests/view_utils.hpp | 117 ++++++++++++++++++++++++++++++++ 5 files changed, 192 insertions(+), 155 deletions(-) create mode 100644 unit_tests/test_send_recv.cpp delete mode 100644 unit_tests/test_sendrecv.cpp delete mode 100644 unit_tests/view_builder.hpp create mode 100644 unit_tests/view_utils.hpp diff --git a/unit_tests/CMakeLists.txt b/unit_tests/CMakeLists.txt index 8dbf2f8a..08d81999 100644 --- a/unit_tests/CMakeLists.txt +++ b/unit_tests/CMakeLists.txt @@ -46,7 +46,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake") include(kc-test) # --- Core API unit tests --- # -kc_add_unit_test(test.core.p2p CORE NUM_PES 2 FILES test_main.cpp test_sendrecv.cpp) +kc_add_unit_test(test.core.send-recv CORE NUM_PES 2 FILES test_main.cpp test_send_recv.cpp) kc_add_unit_test(test.core.broadcast CORE NUM_PES 2 FILES test_main.cpp test_broadcast.cpp) kc_add_unit_test(test.core.all-gather CORE NUM_PES 2 FILES test_main.cpp test_allgather.cpp) kc_add_unit_test(test.core.reduce CORE NUM_PES 2 FILES test_main.cpp test_reduce.cpp) diff --git a/unit_tests/test_send_recv.cpp b/unit_tests/test_send_recv.cpp new file mode 100644 index 00000000..15ffb8fe --- /dev/null +++ b/unit_tests/test_send_recv.cpp @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// SPDX-FileCopyrightText: Copyright Contributors to the Kokkos project + +#include +#include + +#include "view_utils.hpp" +#if defined(KOKKOSCOMM_ENABLE_NCCL) +#include "nccl/utils.hpp" +#endif + +namespace { + +template +void test_core_send_recv(const View& v) { +#if defined(KOKKOSCOMM_ENABLE_NCCL) + auto nccl_ctx = test_utils::nccl::Ctx::init(); + auto raw_comm = nccl_ctx.comm(); +#else + auto raw_comm = MPI_COMM_WORLD; +#endif + auto exec = Kokkos::DefaultExecutionSpace{}; + auto comm = KokkosComm::Communicator<>::from_raw(raw_comm, exec); + const int size = comm.size(); + const int rank = comm.rank(); + if (size < 2) { + GTEST_SKIP() << "Requires >= 2 ranks (" << size << " provided)"; + } + const int src = 0; + const int dst = 1; + + if (rank == src) { + test_utils::init_view(exec, v); + exec.fence(); + KokkosComm::send(comm, v, dst).wait(); + } else if (rank == dst) { + KokkosComm::recv(comm, v, src).wait(); + int errs = test_utils::count_errors(v); + ASSERT_EQ(errs, 0); + } +} + +template +class SendRecv : public ::testing::Test { + public: + using Scalar = T; +}; + +#if defined(KOKKOSCOMM_ENABLE_NCCL) +using ScalarTypes = ::testing::Types; +#else +using ScalarTypes = + ::testing::Types, Kokkos::complex, int, unsigned, int64_t, size_t>; +#endif +TYPED_TEST_SUITE(SendRecv, ScalarTypes); + +TYPED_TEST(SendRecv, Contig1D) { + auto v = test_utils::build_view(test_utils::Contig{}, "v", 1013); + test_core_send_recv(v); +} +TYPED_TEST(SendRecv, NonContig1D) { + auto v = test_utils::build_view(test_utils::NonContig{}, "v", 1013); + test_core_send_recv(v); +} +TYPED_TEST(SendRecv, Contig2D) { + auto v = test_utils::build_view(test_utils::Contig{}, "v", 137, 17); + test_core_send_recv(v); +} +TYPED_TEST(SendRecv, NonContig2D) { + auto v = test_utils::build_view(test_utils::NonContig{}, "v", 137, 17); + test_core_send_recv(v); +} + +} // namespace diff --git a/unit_tests/test_sendrecv.cpp b/unit_tests/test_sendrecv.cpp deleted file mode 100644 index 787836c7..00000000 --- a/unit_tests/test_sendrecv.cpp +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// SPDX-FileCopyrightText: Copyright Contributors to the Kokkos project - -#include -#include - -#include "view_builder.hpp" -#if defined(KOKKOSCOMM_ENABLE_NCCL) -#include "nccl/utils.hpp" -#endif - -namespace { - -using Ex = Kokkos::DefaultExecutionSpace; -using Co = KokkosComm::DefaultCommunicationSpace; - -template -class SendRecv : public testing::Test { - public: - using Scalar = T; -}; - -#if defined(KOKKOSCOMM_ENABLE_NCCL) -using ScalarTypes = ::testing::Types; -#else -using ScalarTypes = - ::testing::Types, Kokkos::complex, int, unsigned, int64_t, size_t>; -#endif -TYPED_TEST_SUITE(SendRecv, ScalarTypes); - -template -void test_1d(const View1D &v) { - static_assert(View1D::rank == 1, ""); - using Scalar = typename View1D::non_const_value_type; - -#if defined(KOKKOSCOMM_ENABLE_NCCL) - auto nccl_ctx = test_utils::nccl::Ctx::init(); - auto raw_comm = nccl_ctx.comm(); -#else - auto raw_comm = MPI_COMM_WORLD; -#endif - auto exec = Ex(); - auto comm = KokkosComm::Communicator<>::from_raw(raw_comm, exec); - const int size = comm.size(); - const int rank = comm.rank(); - if (size < 2) { - GTEST_SKIP() << "Requires >= 2 ranks (" << size << " provided)"; - } - const int src = 0; - const int dst = 1; - - if (rank == src) { - Kokkos::parallel_for( - v.extent(0), KOKKOS_LAMBDA(const int i) { v(i) = i; } - ); - KokkosComm::send(comm, v, dst).wait(); - } else if (rank == dst) { - KokkosComm::recv(comm, v, src).wait(); - - int errs; - Kokkos::parallel_reduce( - v.extent(0), KOKKOS_LAMBDA(const int i, int &lsum) { lsum += v(i) != Scalar(i); }, errs - ); - ASSERT_EQ(errs, 0); - } -} - -template -void test_2d(const View2D &v) { - static_assert(View2D::rank == 2, ""); - using Scalar = typename View2D::non_const_value_type; - -#if defined(KOKKOSCOMM_ENABLE_NCCL) - auto nccl_ctx = test_utils::nccl::Ctx::init(); - auto raw_comm = nccl_ctx.comm(); -#else - auto raw_comm = MPI_COMM_WORLD; -#endif - auto exec = Ex(); - auto comm = KokkosComm::Communicator<>::from_raw(raw_comm, exec); - const int size = comm.size(); - const int rank = comm.rank(); - if (size < 2) { - GTEST_SKIP() << "Requires >= 2 ranks (" << size << " provided)"; - } - const int src = 0; - const int dst = 1; - - using Policy = Kokkos::MDRangePolicy>; - Policy policy(exec, {0, 0}, {v.extent(0), v.extent(1)}); - - if (rank == src) { - Kokkos::parallel_for( - policy, KOKKOS_LAMBDA(const int i, const int j) { v(i, j) = i * v.extent(0) + j; } - ); - exec.fence(); - - KokkosComm::send(comm, v, dst).wait(); - } else if (rank == dst) { - KokkosComm::recv(comm, v, src).wait(); - - int errs; - Kokkos::parallel_reduce( - policy, KOKKOS_LAMBDA(const int i, const int j, int &lsum) { lsum += v(i, j) != Scalar(i * v.extent(0) + j); }, - errs - ); - exec.fence(); - ASSERT_EQ(errs, 0); - } -} - -TYPED_TEST(SendRecv, 1D_contig) { - auto v = ViewBuilder::view(contig{}, "v", 1013); - test_1d(v); -} -TYPED_TEST(SendRecv, 2D_contig) { - auto v = ViewBuilder::view(contig{}, "v", 137, 17); - test_2d(v); -} - -} // namespace diff --git a/unit_tests/view_builder.hpp b/unit_tests/view_builder.hpp deleted file mode 100644 index bb2cd82e..00000000 --- a/unit_tests/view_builder.hpp +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// SPDX-FileCopyrightText: Copyright Contributors to the Kokkos project - -#pragma once - -#include - -struct contig {}; -struct noncontig {}; - -template -struct ViewBuilder; - -template -struct ViewBuilder { - static auto view(noncontig, const std::string &name, int e0) { - // this is C-style layout, i.e. v(0,0) is next to v(0,1) - Kokkos::View v(name, e0, 2); - return Kokkos::subview(v, Kokkos::ALL, 1); // take column 1 - } - - static auto view(contig, const std::string &name, int e0) { return Kokkos::View(name, e0); } -}; - -template -struct ViewBuilder { - static auto view(noncontig, const std::string &name, int e0, int e1) { - Kokkos::View v(name, e0, e1, 2); - return Kokkos::subview(v, Kokkos::ALL, Kokkos::ALL, 1); - } - - static auto view(contig, const std::string &name, int e0, int e1) { return Kokkos::View(name, e0, e1); } -}; diff --git a/unit_tests/view_utils.hpp b/unit_tests/view_utils.hpp new file mode 100644 index 00000000..b727a7f9 --- /dev/null +++ b/unit_tests/view_utils.hpp @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// SPDX-FileCopyrightText: Copyright Contributors to the Kokkos project + +#pragma once + +#include +#include +#include + +#include + +namespace test_utils { + +struct Contig {}; +struct NonContig {}; + +namespace Impl { + +template +struct AddPtrs { + using type = typename AddPtrs::type; +}; +template +struct AddPtrs { + using type = T; +}; +template +using Stars = typename AddPtrs::type; + +template +struct InitFunctor { + View v; + std::array exts; + + KOKKOS_FUNCTION void operator()(decltype(static_cast(std::declval().extent(Is)))... idxs) const { + int val = 0; + int ids[] = {int(idxs)...}; + ((val = ids[Is] + exts[Is] * val), ...); + [&](std::index_sequence) { v(ids[Js]...) = val; } + (std::index_sequence{}); + } +}; + +template +struct CountFunctor { + using Scalar = typename View::value_type; + View v; + std::array exts; + + KOKKOS_FUNCTION void operator()(decltype(static_cast(std::declval().extent(Is)))... idxs, int& lsum) + const { + int val = 0; + int ids[] = {int(idxs)...}; + ((val = ids[Is] + exts[Is] * val), ...); + [&](std::index_sequence) { lsum += v(ids[Js]...) != Scalar(val); } + (std::index_sequence{}); + } +}; + +} // namespace Impl + +template +auto build_view(Contig, const std::string& name, Extents... exts) { + static_assert(sizeof...(exts) == R, "Number of extents must match Rank"); + return Kokkos::View>(name, exts...); +} +template +auto build_view(NonContig, const std::string& name, Extents... exts) { + static_assert(sizeof...(exts) == R, "Number of extents must match Rank"); + Kokkos::View, Kokkos::LayoutRight> v(name, exts..., 2); + return [&](std::index_sequence) { return Kokkos::subview(v, (void(Is), Kokkos::ALL)..., 1); } + (std::make_index_sequence{}); +} + +template +auto init_view(const Exec& exec, const View& v) -> void { + constexpr size_t R = View::rank; + if constexpr (R == 1) { + Kokkos::parallel_for( + Kokkos::RangePolicy(exec, 0, v.extent(0)), KOKKOS_LAMBDA(const int i) { v(i) = i; } + ); + } else { + [&](std::index_sequence) { + std::array exts = {static_cast(v.extent(Is))...}; + Kokkos::parallel_for( + Kokkos::MDRangePolicy>(exec, {(void(Is), 0)...}, {static_cast(v.extent(Is))...}), + Impl::InitFunctor{v, exts} + ); + } + (std::make_index_sequence{}); + } + exec.fence(); +} + +template +auto count_errors(const View& v) -> int { + constexpr size_t R = View::rank; + int errs = 0; + using Scalar = typename View::value_type; + if constexpr (R == 1) { + Kokkos::parallel_reduce( + v.extent(0), KOKKOS_LAMBDA(const int i, int& lsum) { lsum += v(i) != Scalar(i); }, errs + ); + } else { + [&](std::index_sequence) { + std::array exts = {static_cast(v.extent(Is))...}; + Kokkos::parallel_reduce( + Kokkos::MDRangePolicy>({(void(Is), 0)...}, {static_cast(v.extent(Is))...}), + Impl::CountFunctor{v, exts}, errs + ); + } + (std::make_index_sequence{}); + } + return errs; +} + +} // namespace test_utils From 252e8db928aa08a374544e74a88b8351b5726fd0 Mon Sep 17 00:00:00 2001 From: Gabriel Dos Santos Date: Wed, 25 Mar 2026 15:26:33 +0100 Subject: [PATCH 02/10] feat(core): refactor contiguous packing to support any dims Signed-off-by: Gabriel Dos Santos --- src/KokkosComm/impl/contiguous.hpp | 36 +++++++++++++----------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/KokkosComm/impl/contiguous.hpp b/src/KokkosComm/impl/contiguous.hpp index c4374b9b..51042976 100644 --- a/src/KokkosComm/impl/contiguous.hpp +++ b/src/KokkosComm/impl/contiguous.hpp @@ -3,7 +3,9 @@ #pragma once +#include #include +#include #include @@ -21,31 +23,23 @@ struct contiguous_view { template using contiguous_view_t = contiguous_view::type; -template -auto allocate_contiguous_for(const Space &space, const std::string &label, View &v) { - using non_const_packed_view_type = contiguous_view_t; - - if constexpr (KokkosComm::rank() == 1) { - return non_const_packed_view_type(Kokkos::view_alloc(space, Kokkos::WithoutInitializing, label), v.extent(0)); - } else if constexpr (KokkosComm::rank() == 2) { - return non_const_packed_view_type(Kokkos::view_alloc(space, Kokkos::WithoutInitializing, label), v.extent(0), - v.extent(1)); - } else { - static_assert(std::is_void_v, "allocate_contiguous_for for views > rank 2 not implemented"); +template +auto allocate_contiguous_for(const Exec& exec, const View& v, const std::string& label = "contiguous_view") { + using ContigView = contiguous_view_t; + return [&](std::index_sequence) { + return ContigView(Kokkos::view_alloc(exec, Kokkos::WithoutInitializing, label), v.extent(Is)...); } + (std::make_index_sequence()>{}); } -template -auto resize_contiguous_for(const Space &space, DstView &out, const SrcView &in) { - static_assert(DstView::rank == SrcView::rank, ""); - - if constexpr (KokkosComm::rank() == 1) { - Kokkos::realloc(Kokkos::view_alloc(space, Kokkos::WithoutInitializing), out, in.extent(0)); - } else if constexpr (KokkosComm::rank() == 2) { - Kokkos::realloc(Kokkos::view_alloc(space, Kokkos::WithoutInitializing), out, in.extent(0), in.extent(1)); - } else { - static_assert(std::is_void_v, "realloc_contiguous_for for views > rank 2 not implemented"); +template +auto resize_contiguous_for(const Exec& exec, const DstV& dst, const SrcV& src) { + static_assert(rank() == rank(), + "KokkosComm::Impl::resize_contiguous_for: source and destination Views must have the same rank"); + [&](std::index_sequence) { + Kokkos::realloc(Kokkos::view_alloc(exec, Kokkos::WithoutInitializing), dst, src.extent(Is)...); } + (std::make_index_sequence()>{}); } } // namespace KokkosComm::Impl From 5f039ef8188aa26dce0c384944136a19d18f2ea7 Mon Sep 17 00:00:00 2001 From: Gabriel Dos Santos Date: Wed, 25 Mar 2026 15:27:48 +0100 Subject: [PATCH 03/10] chore: propagate new interface in backend packers Signed-off-by: Gabriel Dos Santos --- src/KokkosComm/mpi/impl/packer.hpp | 2 +- src/KokkosComm/nccl/impl/packer.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/KokkosComm/mpi/impl/packer.hpp b/src/KokkosComm/mpi/impl/packer.hpp index 72e4dad1..ff3785fb 100644 --- a/src/KokkosComm/mpi/impl/packer.hpp +++ b/src/KokkosComm/mpi/impl/packer.hpp @@ -35,7 +35,7 @@ struct DeepCopy { /// Returns allocated, uninitialized, contiguous view for packing `src`. template static auto allocate_packed_for(const ES &space, const std::string &label, const V &src) -> Args { - auto packed = KokkosComm::Impl::allocate_contiguous_for(space, label, src); + auto packed = KokkosComm::Impl::allocate_contiguous_for(space, src, label); return Args(packed, datatype(), span(packed)); } diff --git a/src/KokkosComm/nccl/impl/packer.hpp b/src/KokkosComm/nccl/impl/packer.hpp index 6b7dcf1b..491ee809 100644 --- a/src/KokkosComm/nccl/impl/packer.hpp +++ b/src/KokkosComm/nccl/impl/packer.hpp @@ -38,7 +38,7 @@ struct DeepCopy { /// @return An allocated, uninitialized, contiguous view fit for packing `view`. template static auto allocate_packed_for(const E& exec, const std::string& label, const V& view) -> PackedNcclView { - auto packed = KokkosComm::Impl::allocate_contiguous_for(exec, label, view); + auto packed = KokkosComm::Impl::allocate_contiguous_for(exec, view, label); return PackedNcclView(packed, datatype(), span(packed)); } From f8ad68e84f1fc50512c651c9cbe273dc4ec39621 Mon Sep 17 00:00:00 2001 From: Gabriel Dos Santos Date: Wed, 25 Mar 2026 16:27:52 +0100 Subject: [PATCH 04/10] docs(core): add source doc-comments for contiguous No public docs since these APIs are internal impl details of Kokkos Comm. Helps with linting for developers though. Signed-off-by: Gabriel Dos Santos --- src/KokkosComm/impl/contiguous.hpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/KokkosComm/impl/contiguous.hpp b/src/KokkosComm/impl/contiguous.hpp index 51042976..1fa0a1dd 100644 --- a/src/KokkosComm/impl/contiguous.hpp +++ b/src/KokkosComm/impl/contiguous.hpp @@ -23,6 +23,12 @@ struct contiguous_view { template using contiguous_view_t = contiguous_view::type; +/// @brief Allocate a contiguous View suitable for packing a non-contiguous View. +/// @tparam Exec A Kokkos Execution Space type. +/// @tparam View A Kokkos View type. +/// @param exec The execution space instance in which to perform the view allocation. +/// @param v The View to make a suitable contiguous allocation for. +/// @param label The label to give to the allocated contiguous View. Defaults to "contiguous_view". template auto allocate_contiguous_for(const Exec& exec, const View& v, const std::string& label = "contiguous_view") { using ContigView = contiguous_view_t; @@ -32,6 +38,13 @@ auto allocate_contiguous_for(const Exec& exec, const View& v, const std::string& (std::make_index_sequence()>{}); } +/// @brief Resize a View into a contiguous one suitable for packing a non-contiguous View. +/// @tparam Exec A Kokkos Execution Space type. +/// @tparam DstV The Kokkos View type of the destination View to resize. +/// @tparam DstV The Kokkos View type of the source View to resize for. +/// @param exec The execution space instance in which to perform the view reallocation. +/// @param dst The View to resize. +/// @param src The View to make a suitable contiguous resize for. template auto resize_contiguous_for(const Exec& exec, const DstV& dst, const SrcV& src) { static_assert(rank() == rank(), From 1ac5d71973e11093f1116002148d24afe8d54345 Mon Sep 17 00:00:00 2001 From: Gabriel Dos Santos Date: Thu, 26 Mar 2026 14:00:01 +0100 Subject: [PATCH 05/10] revert: parameter order in `allocate_contiguous_for` Signed-off-by: Gabriel Dos Santos --- src/KokkosComm/mpi/impl/packer.hpp | 2 +- src/KokkosComm/nccl/impl/packer.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/KokkosComm/mpi/impl/packer.hpp b/src/KokkosComm/mpi/impl/packer.hpp index ff3785fb..72e4dad1 100644 --- a/src/KokkosComm/mpi/impl/packer.hpp +++ b/src/KokkosComm/mpi/impl/packer.hpp @@ -35,7 +35,7 @@ struct DeepCopy { /// Returns allocated, uninitialized, contiguous view for packing `src`. template static auto allocate_packed_for(const ES &space, const std::string &label, const V &src) -> Args { - auto packed = KokkosComm::Impl::allocate_contiguous_for(space, src, label); + auto packed = KokkosComm::Impl::allocate_contiguous_for(space, label, src); return Args(packed, datatype(), span(packed)); } diff --git a/src/KokkosComm/nccl/impl/packer.hpp b/src/KokkosComm/nccl/impl/packer.hpp index 491ee809..6b7dcf1b 100644 --- a/src/KokkosComm/nccl/impl/packer.hpp +++ b/src/KokkosComm/nccl/impl/packer.hpp @@ -38,7 +38,7 @@ struct DeepCopy { /// @return An allocated, uninitialized, contiguous view fit for packing `view`. template static auto allocate_packed_for(const E& exec, const std::string& label, const V& view) -> PackedNcclView { - auto packed = KokkosComm::Impl::allocate_contiguous_for(exec, view, label); + auto packed = KokkosComm::Impl::allocate_contiguous_for(exec, label, view); return PackedNcclView(packed, datatype(), span(packed)); } From b4d474fbc7c35663cd1367e33d6900fcf2d1f31d Mon Sep 17 00:00:00 2001 From: Gabriel Dos Santos Date: Thu, 26 Mar 2026 14:03:11 +0100 Subject: [PATCH 06/10] style(core): explicit captures + explicit ret types + comments Signed-off-by: Gabriel Dos Santos --- src/KokkosComm/impl/contiguous.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/KokkosComm/impl/contiguous.hpp b/src/KokkosComm/impl/contiguous.hpp index 1fa0a1dd..0c5a63f0 100644 --- a/src/KokkosComm/impl/contiguous.hpp +++ b/src/KokkosComm/impl/contiguous.hpp @@ -30,9 +30,10 @@ using contiguous_view_t = contiguous_view::type; /// @param v The View to make a suitable contiguous allocation for. /// @param label The label to give to the allocated contiguous View. Defaults to "contiguous_view". template -auto allocate_contiguous_for(const Exec& exec, const View& v, const std::string& label = "contiguous_view") { +auto allocate_contiguous_for(const Exec& exec, const std::string& label, const View& v) -> contiguous_view { using ContigView = contiguous_view_t; - return [&](std::index_sequence) { + // Unpack `v` extents into the `ContigView` constructor + return [&label, &exec, &v ](std::index_sequence) { return ContigView(Kokkos::view_alloc(exec, Kokkos::WithoutInitializing, label), v.extent(Is)...); } (std::make_index_sequence()>{}); @@ -46,10 +47,11 @@ auto allocate_contiguous_for(const Exec& exec, const View& v, const std::string& /// @param dst The View to resize. /// @param src The View to make a suitable contiguous resize for. template -auto resize_contiguous_for(const Exec& exec, const DstV& dst, const SrcV& src) { +auto resize_contiguous_for(const Exec& exec, const DstV& dst, const SrcV& src) -> void { static_assert(rank() == rank(), "KokkosComm::Impl::resize_contiguous_for: source and destination Views must have the same rank"); - [&](std::index_sequence) { + // Unpack `src` extents into `realloc` call + [&exec, &dst, &src ](std::index_sequence) { Kokkos::realloc(Kokkos::view_alloc(exec, Kokkos::WithoutInitializing), dst, src.extent(Is)...); } (std::make_index_sequence()>{}); From ae7c51fc90eb43823781d3a93903c2d3d6a465a8 Mon Sep 17 00:00:00 2001 From: Gabriel Dos Santos Date: Thu, 26 Mar 2026 14:40:57 +0100 Subject: [PATCH 07/10] fix(core): wrong return type in `allocate_contiguous_for` Signed-off-by: Gabriel Dos Santos --- src/KokkosComm/impl/contiguous.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/KokkosComm/impl/contiguous.hpp b/src/KokkosComm/impl/contiguous.hpp index 0c5a63f0..cf81f266 100644 --- a/src/KokkosComm/impl/contiguous.hpp +++ b/src/KokkosComm/impl/contiguous.hpp @@ -30,11 +30,10 @@ using contiguous_view_t = contiguous_view::type; /// @param v The View to make a suitable contiguous allocation for. /// @param label The label to give to the allocated contiguous View. Defaults to "contiguous_view". template -auto allocate_contiguous_for(const Exec& exec, const std::string& label, const View& v) -> contiguous_view { - using ContigView = contiguous_view_t; +auto allocate_contiguous_for(const Exec& exec, const std::string& label, const View& v) -> contiguous_view_t { // Unpack `v` extents into the `ContigView` constructor return [&label, &exec, &v ](std::index_sequence) { - return ContigView(Kokkos::view_alloc(exec, Kokkos::WithoutInitializing, label), v.extent(Is)...); + return contiguous_view_t(Kokkos::view_alloc(exec, Kokkos::WithoutInitializing, label), v.extent(Is)...); } (std::make_index_sequence()>{}); } From 5f242015aa5235a16250e51fb1b8c79f54fb2a6e Mon Sep 17 00:00:00 2001 From: Gabriel Dos Santos Date: Thu, 2 Apr 2026 21:05:02 +0200 Subject: [PATCH 08/10] refactor(core): remove `resize_contiguous_for` API Removed because unused in the Kokkos Comm code base. Additionally, it was wrongly implemented. According to the latest Kokkos docs (https://kokkos.org/kokkos-core-wiki/API/core/view/realloc.html), there is no overload of `realloc` which allows to pass a `view_alloc` result (probably this was an artifact from copy-pasting the code of `allocate_contiguous_for`?), a destination, and the reshaped extents. --- src/KokkosComm/impl/contiguous.hpp | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/src/KokkosComm/impl/contiguous.hpp b/src/KokkosComm/impl/contiguous.hpp index cf81f266..f0548500 100644 --- a/src/KokkosComm/impl/contiguous.hpp +++ b/src/KokkosComm/impl/contiguous.hpp @@ -16,8 +16,10 @@ namespace KokkosComm::Impl { template struct contiguous_view { - using type = Kokkos::View; + using type = Kokkos::View< + typename View::non_const_data_type, + typename View::execution_space::array_layout, + typename View::memory_space>; }; template @@ -32,28 +34,9 @@ using contiguous_view_t = contiguous_view::type; template auto allocate_contiguous_for(const Exec& exec, const std::string& label, const View& v) -> contiguous_view_t { // Unpack `v` extents into the `ContigView` constructor - return [&label, &exec, &v ](std::index_sequence) { + return [&label, &exec, &v](std::index_sequence) { return contiguous_view_t(Kokkos::view_alloc(exec, Kokkos::WithoutInitializing, label), v.extent(Is)...); - } - (std::make_index_sequence()>{}); -} - -/// @brief Resize a View into a contiguous one suitable for packing a non-contiguous View. -/// @tparam Exec A Kokkos Execution Space type. -/// @tparam DstV The Kokkos View type of the destination View to resize. -/// @tparam DstV The Kokkos View type of the source View to resize for. -/// @param exec The execution space instance in which to perform the view reallocation. -/// @param dst The View to resize. -/// @param src The View to make a suitable contiguous resize for. -template -auto resize_contiguous_for(const Exec& exec, const DstV& dst, const SrcV& src) -> void { - static_assert(rank() == rank(), - "KokkosComm::Impl::resize_contiguous_for: source and destination Views must have the same rank"); - // Unpack `src` extents into `realloc` call - [&exec, &dst, &src ](std::index_sequence) { - Kokkos::realloc(Kokkos::view_alloc(exec, Kokkos::WithoutInitializing), dst, src.extent(Is)...); - } - (std::make_index_sequence()>{}); + }(std::make_index_sequence()>{}); } } // namespace KokkosComm::Impl From 787fad6434bde1513352287bb99d7328b657a807 Mon Sep 17 00:00:00 2001 From: Gabriel Dos Santos Date: Thu, 2 Apr 2026 21:05:39 +0200 Subject: [PATCH 09/10] tests(core): add unit tests for generic `contiguous` iface --- unit_tests/CMakeLists.txt | 1 + unit_tests/test_contiguous.cpp | 65 ++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 unit_tests/test_contiguous.cpp diff --git a/unit_tests/CMakeLists.txt b/unit_tests/CMakeLists.txt index 08d81999..4f0f5459 100644 --- a/unit_tests/CMakeLists.txt +++ b/unit_tests/CMakeLists.txt @@ -56,6 +56,7 @@ kc_add_unit_test(test.core.all-to-all CORE NUM_PES 2 FILES test_main.cpp test_al kc_add_unit_test(test.core.communicator CORE NUM_PES 2 FILES test_main.cpp test_communicator.cpp) kc_add_unit_test(test.core.datatype-conv CORE NUM_PES 1 FILES test_main.cpp test_datatype_conversion.cpp) kc_add_unit_test(test.core.red-op-conv CORE NUM_PES 1 FILES test_main.cpp test_red_op_conversion.cpp) +kc_add_unit_test(test.core.contiguous CORE NUM_PES 2 FILES test_main.cpp test_contiguous.cpp) # --- MPI backend unit tests --- # if(KokkosComm_ENABLE_MPI) diff --git a/unit_tests/test_contiguous.cpp b/unit_tests/test_contiguous.cpp new file mode 100644 index 00000000..141f62e4 --- /dev/null +++ b/unit_tests/test_contiguous.cpp @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// SPDX-FileCopyrightText: Copyright Contributors to the Kokkos project + +#include + +#include + +#include +#include "KokkosComm/concepts.hpp" +#include "KokkosComm/traits.hpp" +#include "view_utils.hpp" + +namespace { + +template +void test_contiguous_allocate(const View& non_contig) { + // If `non_contig` isn't rank-0 (i.e., a scalar), assert that it is indeed non-contiguous + if constexpr (View::rank >= 1) { + EXPECT_FALSE(KokkosComm::is_contiguous(non_contig)); + } + + auto exec = Kokkos::DefaultExecutionSpace{}; + auto contig = KokkosComm::Impl::allocate_contiguous_for(exec, "contig", non_contig); + exec.fence(); + + // Rank should be preserved + static_assert(decltype(contig)::rank == View::rank, ""); + + // Memory space should be preserved + static_assert(std::is_same_v, ""); + + // Size should be preserved + EXPECT_EQ(contig.size(), non_contig.size()); + + // Allocation should be contiguous + EXPECT_TRUE(KokkosComm::is_contiguous(contig)); +} + +template +using ct_usize = std::integral_constant; + +template +class Contiguous : public ::testing::Test { + public: + static constexpr size_t rank = R::value; +}; +// Only test up to rank-7 so we can build a non-contiguous View (Kokkos limits View rank to 8) +using Ranks = ::testing:: + Types, ct_usize<1>, ct_usize<2>, ct_usize<3>, ct_usize<4>, ct_usize<5>, ct_usize<6>, ct_usize<7>>; + +TYPED_TEST_SUITE(Contiguous, Ranks); + +TYPED_TEST(Contiguous, Allocate) { + constexpr size_t R = TestFixture::rank; + constexpr size_t N = 10; + auto non_contig = [R, N](std::index_sequence) { + return test_utils::build_view( + test_utils::NonContig{}, "non_contig", ((void)Is, std::integral_constant{})... + ); + }(std::make_index_sequence{}); + + test_contiguous_allocate(non_contig); +} + +} // namespace From c2642ccea02849519ea923231383b140216a4ee7 Mon Sep 17 00:00:00 2001 From: Gabriel Dos Santos Date: Thu, 2 Apr 2026 21:22:31 +0200 Subject: [PATCH 10/10] chore: format --- src/KokkosComm/impl/contiguous.hpp | 5 +++-- unit_tests/test_contiguous.cpp | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/KokkosComm/impl/contiguous.hpp b/src/KokkosComm/impl/contiguous.hpp index f0548500..c3f1d397 100644 --- a/src/KokkosComm/impl/contiguous.hpp +++ b/src/KokkosComm/impl/contiguous.hpp @@ -34,9 +34,10 @@ using contiguous_view_t = contiguous_view::type; template auto allocate_contiguous_for(const Exec& exec, const std::string& label, const View& v) -> contiguous_view_t { // Unpack `v` extents into the `ContigView` constructor - return [&label, &exec, &v](std::index_sequence) { + return [&label, &exec, &v ](std::index_sequence) { return contiguous_view_t(Kokkos::view_alloc(exec, Kokkos::WithoutInitializing, label), v.extent(Is)...); - }(std::make_index_sequence()>{}); + } + (std::make_index_sequence()>{}); } } // namespace KokkosComm::Impl diff --git a/unit_tests/test_contiguous.cpp b/unit_tests/test_contiguous.cpp index 141f62e4..216d902b 100644 --- a/unit_tests/test_contiguous.cpp +++ b/unit_tests/test_contiguous.cpp @@ -53,11 +53,12 @@ TYPED_TEST_SUITE(Contiguous, Ranks); TYPED_TEST(Contiguous, Allocate) { constexpr size_t R = TestFixture::rank; constexpr size_t N = 10; - auto non_contig = [R, N](std::index_sequence) { - return test_utils::build_view( + auto non_contig = [ R, N ](std::index_sequence) { + return test_utils::build_view( test_utils::NonContig{}, "non_contig", ((void)Is, std::integral_constant{})... ); - }(std::make_index_sequence{}); + } + (std::make_index_sequence{}); test_contiguous_allocate(non_contig); }