Skip to content

Conversation

@gberg617
Copy link
Contributor

@gberg617 gberg617 commented Sep 19, 2025

Summary

  • This WIP PR adds the SubView class. This class stores a view-type object and a tuple of slices that indicate how to access the view's underlying data. The following slice types are supported:
    • range slice: Indexes a subset of the original dimension's extents. Does not reduce dimensionality.
    • fixed slice: Indexes along a fixed value of a given dimension. Reduces dimensionality.
    • no slice: Indexes according to the original view's layout along the given dimension. Does not reduce dimensionality.

A few simple examples are provided below:

    View<Index_type, Layout<2>> view(&my_data[0][0], Layout<2>(3,3));

    // "sv1 = View[1:2,:]"
    auto sv1 = SubView(view, RangeSlice{1,2}, NoSlice{});

    // "sv2 = View[1:2,1]"
    auto sv2 = SubView(view, RangeSlice{1,2}, FixedSlice{1});

Here, sv1 and sv2 are subviews of dimensions (2,3) and (2) respectively.

@gberg617 gberg617 marked this pull request as draft September 19, 2025 21:12
@gberg617
Copy link
Contributor Author

This PR is still a WIP!

@gberg617 gberg617 self-assigned this Sep 19, 2025
Comment on lines 30 to 32
struct RangeSlice { long start, end; };
struct FixedSlice { long idx; };
struct NoSlice { };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will probably want to generalize the integer types used in the slices. We will probably want to add strongly typed slices like the strongly typed indices.

Comment on lines 42 to 49
RAJA_INLINE RAJA_HOST_DEVICE constexpr auto array_to_tuple_impl(const camp::array<T, N>& arr, camp::idx_seq<Is...>) {
return camp::make_tuple(arr[Is]...);
}

template <typename T, size_t N>
RAJA_INLINE RAJA_HOST_DEVICE constexpr auto array_to_tuple(const camp::array<T, N>& arr) {
return array_to_tuple_impl(arr, camp::make_idx_seq_t<N>{});
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we not have this somewhere?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If not it would be good to add it in a more general header.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I'll look into moving this to camp.

@gberg617 gberg617 force-pushed the feature/bergel1/subview branch from 804abcd to 5ba920d Compare September 30, 2025 01:08

}

// void test_subviewGPU() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To Do: Fix GPU tests.

@gberg617 gberg617 changed the title Feature/bergel1/subview Add SubView class Sep 30, 2025
Comment on lines 80 to 81
ViewType view_;
camp::tuple<Slices...> slices_;
Copy link
Member

@MrBurmark MrBurmark Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be a static assert that the dimensionality of the view and number of slices match?

EXPECT_EQ(sv(1,2), 9);

}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gberg617 how would you do something like we have in the original impl of SubViews: https://github.com/LLNL/SNLS/blob/develop/test/SNLS_forall_subviews.cxx#L69-L75 where we allow for a sliding window with the SubView. Additionally, how can we get the underlying data pointer at the SubView's (0..) index like here: https://github.com/LLNL/SNLS/blob/develop/test/SNLS_forall_subviews.cxx#L150 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added set_slice to allow things like "sliding windows"

