On Sun, Aug 3, 2025 at 11:05 PM Luc Grosheintz <luc.groshei...@gmail.com>
wrote:

> In mdspan related code, for extents with no static extents, i.e. only
> dynamic extents, the following simplifications can be made:
>
>   - The array of dynamic extents has size rank.
>   - The two arrays dynamic-index and dynamic-index-inv become
>   trivial, e.g. k[i] == i.
>   - All elements of the arrays __{fwd,rev}_partial_prods are 1.
>
> This commits eliminates the arrays for dynamic-index, dynamic-index-inv
> and __{fwd,rev}_partial_prods. It also removes the indirection k[i] == i
> from the source code, which isn't as relevant because the optimizer is
> (often) capable of eliminating the indirection.
>
> To check if it's working we look at:
>
>   using E2 = std::extents<int, dyn, dyn, dyn, dyn>;
>
>   int stride_left_E2(const std::layout_left::mapping<E2>& m, size_t r)
>   { return m.stride(r); }
>
> which generates the following
>
>   0000000000000190 <stride_left_E2>:
>   190:  48 c1 e6 02            shl    rsi,0x2
>   194:  74 22                  je     1b8 <stride_left_E2+0x28>
>   196:  48 01 fe               add    rsi,rdi
>   199:  b8 01 00 00 00         mov    eax,0x1
>   19e:  66 90                  xchg   ax,ax
>   1a0:  48 63 17               movsxd rdx,DWORD PTR [rdi]
>   1a3:  48 83 c7 04            add    rdi,0x4
>   1a7:  48 0f af c2            imul   rax,rdx
>   1ab:  48 39 fe               cmp    rsi,rdi
>   1ae:  75 f0                  jne    1a0 <stride_left_E2+0x10>
>   1b0:  c3                     ret
>   1b1:  0f 1f 80 00 00 00 00   nop    DWORD PTR [rax+0x0]
>   1b8:  b8 01 00 00 00         mov    eax,0x1
>   1bd:  c3                     ret
>
> We see that:
>
>   - There's no code to load the partial product of static extents.
>   - There's no indirection D[k[i]], it's just D[i] (as before).
>
> On a test file which computes both mapping::stride(r) and
> mapping::required_span_size, we check for static storage with
>
>   objdump -h
>
> we don't see the NTTP _Extents, anything (anymore) related to
> _StaticExtents, __fwd_partial_prods or __rev_partial_prods. We also
> check that the size of the reference object file (described three
> commits prior) reduced by a few percent from 41.9kB to 39.4kB.
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/mdspan (__mdspan::__all_dynamic): New function.
>         (__mdspan::_StaticExtents::_S_dynamic_index): Convert to method.
>         (__mdspan::_StaticExtents::_S_dynamic_index_inv): Ditto.
>         (__mdspan::_StaticExtents): New specialization for fully dynamic
>         extents.
>         (__mdspan::__fwd_prod): New constexpr if branch to avoid
>         instantiating __fwd_partial_prods.
>         (__mdspan::__rev_prod): Ditto.
>
> Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com>
>
I have made a change below locally, with it LGTM.

