From b71af80fd4fd7ca5d6b33e371806dec2105ba084 Mon Sep 17 00:00:00 2001 From: Mark Hoemmen Date: Tue, 21 Mar 2023 10:37:42 -0600 Subject: [PATCH 1/5] Add for_each_extents example --- examples/CMakeLists.txt | 1 + examples/for_each_extents/CMakeLists.txt | 1 + .../for_each_extents/for_each_extents.cpp | 115 ++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 examples/for_each_extents/CMakeLists.txt create mode 100755 examples/for_each_extents/for_each_extents.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 32981b6c..4b9b45cd 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -16,3 +16,4 @@ add_subdirectory(dot_product) add_subdirectory(tiled_layout) add_subdirectory(restrict_accessor) add_subdirectory(aligned_accessor) +add_subdirectory(for_each_extents) diff --git a/examples/for_each_extents/CMakeLists.txt b/examples/for_each_extents/CMakeLists.txt new file mode 100644 index 00000000..b8f9b3b9 --- /dev/null +++ b/examples/for_each_extents/CMakeLists.txt @@ -0,0 +1 @@ +mdspan_add_example(for_each_extents) diff --git a/examples/for_each_extents/for_each_extents.cpp b/examples/for_each_extents/for_each_extents.cpp new file mode 100755 index 00000000..45bb073d --- /dev/null +++ b/examples/for_each_extents/for_each_extents.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include + +// "gcc trunk" on godbolt.org as of 2023/03/21 +// (> 12.2) does not define __cpp_lib_ranges_iota, +// yet std::views::iota works just fine. +#if defined(__cpp_lib_ranges_cartesian_product) // && defined(__cpp_lib_ranges_iota) +# define MDSPAN_EXAMPLE_CAN_USE_STD_RANGES 1 +#endif + +#if defined(MDSPAN_EXAMPLE_CAN_USE_STD_RANGES) + +// GCC >= 13 ("gcc trunk" on godbolt.org as of 2023/03/21) +// implements std::views::cartesian_product. +// If you don't have it, you can use range-v3 instead. +// Note that mixing std::views::iota with +// ranges::views::cartesian_product doesn't work. +// The range-v3 work-around looks like this. +// +// #include +// #include +// namespace ranges_views = ranges::views; + +#include +namespace ranges_views = std::views; + +namespace stdex = std::experimental; + +auto print_args = [] (Args&&... args) { + ((std::cout << std::forward(args) << '\n'), ...); +}; + +template +auto concat_index_sequence(std::index_sequence, + std::index_sequence) +{ + return std::index_sequence{}; +} + +auto reverse_index_sequence(std::index_sequence<> x) +{ + return x; +} + +template +auto reverse_index_sequence(std::index_sequence) +{ + return concat_index_sequence( + reverse_index_sequence(std::index_sequence{}), + std::index_sequence{}); +} + +template +auto make_reverse_index_sequence() +{ + return reverse_index_sequence(std::make_index_sequence()); +} + +template +void for_each_in_extents_impl(Callable&& f, + stdex::extents e, + std::index_sequence rank_sequence) +{ + // In the layout_left case, caller passes in N-1, N-2, ..., 1, 0. + // This reverses the order of the Cartesian product, + // but also reverses the order of indices in each tuple. + [&] (std::index_sequence) { + auto v = std::views::cartesian_product( + std::views::iota(IndexType(0), e.extent(Indices))...); + for (const auto& tuple_of_indices : v) { + // In the layout_left case, we undo the reversal of each tuple + // by getting its elements in reverse order. + [&] (std::index_sequence) { + std::forward(f)(std::get(tuple_of_indices)...); + } (rank_sequence); + } + } (rank_sequence); +} + +template +void for_each_in_extents(Callable&& f, + stdex::extents e, + Layout) +{ + using layout_type = std::remove_cvref_t; + if constexpr (std::is_same_v) { + for_each_in_extents_impl(std::forward(f), e, + make_reverse_index_sequence()); + } + else { // layout_right or any other layout + for_each_in_extents_impl(std::forward(f), e, + std::make_index_sequence()); + } +} + +#endif // defined(MDSPAN_EXAMPLE_CAN_USE_STD_RANGES) + +int main() { + +#if defined(MDSPAN_EXAMPLE_CAN_USE_STD_RANGES) + stdex::extents e; + auto printer = [] (int i, int j) { + std::cout << "(" << i << "," << j << ")\n"; + }; + std::cout << "layout_right:\n"; + for_each_in_extents(printer, e, stdex::layout_right{}); + std::cout << "\nlayout_left:\n"; + for_each_in_extents(printer, e, stdex::layout_left{}); +#endif // defined(MDSPAN_EXAMPLE_CAN_USE_STD_RANGES) + + return 0; +} From 3e43edf7f07faa1e0b14a1c327efb3bb5dc6878b Mon Sep 17 00:00:00 2001 From: Mark Hoemmen Date: Tue, 21 Mar 2023 11:24:42 -0600 Subject: [PATCH 2/5] Add for_each_extents example that doesn't need ranges --- examples/for_each_extents/CMakeLists.txt | 1 + .../for_each_extents_no_ranges.cpp | 365 ++++++++++++++++++ 2 files changed, 366 insertions(+) create mode 100755 examples/for_each_extents/for_each_extents_no_ranges.cpp diff --git a/examples/for_each_extents/CMakeLists.txt b/examples/for_each_extents/CMakeLists.txt index b8f9b3b9..719e1ebd 100644 --- a/examples/for_each_extents/CMakeLists.txt +++ b/examples/for_each_extents/CMakeLists.txt @@ -1 +1,2 @@ mdspan_add_example(for_each_extents) +mdspan_add_example(for_each_extents_no_ranges) diff --git a/examples/for_each_extents/for_each_extents_no_ranges.cpp b/examples/for_each_extents/for_each_extents_no_ranges.cpp new file mode 100755 index 00000000..84bb9339 --- /dev/null +++ b/examples/for_each_extents/for_each_extents_no_ranges.cpp @@ -0,0 +1,365 @@ +#include +#include +#include +#include +#include + +namespace stdex = std::experimental; + +// There's no separate feature test macro for the C++20 feature +// of lambdas with named template parameters (P0428R2). +#if __cplusplus >= 202002L +# define MDSPAN_EXAMPLE_CAN_USE_LAMBDA_TEMPLATE_PARAM_LIST 1 +#endif + +#if defined(MDSPAN_EXAMPLE_CAN_USE_LAMBDA_TEMPLATE_PARAM_LIST) + +////////////////////////////////////////////////////////////////////////// +// Part 1: Compile-time iteration +////////////////////////////////////////////////////////////////////////// + +// C++20 lets you write lambdas with explicitly named template +// parameters (vs. C++14 lambdas with "auto" parameters). If you have +// a lambda templated on that takes a +// std::index_sequence parameter, you can then call the +// lambda with the result of std::make_index_sequence. This calls +// the lambda with the template arguments 0, 1, 2, ..., N-1. You can +// then use these as "loop indices" to "iterate" at compile time over +// a parameter pack. +// +// If you don't have C++20, you can replace the lambda with a +// separate, named helper function. +// +// Another approach would be to write a reusable "template for each". +// This example doesn't do that, but it could be useful for +// backporting or for documenting intent. + +// Print all the elements of a parameter pack. +// +// This is a lambda and not a function because you can't +// straightforwardly use std::apply on templated nonmember functions +// (as it doesn't know which overload to use), but you can use it on +// generic lambdas. See the example here: +// +// https://en.cppreference.com/w/cpp/utility/apply +auto print_pack = [](InputTypes&& ... input) { + auto print_all = [&]( std::index_sequence ) { + auto print_one = [&] (std::size_t index, auto&& in) { + std::cout << in; + if(index + 1 < sizeof...(Indices)) { + std::cout << ", "; + } + }; + (print_one(Indices, input), ...); + }; + std::cout << '('; + print_all(std::make_index_sequence()); + std::cout << ")\n"; +}; + +////////////////////////////////////////////////////////////////////////// +// Part 2: Splitting extents +// +// extents is part of +// mdspan. It can mix run-time and compile-time extents values. +// +// We can express multidimensional iteration recursively +// by splitting an extents object into two parts (left and right), +// and iterating over one part while fixing the other. +////////////////////////////////////////////////////////////////////////// + +// Returns a new extents object representing +// all but the leftmost extent of e. +// +// extents -> extents +// extents -> extents +// +// This example shows that you can do +// index arithmetic on an index sequence. +template +auto right_extents( stdex::extents e ) +{ + static_assert(sizeof...(Extents) != 0); + return [&]( std::index_sequence ) { + return stdex::extents{ + e.extent(Indices + 1)... + }; + }( std::make_index_sequence() ); +} + +// Return two things: +// +// * the leftmost extent as an extents object, and +// * all the other (right) extents as a (single) extents object. +// +// Encoding the leftmost extent as an extents object +// lets us preserve its compile-time-ness. +// +// This needs to be a lambda or function object, +// not a templated function. +auto split_extents_at_leftmost = + [](stdex::extents e) +{ + static_assert(sizeof...(Extents) != 0); + stdex::extents left_ext( + e.extent(0)); + return std::tuple{left_ext, right_extents(e)}; +}; + +// right_extents can be implemented by overloading for +// extents. +// That approach doesn't work for left_extents. + +// Returns a new extents object representing +// all but the rightmost extent of e. +template +auto left_extents( stdex::extents e ) +{ + static_assert(sizeof...(Extents) != 0); + return [&]( std::index_sequence ) { + return stdex::extents{ + e.extent(Indices)... + }; + }( std::make_index_sequence() ); +} + +// This needs to be a lambda or function object, not a templated function. +auto split_extents_at_rightmost = + [](stdex::extents e) +{ + static_assert(sizeof...(Extents) != 0); + stdex::extents right_ext( + e.extent(e.rank() - 1)); + return std::tuple{left_extents(e), right_ext}; +}; + +////////////////////////////////////////////////////////////////////////// +// Part 3: Recursing on extents +////////////////////////////////////////////////////////////////////////// + +// This is a loop over one extent (dimension). +// By packaging up lambdas that fix other extents, +// we can use this as a building block +// for iterating over all the extents of a multidimensional array. +// +// This could also serve as a hook for passing along +// optimization information -- e.g., whether we want +// to apply "#pragma omp simd" to a particular extent. +template +void for_each_one_extent(Callable&& callable, stdex::extents ext) +{ + // If it's a run-time extent, do a run-time loop. + if constexpr(ext.static_extent(0) == stdex::dynamic_extent) { + for(IndexType index = 0; index < ext.extent(0); ++index) { + std::forward(callable)(index); + } + } + else { + // It's a compile-time extent, so use a fold expression + // to "iterate at compile time." + // This effectively unrolls the loop. + // + // Since we know the extent at compile time, + // we could also apply other optimizations here, + // like unrolling for specific SIMD widths. + [&] ( std::index_sequence ) { + (std::forward(callable)(Indices), ...); + }( std::make_index_sequence() ); + } +} + +// Call callable on each multidimensional index in the extents, +// iterating in row-major order. +template +void for_each_in_extents_row_major( + Callable&& callable, + stdex::extents ext) +{ + if constexpr(ext.rank() == 0) { + return; + } else if constexpr(ext.rank() == 1) { + for_each_one_extent(callable, ext); + } else { + auto [left_exts, right_exts] = split_extents_at_leftmost(ext); + auto inner = [&](auto... left_indices) { + auto next = [&] (auto... right_indices) { + // left_indices is really only one index here, + // but it's still a parameter pack. + // Writing the code this way suggests a more general approach. + std::forward(callable)(left_indices..., right_indices...); + }; + for_each_in_extents_row_major(next, right_exts); + }; + for_each_one_extent(inner, left_exts); + } +} + +// Call callable on each multidimensional index in the extents, +// iterating in column-major order. +// +// The implementation differs in only two places from the row-major version. +// This suggests a way to generalize. +// +// Overloading on stdex::extents +// works fine for the row major case, but not for the column major case. +template +void for_each_in_extents_col_major( + Callable&& callable, + stdex::extents ext) +{ + if constexpr(ext.rank() == 0) { + return; + } else if constexpr (ext.rank() == 1) { + for_each_one_extent(callable, ext); + } else { + // 1. Split rightmost instead of leftmost. + auto [left_exts, right_exts] = split_extents_at_rightmost(ext); + auto inner = [&](auto... right_indices) { + // 2. Put the left indices in the inner loop, + // instead of the right indices. + auto next = [&] (auto... left_indices) { + std::forward(callable)(left_indices..., right_indices...); + }; + for_each_in_extents_col_major(next, left_exts); + }; + for_each_in_extents_col_major(inner, right_exts); + } +} + +////////////////////////////////////////////////////////////////////////// +// Part 4: Generalize iteration order +////////////////////////////////////////////////////////////////////////// + +// We revise the above example +// by picking one iteration order as canonical +// (we've chosen row-major order above), +// and implementing other orders +// by changing the orders of extents and indices. + +template +void for_each_in_extents_impl(Callable&& callable, + stdex::extents ext, + ExtentsReorderer reorder_extents, + ExtentsSplitter split_extents, + IndicesReorderer reorder_indices) +{ + if constexpr(ext.rank() == 0) { + return; + } else if constexpr(ext.rank() == 1) { + for_each_one_extent(callable, ext); + } else { + // 1. Reorder the input extents. + auto reordered_extents = reorder_extents(ext); + + // 2. Split into "left" and "right." + // For row-major and column-major, the resulting left_exts + // should always have rank 1 (i.e., only contain one extent). + auto [left_exts, right_exts] = split_extents(reordered_extents); + + // 3. Create a lambda that loops over the right extents, + // and takes the left extent(s) as input. + auto inner = [&] (auto... left_indices) { + auto next = [&] (auto... right_indices) { + // 4. "Fix" the order of indices to match + // the above reordering of extents. + std::apply(std::forward(callable), + reorder_indices(left_indices..., right_indices...)); + }; + for_each_in_extents_impl(next, right_exts, reorder_extents, + split_extents, reorder_indices); + }; + + // 5. Take the above lambda and loop over the left extent(s). + for_each_in_extents_impl(inner, left_exts, reorder_extents, + split_extents, reorder_indices); + } +} + +auto extents_identity = []( + stdex::extents ext) +{ + return ext; +}; + +auto extents_reverse = []( + stdex::extents ext) +{ + constexpr std::size_t N = ext.rank(); + + return [&]( std::index_sequence ) { + return stdex::extents< + IndexType, + ext.static_extent(N - 1 - Indices)... + >{ ext.extent(N - 1 - Indices)... }; + }( std::make_index_sequence() ); +}; + +// Return a parameter pack as a tuple. +auto indices_identity = [](auto... indices) { + return std::tuple{indices...}; +}; + +// Get the n-th item in a parameter pack, +// where n is a compile-time value. +template +auto get_pack(Args... args) +{ + std::common_type_t result; + std::size_t i = 0; + return ((i++ == n ? (result = args, true) : false) || ...); +} + +// Return the reverse of a parameter pack as a std::tuple. +auto indices_reverse = [](auto... args) { + constexpr std::size_t N = sizeof...(args); + + return [&]( std::index_sequence ) { + return std::tuple{ get_pack(args)... }; + }( std::make_index_sequence() ); +}; + +// Row-major iteration +template +void for_each_in_extents(Callable&& callable, + stdex::extents ext, + stdex::layout_right) +{ + for_each_in_extents_impl(std::forward(callable), ext, + extents_identity, split_extents_at_leftmost, indices_identity); +} + +// Column-major iteration +template +void for_each_in_extents(Callable&& callable, + stdex::extents ext, + stdex::layout_left) +{ + for_each_in_extents_impl(std::forward(callable), ext, + extents_reverse, split_extents_at_rightmost, indices_reverse); +} + +#endif // defined(MDSPAN_EXAMPLE_CAN_USE_LAMBDA_TEMPLATE_PARAM_LIST) + +int main() { + +#if defined(MDSPAN_EXAMPLE_CAN_USE_LAMBDA_TEMPLATE_PARAM_LIST) + // The functions work for any combination + // of compile-time or run-time extents. + stdex::extents e{4}; + + std::cout << "\nRow major:\n"; + for_each_in_extents_row_major(print_pack, e); + + std::cout << "\nColumn major\n"; + for_each_in_extents_col_major(print_pack, e); + + std::cout << "\nfor_each_in_extents: row major:\n"; + for_each_in_extents(print_pack, e, stdex::layout_right{}); + + std::cout << "\nfor_each_in_extents: column major:\n"; + for_each_in_extents(print_pack, e, stdex::layout_left{}); +#endif // defined(MDSPAN_EXAMPLE_CAN_USE_LAMBDA_TEMPLATE_PARAM_LIST) + + return 0; +} From 62f409e2dd87a1791c05ae6f1dbb008a4bf998ec Mon Sep 17 00:00:00 2001 From: Mark Hoemmen Date: Tue, 21 Mar 2023 11:31:37 -0600 Subject: [PATCH 3/5] Disable example for Clang --- examples/for_each_extents/for_each_extents_no_ranges.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/for_each_extents/for_each_extents_no_ranges.cpp b/examples/for_each_extents/for_each_extents_no_ranges.cpp index 84bb9339..6d91afec 100755 --- a/examples/for_each_extents/for_each_extents_no_ranges.cpp +++ b/examples/for_each_extents/for_each_extents_no_ranges.cpp @@ -12,7 +12,10 @@ namespace stdex = std::experimental; # define MDSPAN_EXAMPLE_CAN_USE_LAMBDA_TEMPLATE_PARAM_LIST 1 #endif -#if defined(MDSPAN_EXAMPLE_CAN_USE_LAMBDA_TEMPLATE_PARAM_LIST) +// This example doesn't currently work with Clang, +// because Clang doesn't like structured binding results +// being captured by inner lambdas. +#if ! defined(__clang__) && defined(MDSPAN_EXAMPLE_CAN_USE_LAMBDA_TEMPLATE_PARAM_LIST) ////////////////////////////////////////////////////////////////////////// // Part 1: Compile-time iteration @@ -339,11 +342,11 @@ void for_each_in_extents(Callable&& callable, extents_reverse, split_extents_at_rightmost, indices_reverse); } -#endif // defined(MDSPAN_EXAMPLE_CAN_USE_LAMBDA_TEMPLATE_PARAM_LIST) +#endif // ! defined(__clang__) && defined(MDSPAN_EXAMPLE_CAN_USE_LAMBDA_TEMPLATE_PARAM_LIST) int main() { -#if defined(MDSPAN_EXAMPLE_CAN_USE_LAMBDA_TEMPLATE_PARAM_LIST) +#if ! defined(__clang__) && defined(MDSPAN_EXAMPLE_CAN_USE_LAMBDA_TEMPLATE_PARAM_LIST) // The functions work for any combination // of compile-time or run-time extents. stdex::extents e{4}; From 457b822fece503567754db574fff0e004e5e373f Mon Sep 17 00:00:00 2001 From: Mark Hoemmen Date: Tue, 2 Apr 2024 16:26:12 -0600 Subject: [PATCH 4/5] Update due to namespace change; apply review suggestion --- .../for_each_extents/for_each_extents.cpp | 50 ++++++------------ .../for_each_extents_no_ranges.cpp | 52 +++++++++---------- 2 files changed, 40 insertions(+), 62 deletions(-) diff --git a/examples/for_each_extents/for_each_extents.cpp b/examples/for_each_extents/for_each_extents.cpp index 45bb073d..ecbcf426 100755 --- a/examples/for_each_extents/for_each_extents.cpp +++ b/examples/for_each_extents/for_each_extents.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -26,42 +26,22 @@ #include namespace ranges_views = std::views; -namespace stdex = std::experimental; - auto print_args = [] (Args&&... args) { ((std::cout << std::forward(args) << '\n'), ...); }; -template -auto concat_index_sequence(std::index_sequence, - std::index_sequence) -{ - return std::index_sequence{}; -} - -auto reverse_index_sequence(std::index_sequence<> x) -{ - return x; -} +template +auto reverse(std::index_sequence) -> + std::index_sequence; -template -auto reverse_index_sequence(std::index_sequence) -{ - return concat_index_sequence( - reverse_index_sequence(std::index_sequence{}), - std::index_sequence{}); -} - -template -auto make_reverse_index_sequence() -{ - return reverse_index_sequence(std::make_index_sequence()); -} +template +using reverse_index_sequence_t = + decltype(reverse(std::make_index_sequence())); template void for_each_in_extents_impl(Callable&& f, - stdex::extents e, + Kokkos::extents e, std::index_sequence rank_sequence) { // In the layout_left case, caller passes in N-1, N-2, ..., 1, 0. @@ -82,17 +62,17 @@ void for_each_in_extents_impl(Callable&& f, template void for_each_in_extents(Callable&& f, - stdex::extents e, + Kokkos::extents e, Layout) { using layout_type = std::remove_cvref_t; - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { for_each_in_extents_impl(std::forward(f), e, - make_reverse_index_sequence()); + reverse_index_sequence_t{}); } else { // layout_right or any other layout for_each_in_extents_impl(std::forward(f), e, - std::make_index_sequence()); + reverse_index_sequence_t{}); } } @@ -101,14 +81,14 @@ void for_each_in_extents(Callable&& f, int main() { #if defined(MDSPAN_EXAMPLE_CAN_USE_STD_RANGES) - stdex::extents e; + Kokkos::extents e; auto printer = [] (int i, int j) { std::cout << "(" << i << "," << j << ")\n"; }; std::cout << "layout_right:\n"; - for_each_in_extents(printer, e, stdex::layout_right{}); + for_each_in_extents(printer, e, Kokkos::layout_right{}); std::cout << "\nlayout_left:\n"; - for_each_in_extents(printer, e, stdex::layout_left{}); + for_each_in_extents(printer, e, Kokkos::layout_left{}); #endif // defined(MDSPAN_EXAMPLE_CAN_USE_STD_RANGES) return 0; diff --git a/examples/for_each_extents/for_each_extents_no_ranges.cpp b/examples/for_each_extents/for_each_extents_no_ranges.cpp index 6d91afec..e35f353d 100755 --- a/examples/for_each_extents/for_each_extents_no_ranges.cpp +++ b/examples/for_each_extents/for_each_extents_no_ranges.cpp @@ -1,11 +1,9 @@ -#include +#include #include #include #include #include -namespace stdex = std::experimental; - // There's no separate feature test macro for the C++20 feature // of lambdas with named template parameters (P0428R2). #if __cplusplus >= 202002L @@ -80,11 +78,11 @@ auto print_pack = [](InputTypes&& ... input) { // This example shows that you can do // index arithmetic on an index sequence. template -auto right_extents( stdex::extents e ) +auto right_extents( Kokkos::extents e ) { static_assert(sizeof...(Extents) != 0); return [&]( std::index_sequence ) { - return stdex::extents{ + return Kokkos::extents{ e.extent(Indices + 1)... }; }( std::make_index_sequence() ); @@ -101,10 +99,10 @@ auto right_extents( stdex::extents e ) // This needs to be a lambda or function object, // not a templated function. auto split_extents_at_leftmost = - [](stdex::extents e) + [](Kokkos::extents e) { static_assert(sizeof...(Extents) != 0); - stdex::extents left_ext( + Kokkos::extents left_ext( e.extent(0)); return std::tuple{left_ext, right_extents(e)}; }; @@ -116,11 +114,11 @@ auto split_extents_at_leftmost = // Returns a new extents object representing // all but the rightmost extent of e. template -auto left_extents( stdex::extents e ) +auto left_extents( Kokkos::extents e ) { static_assert(sizeof...(Extents) != 0); return [&]( std::index_sequence ) { - return stdex::extents{ + return Kokkos::extents{ e.extent(Indices)... }; }( std::make_index_sequence() ); @@ -128,10 +126,10 @@ auto left_extents( stdex::extents e ) // This needs to be a lambda or function object, not a templated function. auto split_extents_at_rightmost = - [](stdex::extents e) + [](Kokkos::extents e) { static_assert(sizeof...(Extents) != 0); - stdex::extents right_ext( + Kokkos::extents right_ext( e.extent(e.rank() - 1)); return std::tuple{left_extents(e), right_ext}; }; @@ -149,10 +147,10 @@ auto split_extents_at_rightmost = // optimization information -- e.g., whether we want // to apply "#pragma omp simd" to a particular extent. template -void for_each_one_extent(Callable&& callable, stdex::extents ext) +void for_each_one_extent(Callable&& callable, Kokkos::extents ext) { // If it's a run-time extent, do a run-time loop. - if constexpr(ext.static_extent(0) == stdex::dynamic_extent) { + if constexpr(ext.static_extent(0) == Kokkos::dynamic_extent) { for(IndexType index = 0; index < ext.extent(0); ++index) { std::forward(callable)(index); } @@ -176,7 +174,7 @@ void for_each_one_extent(Callable&& callable, stdex::extents template void for_each_in_extents_row_major( Callable&& callable, - stdex::extents ext) + Kokkos::extents ext) { if constexpr(ext.rank() == 0) { return; @@ -203,12 +201,12 @@ void for_each_in_extents_row_major( // The implementation differs in only two places from the row-major version. // This suggests a way to generalize. // -// Overloading on stdex::extents +// Overloading on extents // works fine for the row major case, but not for the column major case. template void for_each_in_extents_col_major( Callable&& callable, - stdex::extents ext) + Kokkos::extents ext) { if constexpr(ext.rank() == 0) { return; @@ -242,7 +240,7 @@ void for_each_in_extents_col_major( template void for_each_in_extents_impl(Callable&& callable, - stdex::extents ext, + Kokkos::extents ext, ExtentsReorderer reorder_extents, ExtentsSplitter split_extents, IndicesReorderer reorder_indices) @@ -280,18 +278,18 @@ void for_each_in_extents_impl(Callable&& callable, } auto extents_identity = []( - stdex::extents ext) + Kokkos::extents ext) { return ext; }; auto extents_reverse = []( - stdex::extents ext) + Kokkos::extents ext) { constexpr std::size_t N = ext.rank(); return [&]( std::index_sequence ) { - return stdex::extents< + return Kokkos::extents< IndexType, ext.static_extent(N - 1 - Indices)... >{ ext.extent(N - 1 - Indices)... }; @@ -325,8 +323,8 @@ auto indices_reverse = [](auto... args) { // Row-major iteration template void for_each_in_extents(Callable&& callable, - stdex::extents ext, - stdex::layout_right) + Kokkos::extents ext, + Kokkos::layout_right) { for_each_in_extents_impl(std::forward(callable), ext, extents_identity, split_extents_at_leftmost, indices_identity); @@ -335,8 +333,8 @@ void for_each_in_extents(Callable&& callable, // Column-major iteration template void for_each_in_extents(Callable&& callable, - stdex::extents ext, - stdex::layout_left) + Kokkos::extents ext, + Kokkos::layout_left) { for_each_in_extents_impl(std::forward(callable), ext, extents_reverse, split_extents_at_rightmost, indices_reverse); @@ -349,7 +347,7 @@ int main() { #if ! defined(__clang__) && defined(MDSPAN_EXAMPLE_CAN_USE_LAMBDA_TEMPLATE_PARAM_LIST) // The functions work for any combination // of compile-time or run-time extents. - stdex::extents e{4}; + Kokkos::extents e{4}; std::cout << "\nRow major:\n"; for_each_in_extents_row_major(print_pack, e); @@ -358,10 +356,10 @@ int main() { for_each_in_extents_col_major(print_pack, e); std::cout << "\nfor_each_in_extents: row major:\n"; - for_each_in_extents(print_pack, e, stdex::layout_right{}); + for_each_in_extents(print_pack, e, Kokkos::layout_right{}); std::cout << "\nfor_each_in_extents: column major:\n"; - for_each_in_extents(print_pack, e, stdex::layout_left{}); + for_each_in_extents(print_pack, e, Kokkos::layout_left{}); #endif // defined(MDSPAN_EXAMPLE_CAN_USE_LAMBDA_TEMPLATE_PARAM_LIST) return 0; From cf17078441b15574675f3f4d4e6b10079a37dadc Mon Sep 17 00:00:00 2001 From: Mark Hoemmen Date: Tue, 2 Apr 2024 16:36:47 -0600 Subject: [PATCH 5/5] Work around for broken Intel LLVM ranges --- examples/for_each_extents/for_each_extents.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/for_each_extents/for_each_extents.cpp b/examples/for_each_extents/for_each_extents.cpp index ecbcf426..b366dea4 100755 --- a/examples/for_each_extents/for_each_extents.cpp +++ b/examples/for_each_extents/for_each_extents.cpp @@ -6,7 +6,10 @@ // "gcc trunk" on godbolt.org as of 2023/03/21 // (> 12.2) does not define __cpp_lib_ranges_iota, // yet std::views::iota works just fine. -#if defined(__cpp_lib_ranges_cartesian_product) // && defined(__cpp_lib_ranges_iota) +// +// "icpx C++23" github.com/kokkos/mdspan test build +// has a broken header as of 2024/04/02. +#if defined(__cpp_lib_ranges_cartesian_product) && (! defined(__INTEL_LLVM_COMPILER)) // && defined(__cpp_lib_ranges_iota) # define MDSPAN_EXAMPLE_CAN_USE_STD_RANGES 1 #endif