class SubView<ViewType, camp::list<Slices...>, IndexType> {
ViewType view_;
camp::tuple<Slices...> slices_;
std::array<IndexType, sizeof...(Slices)> map_;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still need to make this static inline constexpr

size_t sub_idx = 0;
std::array<IndexType, sizeof...(Slices)> map;

for_each_tuple_index( slices_,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great. Unfortunately it uses a non-static member to do something static.


template <typename... Idxs>
RAJA_INLINE RAJA_HOST_DEVICE constexpr IndexType operator()(Idxs... idxs) const {
constexpr size_t nidx = ((Slices::reduces_dimension == false ? 1 : 0) + ...);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a static member variable of the class.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to have something like num_slices as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would also be nice to have a bit more descriptive names.


for_each_tuple_index( slices_,
[&](auto slice, auto index) {
parent_indices[index] = slice.map_index(arr[map_[index]]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This causes out of bounds accesses for slices that reduce dimensions.

parent_indices[index] = slice.map_index(arr[map_[index]]);
});

return camp::apply(view_, array_to_tuple(parent_indices));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use a tuple to start with?

{
using camp::get;
// braced init lists are evaluated in order
int seq_unused_array[] = {0, (func(get<Is>(std::forward<Tuple>(t)), Is), 0)...};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
int seq_unused_array[] = {0, (func(get<Is>(std::forward<Tuple>(t)), Is), 0)...};
int seq_unused_array[] = {0, (func(get<Is>(std::forward<Tuple>(t)), std::integral_constant<std::size_t, Is>{}), 0)...};

Comment on lines 44 to 45
template<typename UnaryFunc, typename IndexType, IndexType... Is>
RAJA_HOST_DEVICE RAJA_INLINE UnaryFunc for_each_index(camp::idx_seq<Is...>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
template<typename UnaryFunc, typename IndexType, IndexType... Is>
RAJA_HOST_DEVICE RAJA_INLINE UnaryFunc for_each_index(camp::idx_seq<Is...>,
template<typename UnaryFunc, camp::idx_t... Is>
RAJA_HOST_DEVICE RAJA_INLINE UnaryFunc for_each_index(camp::idx_seq<Is...>,

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should all be size_t or camp::idx_t.

@MrBurmark
Copy link
Member

I'm considering separating the SubView holding the view from the slices that are used to access the view, somewhat analogous to the approach of having layouts and Views be separate classes. Any thoughts on this?

You mean something more like making a SubLayout that you can use anywhere a layout can be used?

@gberg617
Copy link
Contributor Author

I'm considering separating the SubView holding the view from the slices that are used to access the view, somewhat analogous to the approach of having layouts and Views be separate classes. Any thoughts on this?

You mean something more like making a SubLayout that you can use anywhere a layout can be used?

Yes, but the intent is that these "sub-layouts" are only used with SubViews. I will try to prototype something here soon.

@MrBurmark
Copy link
Member

You mean something more like making a SubLayout that you can use anywhere a layout can be used?

Yes, but the intent is that these "sub-layouts" are only used with SubViews. I will try to prototype something here soon.

We'd have to think about it. It seems simpler to me if you can use a sublayout like a layout, that way a subview would just be a view using a sublayout.

@rhornung67 rhornung67 added this to the Dec 2025 Release milestone Nov 11, 2025
RAJA_INLINE
constexpr linear_index_type size() const { return m_layout.size(); }

RAJA_HOST_DEVICE
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changes to View and MultiView are still a WIP.

Comment on lines +40 to +43
template<IndexType DIM, typename LayoutType>
RAJA_INLINE RAJA_HOST_DEVICE constexpr IndexType size(const LayoutType&) const {
return (end_ - start_ + 1);
}
Copy link
Member

@MrBurmark MrBurmark Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is DIM here? If its unused we should use the unused arg macro so we don't get a warning.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also why the +1, this is including end?


template<typename IndexType = Index_type>
struct StridedSlice {
IndexType start_, end_, stride_;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this support negative strides? I'm pretty sure that the strided range supports negative strides.

RAJA_INLINE RAJA_HOST_DEVICE constexpr auto make_slice_to_parent_index_map() {
IndexType sub_idx = 0;
IndexType i = 0;
camp::array<IndexType, sizeof...(Slices)> map = {};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not do something like this?

Suggested change
camp::array<IndexType, sizeof...(Slices)> map = {};
camp::array<IndexType, sizeof...(Slices)> map{};

Comment on lines +133 to +134
camp::array<IndexType, sizeof...(Slices)> map = {};
((map[i++] = (Slices::reduces_dimension ? -1 : sub_idx++)), ...);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or even do something like this?

Suggested change
camp::array<IndexType, sizeof...(Slices)> map = {};
((map[i++] = (Slices::reduces_dimension ? -1 : sub_idx++)), ...);
camp::array<IndexType, sizeof...(Slices)> map{{(Slices::reduces_dimension ? -1 : sub_idx++)...}};

Comment on lines +138 to +139
template<typename IndexType, size_t n_parent_dims, typename... Slices>
RAJA_INLINE RAJA_HOST_DEVICE constexpr auto make_parent_to_slice_index_map() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make more sense to take n_parent_dims as an argument or calculate it inside?


using IndexLinear = IndexType;

const LayoutType& parent_;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of having a copy of the parent this has a reference? What if you make the SubRegion on the host and then copy it to the device?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also what do you think about using the member variables are named with m_ and static members are named with s_ convention?

const LayoutType& parent_;
camp::tuple<Slices...> slices_;

static inline constexpr size_t num_slices_ = sizeof...(Slices);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static data members and functions should be above non-static data members and functions.

static inline constexpr size_t num_slices_ = sizeof...(Slices);

static inline constexpr
IndexType n_dims = ((Slices::reduces_dimension == false ? 1 : 0) + ...);
Copy link
Member

@MrBurmark MrBurmark Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about using Slices::reduces_dimension as a boolean if its a boolean?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like to have all the uses of something look similar if possible. I think this is often used like this !Slices::reduces_dimension so it would be simpler to understand if this also looked the same.

Comment on lines +192 to +203
RAJA_INLINE RAJA_HOST_DEVICE constexpr auto& get_parent() const {
return parent_;
}

RAJA_INLINE RAJA_HOST_DEVICE constexpr auto& get_slices() const {
return slices_;
}

template<IndexType Index>
RAJA_INLINE RAJA_HOST_DEVICE constexpr auto& get_slice() const {
return camp::get<Index>(slices_);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there are const they should probably return const references.

for_each_tuple_index( slices_,
[&](auto slice, auto index) {
const IndexType dim_size = slice.template size<index>(parent_);
prod_dims *= (dim_size == 0) ? 1 : dim_size;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there really is a dimension that is completely projected out, shouldn't the size be 0?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also do we have to consider the 0 dimension case?

Comment on lines +230 to +231
constexpr auto SliceDim = parent_to_slice_map_[DIM];
return camp::get<SliceDim>(slices_).template size<DIM>(parent_);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be able to static_assert that DIM is in bounds.


for_each_tuple_index( slices_,
[&](auto slice, auto index) {
if (slice_to_parent_map_[index] >= 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be if constexpr?

Comment on lines +252 to +255
// map_index will not need index values for dimension-reducing slices
// so we pass a "dummy" value.
constexpr IndexType dummy_value = -1;
parent_indices[index] = slice.map_index(dummy_value);
Copy link
Member

@MrBurmark MrBurmark Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should dimension reducing slices not take a value at all?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants