ORNL / ORNL/cpp-proposals-pub

mdarray: Explain why we omit reshape / resize

Open
#287 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
HTML
Stars
29
Forks
26
PR merge metrics
No merged PRs in 30d

Description

Why mdarray omits reshape / resize

Some users have asked why mdarray does not have a "reshape" or "resize" function. This would change the extents of an existing mdarray "in place," like vector::resize. We should add text to P1684 explaining why mdarray does not have these functions. Here is a draft of this text.

Reasons not to add a reshape / resize member function to mdarray

  1. The function would only make sense if the mdarray has at least one dynamic extent.
  2. An mdarray can have dynamic extents but still use a bounded container type like std::array. Thus, the reshape operation's preconditions would depend on the container type, making it more difficult to write generic code. mdarray would also need a new concept to know whether the container has a resize method (or some other way to resize).
  3. Resizing an mdarray would invalidate existing views, during the lifetime of the mdarray being viewed. (vector has the same problem.)
  4. If the mdarray's container is vector, then resizing / reshaping would double-initialize the new elements. This is one justification for the std::string::resize_and_overwrite member function in C++23.
  5. C++ arguably should have standardized a dynamically sized, non-resize-able array type, to fill the gap between array (fixed compile-time size, O(size()) move cost) and vector (arbitrarily resize-able, O(1) move cost). mdarray is the multidimensional analogy of this missing type in the C++ Standard Library.

What about a nonmember function?

We could consider a nonmember reshape function that can consume and recycle an existing mdarray's storage, if reuse is possible.

template<class ElementType, class InExtents, class Layout, class Container, class OutputIndexType, size_t ... OutputExtents>
mdarray<ElementType, extents<OutputIndexType, OutputExtents...>, Layout, Container>
reshape(mdarray<ElementType, InExtents, Layout, Container>&& recycle_me,
    extents<OutputIndexType, OutputExtents...> e,
    ElementType default_fill_value = ElementType{});

This would permit changing the extents, even if all the input extents are static. After return, the input mdarray would be in that uncomfortable moved-from state, but mdarray already has move constructors and move assignment, so this is not a new discomfort.

Note that there's no way for users to define reshape in a way that can reuse the container's storage. This is because mdarray does not expose access to the underlying container after mdarray construction. (This was a P1684R2 change.)

What about a different container?

Above, I pointed out that C++ arguably should have standardized a dynamically sized, non-resize-able array type, to fill the gap between array (fixed compile-time size, O(size()) move cost) and vector (arbitrarily resize-able, O(1) move cost). mdarray is the multidimensional analogy of this missing type in the C++ Standard Library.

I do agree that it's convenient to be able to reshape an existing container in place. I've certainly written Matlab code that adds a new column to a matrix now and then. However, there's no need for this reshape-able container to be named "mdarray." In general, we shouldn't necessarily be shy in defining new container types. The fundamental vocabulary type is the view, not the container. Different container types communicate with algorithms through mdspan, which is the common interface between containers and algorithms-on-views-of-data. Algorithms that operate on views-of-data should take mdspan . Containers should define a way to get an mdspan viewing the container's data.

Forcing mdarray to be the dynamically reshape-able container could prevent optimizations. For example, a dynamically reshape-able container need not even use contiguous storage. It might store add-ons as separate allocations, and then have a "pack" operation that transforms it into a single contiguous allocation. This could reduce intermediate reallocation costs.

// mdarray<...> pack(dyanmic_mdarray<...>&&);

dynamic_mdarray A_dynamic{...};
while(some_condition()) {
  auto new_column = allocate_and_compute_new_column(A_dynamic.extent(0)); 
  // 1 is the extent to which to append the column
  A_dynamic.append(1, std::move(new_column));
}
auto A_packed = pack(A_dynamic);
some_algorithm_taking_mdspan(A_packed.mdspan());

This is analogous to the "string builder" vs. string distinction.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Locate the P1684 paper in the repository and review its existing discussion of mdarray, reshape, and resize. Add the supplied rationale and alternatives in the appropriate section, then verify that the paper clearly explains why these functions are omitted and that its formatting remains consistent.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
documentation
Issue type
Documentation
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.