Skip to content

Commit

Permalink
kokkos#6805: add initial converting constructor from mdspan to view
Browse files Browse the repository at this point in the history
  • Loading branch information
nmm0 committed May 15, 2024
1 parent df018d9 commit fafae27
Show file tree
Hide file tree
Showing 6 changed files with 273 additions and 0 deletions.
33 changes: 33 additions & 0 deletions core/src/Kokkos_View.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ static_assert(false,

#ifdef KOKKOS_ENABLE_IMPL_MDSPAN
#include <View/MDSpan/Kokkos_MDSpan_Extents.hpp>
#include <View/MDSpan/Kokkos_MDSpan_Layout.hpp>
#endif
#include <Kokkos_MinMax.hpp>

Expand Down Expand Up @@ -1722,6 +1723,38 @@ class View : public ViewTraits<DataType, Properties...> {
"Layout is not constructible from extent arguments. Use "
"overload taking a layout object instead.");
}

//----------------------------------------
// MDSpan converting constructors
#ifdef KOKKOS_ENABLE_IMPL_MDSPAN
#ifndef KOKKOS_IMPL_TEST_ACCESS
private:
#endif // KOKKOS_IMPL_TEST_ACCESS
template <class OtherExtents, class OtherLayoutPolicy, class Accessor>
static constexpr inline bool mdspan_conversion_constraints =
!traits::is_managed &&
std::is_constructible_v<typename traits::value_type,
typename Accessor::reference> &&
std::is_assignable_v<typename Accessor::reference,
typename traits::value_type> &&
std::is_default_constructible_v<typename traits::value_type> &&
std::is_constructible_v<
typename traits::array_layout,
const typename Experimental::Impl::ArrayLayoutFromLayout<
OtherLayoutPolicy>::type&>;

public:
template <class OtherElementType, class OtherExtents, class OtherLayoutPolicy,
class = std::enable_if_t<mdspan_conversion_constraints<
OtherExtents, OtherLayoutPolicy,
Kokkos::default_accessor<OtherElementType>>>>
MDSPAN_CONDITIONAL_EXPLICIT(
!(std::is_convertible_v<Kokkos::default_accessor,
typename traits::value_type>))
KOKKOS_INLINE_FUNCTION
View(mdspan<OtherElementType, OtherExtents, OtherLayoutPolicy> const& mds)
: m_track(), m_map(mds) {}
#endif // KOKKOS_ENABLE_IMPL_MDSPAN
};

template <typename D, class... P>
Expand Down
7 changes: 7 additions & 0 deletions core/src/View/MDSpan/Kokkos_MDSpan_Extents.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ struct DataTypeFromExtents {
// Will cause a compile error if it is malformed (i.e. dynamic after static)
using type = typename ::Kokkos::Impl::ViewDataType<T, dimension_type>::type;
};

/// Convert from a mdspan extent to a Kokkos extent, inserting 0s for static extents
template<class Extents>
auto dimension_from_extent(const Extents &e, std::size_t r) noexcept
{
return Extents::static_extent(r) == dynamic_extent ? e.extent(r) : 0;
}
} // namespace Kokkos::Experimental::Impl

#endif // KOKKOS_EXPERIMENTAL_MDSPAN_EXTENTS_HPP
93 changes: 93 additions & 0 deletions core/src/View/MDSpan/Kokkos_MDSpan_Layout.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//@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

#ifndef KOKKOS_IMPL_PUBLIC_INCLUDE
static_assert(false,
"Including non-public Kokkos header files is not allowed.");
#endif

#ifndef KOKKOS_EXPERIMENTAL_MDSPAN_LAYOUT_HPP
#define KOKKOS_EXPERIMENTAL_MDSPAN_LAYOUT_HPP

#include "Kokkos_MDSpan_Extents.hpp"
#include <Kokkos_Core_fwd.hpp>
#include <impl/Kokkos_ViewDataAnalysis.hpp>