> ---
>  libstdc++-v3/include/std/mdspan | 64 +++++++++++++++++++++++++++------
>  1 file changed, 54 insertions(+), 10 deletions(-)
>
> diff --git a/libstdc++-v3/include/std/mdspan
> b/libstdc++-v3/include/std/mdspan
> index 11a5063f60d..49e3969134b 100644
> --- a/libstdc++-v3/include/std/mdspan
> +++ b/libstdc++-v3/include/std/mdspan
> @@ -49,6 +49,16 @@ namespace std _GLIBCXX_VISIBILITY(default)
>  _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    namespace __mdspan
>    {
> +    template<array _Extents>
> +      consteval bool
> +      __all_dynamic()
>
There is no need for this function to use-template parameter, changed it
locally to:
use:
   consteval bool
   __all_dynamic(std::span<const size_t> __extents)

Full diff of my local changes:
diff --git a/libstdc++-v3/include/std/mdspan
b/libstdc++-v3/include/std/mdspan
index a57988b8400..a9c49a2f40c 100644
--- a/libstdc++-v3/include/std/mdspan
+++ b/libstdc++-v3/include/std/mdspan
@@ -49,15 +49,14 @@ namespace std _GLIBCXX_VISIBILITY(default)
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
   namespace __mdspan
   {
-    template<array _Extents>
-      consteval bool
-      __all_dynamic()
-      {
-       for(auto __ext : _Extents)
-         if (__ext != dynamic_extent)
-           return false;
-       return true;
-      }
+    consteval bool
+    __all_dynamic(std::span<const size_t> __extents)
+    {
+      for(auto __ext : __extents)
+       if (__ext != dynamic_extent)
+         return false;
+      return true;
+    }

     template<array _Extents>
       class _StaticExtents
@@ -470,7 +469,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          return 1;
        else if constexpr (__rank == 2)
          return __r == 0 ? 1 : __exts.extent(0);
-       else if constexpr (__all_dynamic<__sta_exts>())
+       else if constexpr (__all_dynamic(__sta_exts))
          return __extents_prod(__exts, 1, 0, __r);
        else
          {
@@ -490,7 +489,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          return 1;
        else if constexpr (__rank == 2)
          return __r == 0 ? __exts.extent(1) : 1;
-       else if constexpr (__all_dynamic<__sta_exts>())
+       else if constexpr (__all_dynamic(__sta_exts))
          return __extents_prod(__exts, 1, __r + 1, __rank);
        else
          {

+      {
> +       for(auto __ext : _Extents)
> +         if (__ext != dynamic_extent)
> +           return false;
> +       return true;
> +      }
> +
>      template<array _Extents>
>        class _StaticExtents
>        {
> @@ -59,13 +69,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
>         static constexpr size_t _S_rank = _Extents.size();
>
> -       // For __r in [0, _S_rank], _S_dynamic_index[__r] is the number
> +       // For __r in [0, _S_rank], _S_dynamic_index(__r) is the number
>         // of dynamic extents up to (and not including) __r.
>         //
>         // If __r is the index of a dynamic extent, then
>         // _S_dynamic_index[__r] is the index of that extent in
>         // _M_dyn_exts.
> -       static constexpr auto _S_dynamic_index = [] consteval
> +       static constexpr size_t
> +       _S_dynamic_index(size_t __r) noexcept
> +       { return _S_dynamic_index_data[__r]; }
> +
> +       static constexpr auto _S_dynamic_index_data = [] consteval
>         {
>           array<size_t, _S_rank+1> __ret;
>           size_t __dyn = 0;
> @@ -78,11 +92,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>           return __ret;
>         }();
>
> -       static constexpr size_t _S_rank_dynamic =
> _S_dynamic_index[_S_rank];
> +       static constexpr size_t _S_rank_dynamic =
> _S_dynamic_index(_S_rank);
>
> -       // For __r in [0, _S_rank_dynamic), _S_dynamic_index_inv[__r] is
> the
> +       // For __r in [0, _S_rank_dynamic), _S_dynamic_index_inv(__r) is
> the
>         // index of the __r-th dynamic extent in _Extents.
> -       static constexpr auto _S_dynamic_index_inv = [] consteval
> +       static constexpr size_t
> +       _S_dynamic_index_inv(size_t __r) noexcept
> +       { return _S_dynamic_index_inv_data[__r]; }
> +
> +       static constexpr auto _S_dynamic_index_inv_data = [] consteval
>         {
>           array<size_t, _S_rank_dynamic> __ret;
>           for (size_t __i = 0, __r = 0; __i < _S_rank; ++__i)
> @@ -96,6 +114,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         { return _Extents[__r]; }
>        };
>
> +    template<array _Extents>
> +      requires (__all_dynamic<_Extents>())
> +      class _StaticExtents<_Extents>
> +      {
> +      public:
> +       static constexpr size_t _S_rank = _Extents.size();
> +
> +       static constexpr size_t
> +       _S_dynamic_index(size_t __r) noexcept
> +       { return __r; }
> +
> +       static constexpr size_t _S_rank_dynamic = _S_rank;
> +
> +       static constexpr size_t
> +       _S_dynamic_index_inv(size_t __k) noexcept
> +       { return __k; }
> +
> +       static constexpr size_t
> +       _S_static_extent(size_t) noexcept
> +       { return dynamic_extent; }
> +      };
> +
>      template<typename _IndexType, array _Extents>
>        class _ExtentsStorage : public _StaticExtents<_Extents>
>        {
> @@ -107,6 +147,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         using _S_base::_S_rank_dynamic;
>         using _S_base::_S_dynamic_index;
>         using _S_base::_S_dynamic_index_inv;
> +       using _S_base::_S_static_extent;
>
>         template<typename _OIndexType>
>           static constexpr _IndexType
> @@ -118,7 +159,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         {
>           auto __se = _Extents[__r];
>           if (__se == dynamic_extent)
> -           return _M_dyn_exts[_S_dynamic_index[__r]];
> +           return _M_dyn_exts[_S_dynamic_index(__r)];
>           else
>             return __se;
>         }
> @@ -144,7 +185,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>               {
>                 size_t __di = __i;
>                 if constexpr (_OtherRank != _S_rank_dynamic)
> -                 __di = _S_dynamic_index_inv[__i];
> +                 __di = _S_dynamic_index_inv(__i);
>                 _M_dyn_exts[__i] = _S_int_cast(__get_extent(__di));
>               }
>           }
> @@ -178,8 +219,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         _M_dynamic_extents(size_t __begin, size_t __end) const noexcept
>         requires (_Extents.size() > 0)
>         {
> -         return {_M_dyn_exts + _S_dynamic_index[__begin],
> -                 _M_dyn_exts + _S_dynamic_index[__end]};
> +         return {_M_dyn_exts + _S_dynamic_index(__begin),
> +                 _M_dyn_exts + _S_dynamic_index(__end)};
>         }
>
>        private:
> @@ -252,7 +293,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        static_assert(
>           (__mdspan::__valid_static_extent<_Extents, _IndexType> && ...),
>           "Extents must either be dynamic or representable as IndexType");
> -
>      public:
>        using index_type = _IndexType;
>        using size_type = make_unsigned_t<index_type>;
> @@ -429,6 +469,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>           return 1;
>         else if constexpr (__rank == 2)
>           return __r == 0 ? 1 : __exts.extent(0);
> +       else if constexpr (__all_dynamic<__sta_exts>())
> +         return __extents_prod(__exts, 1, 0, __r);
>         else
>           {
>             size_t __sta_prod = __fwd_partial_prods<__sta_exts>[__r];
> @@ -446,6 +488,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>           return 1;
>         else if constexpr (__rank == 2)
>           return __r == 0 ? __exts.extent(1) : 1;
> +       else if constexpr (__all_dynamic<__sta_exts>())
> +         return __extents_prod(__exts, 1, __r + 1, __rank);
>         else
>           {
>             size_t __sta_prod = __rev_partial_prods<__sta_exts>[__r];
> --
> 2.50.0
>
>

Reply via email to