From 9b6d560fed56592908afda8f92f932c7deef8153 Mon Sep 17 00:00:00 2001 From: Nicolas Morales Date: Mon, 15 Apr 2024 16:28:45 -0700 Subject: [PATCH 1/3] initial work on mdspan copy impl --- include/experimental/p3242_bits/copy.hpp | 131 +++++++++++++++++++++++ include/mdspan/mdspan.hpp | 1 + tests/CMakeLists.txt | 1 + tests/test_mdspan_copy_and_fill.cpp | 65 +++++++++++ 4 files changed, 198 insertions(+) create mode 100644 include/experimental/p3242_bits/copy.hpp create mode 100644 tests/test_mdspan_copy_and_fill.cpp diff --git a/include/experimental/p3242_bits/copy.hpp b/include/experimental/p3242_bits/copy.hpp new file mode 100644 index 00000000..cd027f72 --- /dev/null +++ b/include/experimental/p3242_bits/copy.hpp @@ -0,0 +1,131 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) 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. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER +#pragma once + +#include "../__p0009_bits/default_accessor.hpp" +#include "../__p0009_bits/extents.hpp" +#include "../__p0009_bits/layout_left.hpp" +#include "../__p0009_bits/layout_right.hpp" +#include "../__p0009_bits/mdspan.hpp" +#include +#include +#include +#include + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { +namespace detail { + +template +constexpr void apply_fun_over_extents(const Extents &ext, F &fun, + ArrayType &indices, + std::index_sequence<>) { + std::apply(fun, indices); +} + +template +constexpr void apply_fun_over_extents(const Extents &ext, F &fun, + ArrayType &indices, + std::index_sequence) { + using index_type = typename Extents::index_type; + for (index_type i = 0; i < ext.extent(R); ++i) { + indices[R] = i; + apply_fun_over_extents(ext, fun, indices, std::index_sequence{}); + } +} + +template struct make_reverse_index_sequence_impl; + +template +struct make_reverse_index_sequence_impl> { + using type = std::index_sequence<(N - 1 - Indices)...>; +}; + +template +using make_reverse_index_sequence = typename make_reverse_index_sequence_impl< + N, std::make_index_sequence>::type; + +template +struct mdspan_copy_impl { + using extents_type = typename DstMDSpanType::extents_type; + + static constexpr void copy_over_extents(const extents_type &ext, + const SrcMDSpanType &src, + const DstMDSpanType &dst) { + // Generic copy algorithm; this assumes a roughly layout-right traversal + // but this may not be cache efficient if we can't determine anything about + // the layout memory ordering + constexpr auto rank = extents_type::rank(); + auto indices = std::array{}; + apply_fun_over_extents( + ext, [&src, &dst](auto... idxs) { dst(idxs...) = src(idxs...); }, + indices, make_reverse_index_sequence{}); + } +}; + +template +struct mdspan_copy_impl< + mdspan>, + mdspan>, + void> { + using extents_type = DstExtents; + using src_mdspan_type = mdspan>; + using dst_mdspan_type = mdspan>; + + static constexpr void copy_over_extents(const extents_type &ext, + const src_mdspan_type &src, + const dst_mdspan_type &dst) { + std::memcpy(dst.data_handle(), src.data_handle(), dst.mapping().required_span_size() * sizeof(ElementType)); + } +}; + +template +struct mdspan_copy_impl< + mdspan>, + mdspan>, + void> { + using extents_type = DstExtents; + using src_mdspan_type = mdspan>; + using dst_mdspan_type = mdspan>; + + static constexpr void copy_over_extents(const extents_type &ext, + const src_mdspan_type &src, + const dst_mdspan_type &dst) { + std::memcpy(dst.data_handle(), src.data_handle(), dst.mapping().required_span_size() * sizeof(ElementType)); + } +}; +} // namespace detail + +template +void copy( + mdspan src, + mdspan + dst) { + using src_type = + mdspan; + using dst_type = + mdspan; + detail::mdspan_copy_impl::copy_over_extents(src.extents(), + src, dst); +} +} // namespace MDSPAN_IMPL_PROPOSED_NAMESPACE +} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/include/mdspan/mdspan.hpp b/include/mdspan/mdspan.hpp index 4a0e354f..fa06d212 100644 --- a/include/mdspan/mdspan.hpp +++ b/include/mdspan/mdspan.hpp @@ -37,6 +37,7 @@ #if MDSPAN_HAS_CXX_17 #include "../experimental/__p2642_bits/layout_padded.hpp" #include "../experimental/__p2630_bits/submdspan.hpp" +#include "../experimental/p3242_bits/copy.hpp" #endif #include "../experimental/__p2389_bits/dims.hpp" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1693ac65..4c5167f2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -99,6 +99,7 @@ if(NOT CMAKE_CXX_STANDARD STREQUAL "14") mdspan_add_test(test_submdspan_static_slice) mdspan_add_test(test_layout_padded_left ENABLE_PRECONDITIONS) mdspan_add_test(test_layout_padded_right ENABLE_PRECONDITIONS) + mdspan_add_test(test_mdspan_copy_and_fill) endif() # both of those don't work yet since its using vector if(NOT MDSPAN_ENABLE_CUDA AND NOT MDSPAN_ENABLE_HIP) diff --git a/tests/test_mdspan_copy_and_fill.cpp b/tests/test_mdspan_copy_and_fill.cpp new file mode 100644 index 00000000..6899c770 --- /dev/null +++ b/tests/test_mdspan_copy_and_fill.cpp @@ -0,0 +1,65 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) 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. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// See https://kokkos.org/LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER + +#include +#include + +#include + +template +void test_mdspan_copy_impl(const SrcExtents &src_exts, + const DstExtents &dst_exts) { + Kokkos::Experimental::mdarray src1{src_exts}; + Kokkos::Experimental::mdarray dst1{dst_exts}; + auto &src1c = src1.container(); + for (size_t i = 0; i < src1c.size(); ++i) + src1c[i] = static_cast(i * i); + + ASSERT_NE(dst1.container(), src1.container()); + Kokkos::Experimental::copy(src1.to_mdspan(), dst1.to_mdspan()); + ASSERT_EQ(dst1.container(), src1.container()); +} + +TEST(TestMdspanCopyAndFill, test_mdspan_copy) { + test_mdspan_copy_impl( + Kokkos::extents{}, Kokkos::extents{}); + test_mdspan_copy_impl( + Kokkos::extents{}, Kokkos::extents{}); + test_mdspan_copy_impl( + Kokkos::extents{}, Kokkos::extents{}); + + test_mdspan_copy_impl( + Kokkos::dextents{5, 3}, Kokkos::extents{}); + test_mdspan_copy_impl( + Kokkos::dextents{5, 3}, Kokkos::extents{}); + test_mdspan_copy_impl( + Kokkos::dextents{5, 3}, Kokkos::extents{}); + + test_mdspan_copy_impl( + Kokkos::extents{}, Kokkos::dextents{5, 3}); + test_mdspan_copy_impl( + Kokkos::extents{}, Kokkos::dextents{5, 3}); + test_mdspan_copy_impl( + Kokkos::extents{}, Kokkos::dextents{5, 3}); + + test_mdspan_copy_impl( + Kokkos::dextents{5, 3}, Kokkos::dextents{5, 3}); + test_mdspan_copy_impl( + Kokkos::dextents{5, 3}, Kokkos::dextents{5, 3}); + test_mdspan_copy_impl( + Kokkos::dextents{5, 3}, Kokkos::dextents{5, 3}); +} From 195a9228e001e33e6ed32105dfb28d95052fe64c Mon Sep 17 00:00:00 2001 From: Nicolas Morales Date: Mon, 15 Apr 2024 19:29:20 -0700 Subject: [PATCH 2/3] explicitly construct dst extents type --- include/experimental/p3242_bits/copy.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/experimental/p3242_bits/copy.hpp b/include/experimental/p3242_bits/copy.hpp index cd027f72..5f7a184f 100644 --- a/include/experimental/p3242_bits/copy.hpp +++ b/include/experimental/p3242_bits/copy.hpp @@ -124,7 +124,7 @@ void copy( mdspan; using dst_type = mdspan; - detail::mdspan_copy_impl::copy_over_extents(src.extents(), + detail::mdspan_copy_impl::copy_over_extents(DstExtents{src.extents()}, src, dst); } } // namespace MDSPAN_IMPL_PROPOSED_NAMESPACE From d4e67f917fa0cc56800a5f41c91c66a4b6b03103 Mon Sep 17 00:00:00 2001 From: Nicolas Morales Date: Fri, 27 Mar 2026 16:23:32 +0000 Subject: [PATCH 3/3] fix copy bugs, add constexpr test --- include/experimental/p3242_bits/copy.hpp | 45 ++++-------- tests/test_mdspan_copy_and_fill.cpp | 90 +++++++++++++++++++++--- 2 files changed, 96 insertions(+), 39 deletions(-) diff --git a/include/experimental/p3242_bits/copy.hpp b/include/experimental/p3242_bits/copy.hpp index 5f7a184f..ef7e3d0e 100644 --- a/include/experimental/p3242_bits/copy.hpp +++ b/include/experimental/p3242_bits/copy.hpp @@ -24,26 +24,27 @@ #include #include #include +#include namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { namespace detail { template -constexpr void apply_fun_over_extents(const Extents &ext, F &fun, +constexpr void apply_fun_over_extents(const Extents &ext, F &&fun, ArrayType &indices, std::index_sequence<>) { - std::apply(fun, indices); + std::apply(std::forward(fun), indices); } template -constexpr void apply_fun_over_extents(const Extents &ext, F &fun, +constexpr void apply_fun_over_extents(const Extents &ext, F &&fun, ArrayType &indices, std::index_sequence) { using index_type = typename Extents::index_type; for (index_type i = 0; i < ext.extent(R); ++i) { indices[R] = i; - apply_fun_over_extents(ext, fun, indices, std::index_sequence{}); + apply_fun_over_extents(ext, std::forward(fun), indices, std::index_sequence{}); } } @@ -58,7 +59,7 @@ template using make_reverse_index_sequence = typename make_reverse_index_sequence_impl< N, std::make_index_sequence>::type; -template +template struct mdspan_copy_impl { using extents_type = typename DstMDSpanType::extents_type; @@ -71,16 +72,16 @@ struct mdspan_copy_impl { constexpr auto rank = extents_type::rank(); auto indices = std::array{}; apply_fun_over_extents( - ext, [&src, &dst](auto... idxs) { dst(idxs...) = src(idxs...); }, - indices, make_reverse_index_sequence{}); + ext, [&src, &dst](auto... idxs) { dst[idxs...] = src[idxs...]; }, + indices, std::make_index_sequence{}); } }; -template +template + requires (SrcLayout::template mapping::is_always_exhaustive() && DstLayout::template mapping::is_always_exhaustive()) struct mdspan_copy_impl< - mdspan>, - mdspan>, - void> { + mdspan, + mdspan> { using extents_type = DstExtents; using src_mdspan_type = mdspan>; @@ -90,25 +91,7 @@ struct mdspan_copy_impl< static constexpr void copy_over_extents(const extents_type &ext, const src_mdspan_type &src, const dst_mdspan_type &dst) { - std::memcpy(dst.data_handle(), src.data_handle(), dst.mapping().required_span_size() * sizeof(ElementType)); - } -}; - -template -struct mdspan_copy_impl< - mdspan>, - mdspan>, - void> { - using extents_type = DstExtents; - using src_mdspan_type = mdspan>; - using dst_mdspan_type = mdspan>; - - static constexpr void copy_over_extents(const extents_type &ext, - const src_mdspan_type &src, - const dst_mdspan_type &dst) { - std::memcpy(dst.data_handle(), src.data_handle(), dst.mapping().required_span_size() * sizeof(ElementType)); + std::copy(src.data_handle(), src.data_handle() + src.mapping().required_span_size(), dst.data_handle()); } }; } // namespace detail @@ -116,7 +99,7 @@ struct mdspan_copy_impl< template -void copy( +constexpr void copy( mdspan src, mdspan dst) { diff --git a/tests/test_mdspan_copy_and_fill.cpp b/tests/test_mdspan_copy_and_fill.cpp index 6899c770..f71fb940 100644 --- a/tests/test_mdspan_copy_and_fill.cpp +++ b/tests/test_mdspan_copy_and_fill.cpp @@ -16,22 +16,93 @@ #include #include - +#include #include +template +constexpr auto make_mdarray(const Extents &exts) { + Kokkos::Experimental::mdarray mds{exts}; + for (size_t i = 0; i < mds.mapping().required_span_size(); ++i) + mds.data()[i] = static_cast(i); + + return mds; +} + +template +constexpr auto make_constexpr_array_impl(std::index_sequence) { + constexpr auto sz = (Extents::static_extent(Indices) * ...); + std::array ret; + for (size_t i = 0; i < sz; ++i) + ret[i] = static_cast(i); + + return ret; +} + +template +constexpr auto array_size_impl(std::index_sequence) { + return (Extents::static_extent(Indices) * ...); +} + + +template +constexpr size_t array_size() { + return array_size_impl(); +} + + +template +constexpr auto make_constexpr_array() { + return make_constexpr_array_impl(std::make_index_sequence{}); +} + +template +constexpr auto make_mdarray_copy(const Kokkos::mdspan &src) { + Kokkos::Experimental::mdarray dst{}; + Kokkos::Experimental::copy(src, dst.to_mdspan()); + + return dst; +} + +template +constexpr bool test_mdspan_copy_check(const SrcExtents &src_exts, + const DstExtents &dst_exts) { + Kokkos::Experimental::mdarray src1 = + make_mdarray(src_exts); + Kokkos::Experimental::mdarray dst1{dst_exts}; + + if (dst1.container() == src1.container()) return false; + Kokkos::Experimental::copy(src1.to_mdspan(), dst1.to_mdspan()); + return dst1.container() == src1.container(); +} + template void test_mdspan_copy_impl(const SrcExtents &src_exts, const DstExtents &dst_exts) { - Kokkos::Experimental::mdarray src1{src_exts}; - Kokkos::Experimental::mdarray dst1{dst_exts}; - auto &src1c = src1.container(); - for (size_t i = 0; i < src1c.size(); ++i) - src1c[i] = static_cast(i * i); + ASSERT_TRUE( + (test_mdspan_copy_check(src_exts, dst_exts))); +} + +template +constexpr bool test_mdspan_copy_constexpr_impl() { + auto arr = make_constexpr_array(); + Kokkos::Experimental::mdarray src1(SrcExtents{}, arr); + Kokkos::Experimental::mdarray dst1{}; - ASSERT_NE(dst1.container(), src1.container()); Kokkos::Experimental::copy(src1.to_mdspan(), dst1.to_mdspan()); - ASSERT_EQ(dst1.container(), src1.container()); + + for ( std::size_t i = 0; i < arr.size(); ++i ) + if (dst1.container()[i] != src1.container()[i]) + return false; + + return true; +} + +template +void test_mdspan_copy_constexpr() { + static_assert(test_mdspan_copy_constexpr_impl()); } TEST(TestMdspanCopyAndFill, test_mdspan_copy) { @@ -62,4 +133,7 @@ TEST(TestMdspanCopyAndFill, test_mdspan_copy) { Kokkos::dextents{5, 3}, Kokkos::dextents{5, 3}); test_mdspan_copy_impl( Kokkos::dextents{5, 3}, Kokkos::dextents{5, 3}); + + test_mdspan_copy_constexpr, Kokkos::layout_left, + Kokkos::extents, Kokkos::layout_left>(); }