kokkos / mdspan

Reference implementation of mdspan targeting C++23
Other
413 stars 69 forks source link

Using mdspan as a contiguous_range for std::simd interaction? #365

Open danieltowner opened 2 weeks ago

danieltowner commented 2 weeks ago

I am currently working on std::simd range interactions (https://isocpp.org/files/papers/P3299R2.html), and I am investigating some ways of using mdspan (which is fantastic - great for my needs!) with some of the new std::simd constructors.

Something that seems to make sense for much of my code is using an mdspan to provide a view onto an array of simd blocks. I want to be able to grab one of those sub-blocks as a std::simd:

data = ...; // Preallocated block of data from somewhere.

auto m = mdspan<int, std::extents{std::dynamic_extent, 16}>(data, 1024); // 1024 16-element simd objects.

// Grab one row of interest.
auto b = submdspan(m, n, std::full_extent); // Or even better b = m[n] - see #363 

// What I'd like to do to load a simd from the row.
auto s1 = std::load_from<simd<int, 16>>(b);

This doesn't work yet because load_from requires a source which is a contiguous_range, which mdspan doesn't satisfy. I believe this is because the mapping might have holes (strides/padding), and the accessor policy might map even adjacent indexes to non-adjacent objects.

I can see that mdspan can't always be contiguous (or exhaustive as I see it is called in mdspan), but there will be cases where the data does happen to be contiguous/exhaustive, such as my example above, and it would be good to be able to access that data from simd easily. I believe that the data should be contiguous provided that the mdspan is both exhaustive and it uses the default access policy. Handling other cases won't be so easy, but it seems like at least the common case could be handled?

std::ranges::contiguous_range isn't satisfied for exhaustive mdspan's at the moment, but is that because the compilers aren't getting it right, it hasn't been implemented, or is it because that concept can never be satisfied? Is there a reason why an mdspan which is known to be exhaustive at compile-time and with a default accessor shouldn't satisfy std::ranges::contiguous_range?

If the contiguous range concept will never be supported then I could allow a std:simd to be loaded from a contiguous/exhaustive mdspan by adding an overload which checks for exhaustiveness (roughly as):

template<typename M>
requires M::mapping_type::is_exhaustive()
basic_simd(M m)

I'd rather not have to do this if there were a better way because it is making std::simd more complicated, but the interaction of simd and mdspan seems to be too useful to overlook.

Taking this further, if mdspan can be used to build a static span-like thing (i.e., is not only contiguous but also has a known size), then even more useful options are available to construct a simd from an mdspan:

// grab an mdspan of known dimension 1x16.
auto b = submdspan(m, n, std::full_extent); // Or even better b = m[n] - see #363 

// Use simd's constructor from a span-like (contiguous compile-time) thing.
auto s2 = std::simd<int, 16>(b); // Checks that the type and size match

// Or use CTAD
auto s3 = std::basic_simd{b}; // Works out type and size itself.

// Or the ultimate - get a row from a 2-d array as a simd without type/size being required
auto s4 = std::basic_simd(myMdSpan[4]); // #363 

I couldn't find any discussions about this anywhere, so please point me at anything which has already been publishes on this topic.