namespace Kokkos::Experimental::Impl {
template <class Layout>
struct ArrayLayoutFromLayout;

template <std::size_t padding_value>
struct ArrayLayoutFromLayout<Experimental::layout_left_padded<padding_value>> {
using type = Kokkos::LayoutLeft;
static constexpr std::integral_constant<unsigned,
static_cast<unsigned>(padding_value)>
padding = {};
};

template <std::size_t padding_value>
struct ArrayLayoutFromLayout<Experimental::layout_right_padded<padding_value>> {
using type = Kokkos::LayoutRight;
static constexpr std::integral_constant<unsigned,
static_cast<unsigned>(padding_value)>
padding = {};
};

template <class T, class Extents, class Layout>
struct ViewOffsetFromExtents {
using value_type = T;
using data_type = typename DataTypeFromExtents<value_type, Extents>::type;
using array_layout = typename ArrayLayoutFromLayout<Layout>::type;
using data_analysis =
Kokkos::Impl::ViewDataAnalysis<data_type, array_layout, value_type>;
using type =
Kokkos::Impl::ViewOffset<typename data_analysis::dimension, array_layout>;
};

template <class ElementType, class Extents, class LayoutPolicy,
class AccessorPolicy>
auto array_layout_from_mdspan(
const mdspan<ElementType, Extents, LayoutPolicy, AccessorPolicy> &mds) {
using layout_type = typename ArrayLayoutFromLayout<LayoutPolicy>::type;
const auto &ext = mds.extents();

static constexpr auto rank = Extents::rank();

static_assert(rank <= ARRAY_LAYOUT_MAX_RANK,
"Unsupported rank for mdspan (must be <= 8)");
return layout_type{
rank > 0 ? dimension_from_extent(ext, 0) : KOKKOS_IMPL_CTOR_DEFAULT_ARG,
rank > 1 ? dimension_from_extent(ext, 1) : KOKKOS_IMPL_CTOR_DEFAULT_ARG,
rank > 2 ? dimension_from_extent(ext, 2) : KOKKOS_IMPL_CTOR_DEFAULT_ARG,
rank > 3 ? dimension_from_extent(ext, 3) : KOKKOS_IMPL_CTOR_DEFAULT_ARG,
rank > 4 ? dimension_from_extent(ext, 4) : KOKKOS_IMPL_CTOR_DEFAULT_ARG,
rank > 5 ? dimension_from_extent(ext, 5) : KOKKOS_IMPL_CTOR_DEFAULT_ARG,
rank > 6 ? dimension_from_extent(ext, 6) : KOKKOS_IMPL_CTOR_DEFAULT_ARG,
rank > 7 ? dimension_from_extent(ext, 7) : KOKKOS_IMPL_CTOR_DEFAULT_ARG};
}

template <class ElementType, class Extents, class LayoutPolicy,
class AccessorPolicy>
auto view_offset_from_mdspan(
const mdspan<ElementType, Extents, LayoutPolicy, AccessorPolicy> &mds) {
using offset_type =
typename ViewOffsetFromExtents<ElementType, Extents, LayoutPolicy>::type;
static constexpr auto padding = ArrayLayoutFromLayout<LayoutPolicy>::padding;
return offset_type(padding, array_layout_from_mdspan(mds));
};
} // namespace Kokkos::Experimental::Impl

#endif // KOKKOS_EXPERIMENTAL_MDSPAN_LAYOUT_HPP
9 changes: 9 additions & 0 deletions core/src/impl/Kokkos_ViewMapping.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2971,6 +2971,15 @@ class ViewMapping<
: m_impl_handle(Impl::get_property<Impl::PointerTag>(arg_prop)),
m_impl_offset(std::integral_constant<unsigned, 0>(), arg_layout) {}

#ifdef KOKKOS_ENABLE_IMPL_MDSPAN
template <class OtherElementType, class OtherExtents, class OtherLayoutPolicy>
explicit KOKKOS_INLINE_FUNCTION ViewMapping(
Kokkos::mdspan<OtherElementType, OtherExtents, OtherLayoutPolicy> const&
mds)
: m_impl_handle(mds.data_handle()),
m_impl_offset(Experimental::Impl::view_offset_from_mdspan(mds)) {}
#endif

/**\brief Assign data */
KOKKOS_INLINE_FUNCTION
void assign_data(pointer_type arg_ptr) {
Expand Down
15 changes: 15 additions & 0 deletions core/unit_test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,21 @@ foreach(Tag Threads;Serial;OpenMP;Cuda;HPX;OpenMPTarget;OpenACC;HIP;SYCL)
list(APPEND ${Tag}_SOURCES2A ${file})
endforeach()

# View implementation tests
foreach(Name
MDSpanConversion
)
set(file ${dir}/view/Test${Tag}_${Name}.cpp)
# Write to a temporary intermediate file and call configure_file to avoid
# updating timestamps triggering unnecessary rebuilds on subsequent cmake runs.
file(WRITE ${dir}/dummy.cpp
"#include <Test${Tag}_Category.hpp>\n"
"#include <view/Test${Name}.hpp>\n"
)
configure_file(${dir}/dummy.cpp ${file})
list(APPEND ${Tag}_SOURCES2A ${file})
endforeach()

set(TagHostAccessible ${Tag})
if (Tag STREQUAL "Cuda")
set(TagHostAccessible CudaUVM)
Expand Down
116 changes: 116 additions & 0 deletions core/unit_test/view/TestMDSpanConversion.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//@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 <gtest/gtest.h>

#define KOKKOS_IMPL_TEST_ACCESS

#include <Kokkos_Core.hpp>

template <class T, class ExecutionSpace>
struct TestViewMDSpanConversion {
using value_type = T;

template <class MDSpanLayout, class KokkosLayout, class DataType,
class MDSpanExtents>
static void test_conversion_from_mdspan(Kokkos::View<DataType> ref,
const MDSpanExtents &exts) {
using view_type = Kokkos::View<DataType, KokkosLayout,
Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
using mdspan_type = Kokkos::mdspan<value_type, MDSpanExtents, MDSpanLayout>;

using converted_layout_type =
typename Kokkos::Experimental::Impl::ArrayLayoutFromLayout<
MDSpanLayout>::type;
static_assert(
std::is_constructible_v<typename view_type::traits::array_layout,
const converted_layout_type &>);
static_assert(
view_type::template mdspan_conversion_constraints<
MDSpanExtents, MDSpanLayout, Kokkos::default_accessor<value_type>>);

// Manually create an mdspan from ref so we have a valid pointer to play
// with
auto mds = mdspan_type{ref.data(), exts};

auto test_view = view_type(mds);

ASSERT_EQ(test_view.data(), ref.data());
ASSERT_EQ(test_view.data(), mds.data_handle());
for (std::size_t r = 0; r < mdspan_type::rank(); ++r) {
ASSERT_EQ(test_view.extent(r), ref.extent(r));
ASSERT_EQ(test_view.extent(r), exts.extent(r));
}
}

static void run_test() {
static_assert(std::is_same_v<
typename Kokkos::Experimental::Impl::ArrayLayoutFromLayout<
Kokkos::Experimental::layout_left_padded<sizeof(
value_type)>>::type,
Kokkos::LayoutLeft>);
static_assert(std::is_same_v<
typename Kokkos::Experimental::Impl::ArrayLayoutFromLayout<
Kokkos::Experimental::layout_right_padded<sizeof(
value_type)>>::type,
Kokkos::LayoutRight>);

test_conversion_from_mdspan<
Kokkos::Experimental::layout_left_padded<sizeof(value_type)>,
Kokkos::LayoutLeft>(
Kokkos::View<double *>("ref", 7),
Kokkos::extents<std::size_t, Kokkos::dynamic_extent>(7));
test_conversion_from_mdspan<
Kokkos::Experimental::layout_left_padded<sizeof(value_type)>,
Kokkos::LayoutLeft>(Kokkos::View<double[7]>("ref"),
Kokkos::extents<std::size_t, 7>());
test_conversion_from_mdspan<
Kokkos::Experimental::layout_left_padded<sizeof(value_type)>,
Kokkos::LayoutLeft>(
Kokkos::View<double[7]>("ref"),
Kokkos::extents<std::size_t, Kokkos::dynamic_extent>(7));
test_conversion_from_mdspan<
Kokkos::Experimental::layout_left_padded<sizeof(value_type)>,
Kokkos::LayoutLeft>(Kokkos::View<double *>("ref", 7),
Kokkos::extents<std::size_t, 7>());

test_conversion_from_mdspan<
Kokkos::Experimental::layout_left_padded<sizeof(value_type)>,
Kokkos::LayoutLeft>(Kokkos::View<double **>("ref", 7, 3),
Kokkos::dextents<std::size_t, 2>(7, 3));
test_conversion_from_mdspan<
Kokkos::Experimental::layout_left_padded<sizeof(value_type)>,
Kokkos::LayoutLeft>(Kokkos::View<double[7][3]>("ref"),
Kokkos::extents<std::size_t, 7, 3>());
test_conversion_from_mdspan<
Kokkos::Experimental::layout_left_padded<sizeof(value_type)>,
Kokkos::LayoutLeft>(Kokkos::View<double[7][3]>("ref"),
Kokkos::extents<std::size_t, Kokkos::dynamic_extent,
Kokkos::dynamic_extent>(7, 3));
test_conversion_from_mdspan<
Kokkos::Experimental::layout_left_padded<sizeof(value_type)>,
Kokkos::LayoutLeft>(Kokkos::View<double **>("ref", 7, 3),
Kokkos::extents<std::size_t, 7, 3>());
}
};

namespace Test {

TEST(TEST_CATEGORY, view_mdspan_conversion) {
TestViewMDSpanConversion<double, TEST_EXECSPACE>::run_test();
}

} // namespace Test

0 comments on commit fafae27

Please sign in to comment.