On Mon, Jul 28, 2025 at 10:50 AM Luc Grosheintz <luc.groshei...@gmail.com>
wrote:

>
>
> On 7/28/25 08:13, Tomasz Kaminski wrote:
> > On Sun, Jul 27, 2025 at 2:57 PM Luc Grosheintz <luc.groshei...@gmail.com
> >
> > wrote:
> >
> >> In mdspan related code involving static extents, often the IndexType is
> >> part of the template parameters, even though it's not needed.
> >>
> >> This commit extracts the parts of _ExtentsStorage not related to
> >> IndexType into a separate class _StaticExtents.
> >>
> >> It also prefers passing the array of static extents, instead of the
> >> whole extents object where possible.
> >>
> >> These changes don't effect the compilation time, i.e. the difference was
> >> (much) less than 10%. However, the size of the object file is noticeably
> >> impacted.
> >>
> >> Consider an extreme example that computes required_span_size and stride
> >> for all combinations of:
> >>
> >>    - all three layouts,
> >>    - three choices of static extents Indices...
> >>    - eight choices of IndexType.
> >>
> >> In this case the size of the object file reduced by 45% (22.3kB ->
> >> 12.1kB) when compiling with -O2. Using objdump -h we see that the size
> >> of .text is unchanged, but .rodata decreases. This is plausible,
> >> because:
> >>
> >>    - the deduplicated code is short and is always inlined,
> >>    - the static storage for _FwdProd, _RevProd, the NTTP _Extents and
> >>      _StaticExtents::_S_dynamic_index is not deduplicated before this
> >>      commit.
> >>
> >> libstdc++-v3/ChangeLog:
> >>
> >>          * include/std/mdspan (__mdspan::_StaticExtents): New class.
> >>          (__mdspan::_ExtentsStorage): Inherit non-IndexType related
> >>          methods from _StaticExtents.
> >>          (__mdspan::_ExtentsStorage::_S_static_extents): Return
> reference
> >>          to NTTP _Extents.
> >>          (__mdspan::__static_extents): Ditto.
> >>          (__mdspan::__static_prod): Use NTTP for static extents.
> >>          (__mdspan::_FwdProd): Ditto.
> >>          (__mdspan::_RevProd): Ditto.
> >>          (__mdspan::__contains_zero): New overload for const array&.
> >>
> >> Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com>
> >> ---
> >>
> > I have given you the same suggestion while reviewing my previous commit,
> > so this looks good to me. Would it be much of the issue to put this
> comment
> > before the changes introducing the caching of indices when doing new
> > revision.
> > This is not strictly necessary, but this ones is not tradeoff between
> conde
> > size and
> > performance.
>
> I'd prefer not to, the diffs don't commute and require some temporary minor
> inconvenience to eliminate one case of __static_extents(__begin, __end).
>
As I said, I do not have a strong preference in either direction. So, you
can keep the
current order.

>
> Maybe a better excuse: this way around we can focus on code in .text first
> and then check that .rodata and object file size shrink down to the
> expected
> size. Somehow, this order of the git messages feels more interesting than
> the
> reverse. In the reverse we first check object size; then completely rewrite
> how we use static variables; making the previous commit message instantly
> outdated.
>
> >
> >   libstdc++-v3/include/std/mdspan | 70 +++++++++++++++++++++------------
> >>   1 file changed, 45 insertions(+), 25 deletions(-)
> >>
> >> diff --git a/libstdc++-v3/include/std/mdspan
> >> b/libstdc++-v3/include/std/mdspan
> >> index 5b47c95d2c6..2cf572f1410 100644
> >> --- a/libstdc++-v3/include/std/mdspan
> >> +++ b/libstdc++-v3/include/std/mdspan
> >> @@ -49,19 +49,14 @@ namespace std _GLIBCXX_VISIBILITY(default)
> >>   _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>     namespace __mdspan
> >>     {
> >> -    template<typename _IndexType, array _Extents>
> >> -      class _ExtentsStorage
> >> +    template<array _Extents>
> >> +      class _StaticExtents
> >>         {
> >>         public:
> >>          static consteval bool
> >>          _S_is_dyn(size_t __ext) noexcept
> >>          { return __ext == dynamic_extent; }
> >>
> >> -       template<typename _OIndexType>
> >> -         static constexpr _IndexType
> >> -         _S_int_cast(const _OIndexType& __other) noexcept
> >> -         { return _IndexType(__other); }
> >> -
> >>          static constexpr size_t _S_rank = _Extents.size();
> >>
> >>          // For __r in [0, _S_rank], _S_dynamic_index[__r] is the number
> >> @@ -99,6 +94,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>          static constexpr size_t
> >>          _S_static_extent(size_t __r) noexcept
> >>          { return _Extents[__r]; }
> >> +      };
> >> +
> >> +    template<typename _IndexType, array _Extents>
> >> +      class _ExtentsStorage : public _StaticExtents<_Extents>
> >> +      {
> >> +      private:
> >> +       using _S_base = _StaticExtents<_Extents>;
> >> +
> >> +      public:
> >> +       using _S_base::_S_rank;
> >> +       using _S_base::_S_rank_dynamic;
> >> +       using _S_base::_S_dynamic_index;
> >> +       using _S_base::_S_dynamic_index_inv;
> >> +
> >> +       template<typename _OIndexType>
> >> +         static constexpr _IndexType
> >> +         _S_int_cast(const _OIndexType& __other) noexcept
> >> +         { return _IndexType(__other); }
> >>
> >>          constexpr _IndexType
> >>          _M_extent(size_t __r) const noexcept
> >> @@ -157,9 +170,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>                { return __exts[__i]; });
> >>            }
> >>
> >> -       static constexpr span<const size_t>
> >> -       _S_static_extents(size_t __begin, size_t __end) noexcept
> >> -       { return {_Extents.data() + __begin, _Extents.data() + __end}; }
> >> +       static constexpr const array<size_t, _S_rank>&
> >> +       _S_static_extents() noexcept
> >> +       { return _Extents; }
> >>
> >>          constexpr span<const _IndexType>
> >>          _M_dynamic_extents(size_t __begin, size_t __end) const noexcept
> >> @@ -184,27 +197,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>         __valid_static_extent = _Extent == dynamic_extent
> >>          || _Extent <= numeric_limits<_IndexType>::max();
> >>
> >> -    template<typename _Extents>
> >> +    template<array _Extents>
> >>         consteval size_t
> >>         __static_prod(size_t __begin, size_t __end)
> >>         {
> >>          size_t __prod = 1;
> >>          for(size_t __i = __begin; __i < __end; ++__i)
> >>            {
> >> -             auto __ext = _Extents::static_extent(__i);
> >> +             auto __ext = _Extents[__i];
> >>                __prod *= __ext == dynamic_extent ? size_t(1) : __ext;
> >>            }
> >>          return __prod;
> >>         }
> >>
> >>       // Pre-compute: \prod_{i = 0}^r _Extents[i]
> >> -    template<typename _Extents>
> >> +    template<array _Extents>
> >>         struct _FwdProd
> >>         {
> >> -       constexpr static std::array<size_t, _Extents::rank() + 1>
> _S_value
> >> =
> >> +       constexpr static std::array<size_t, _Extents.size() + 1>
> _S_value =
> >>          [] consteval
> >>          {
> >> -         constexpr size_t __rank = _Extents::rank();
> >> +         constexpr size_t __rank = _Extents.size();
> >>            std::array<size_t, __rank + 1> __ret;
> >>            for(size_t __r = 0; __r < __rank + 1; ++__r)
> >>              __ret[__r] = __static_prod<_Extents>(0, __r);
> >> @@ -213,13 +226,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>         };
> >>
> >>       // Pre-compute: \prod_{i = r+1}^n _Extents[i]
> >> -    template<typename _Extents>
> >> +    template<array _Extents>
> >>         struct _RevProd
> >>         {
> >> -       constexpr static std::array<size_t, _Extents::rank()> _S_value =
> >> +       constexpr static std::array<size_t, _Extents.size()> _S_value =
> >>          [] consteval
> >>          {
> >> -         constexpr size_t __rank = _Extents::rank();
> >> +         constexpr size_t __rank = _Extents.size();
> >>            std::array<size_t, __rank> __ret;
> >>            for(size_t __r = 0; __r < __rank; ++__r)
> >>              __ret[__r] = __static_prod<_Extents>(__r + 1, __rank);
> >> @@ -228,10 +241,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>         };
> >>
> >>       template<typename _Extents>
> >> -      constexpr span<const size_t>
> >> -      __static_extents(size_t __begin = 0, size_t __end =
> >> _Extents::rank())
> >> +      constexpr const array<size_t, _Extents::rank()>&
> >> +      __static_extents()
> >>         noexcept
> >> -      { return _Extents::_S_storage::_S_static_extents(__begin,
> __end); }
> >> +      { return _Extents::_S_storage::_S_static_extents(); }
> >>
> >>       template<typename _Extents>
> >>         constexpr span<const typename _Extents::index_type>
> >> @@ -358,8 +371,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>          }
> >>
> >>       private:
> >> -      friend span<const size_t>
> >> -      __mdspan::__static_extents<extents>(size_t, size_t);
> >> +      friend const array<size_t, rank()>&
> >> +      __mdspan::__static_extents<extents>();
> >>
> >>         friend span<const index_type>
> >>         __mdspan::__dynamic_extents<extents>(const extents&, size_t,
> >> size_t);
> >> @@ -384,6 +397,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>          return false;
> >>         }
> >>
> >> +    template<typename _Tp, size_t _Nm>
> >>
> > Add [[__gnu__::__always_inline__]], as we do not want this to be emitted
> > even in debug mode.
> > We may want to do the same for __empty, __size, __fwd_prod and
> __rev__prod,
> > that uses
> > _Extents as template parameter.
> >
> >> +      consteval bool
> >> +      __contains_zero(const array<_Tp, _Nm>& __exts) noexcept
> >> +      { return __contains_zero(span<const _Tp, _Nm>{__exts}); }
> >> +
> >>       template<typename _Extents>
> >>         constexpr bool
> >>         __empty(const _Extents& __exts) noexcept
> >> @@ -412,7 +430,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>         constexpr typename _Extents::index_type
> >>         __fwd_prod(const _Extents& __exts, size_t __r) noexcept
> >>         {
> >> -       size_t __sta_prod = _FwdProd<_Extents>::_S_value[__r];
> >> +       constexpr auto __sta_exts = __static_extents<_Extents>();
> >> +       size_t __sta_prod = _FwdProd<__sta_exts>::_S_value[__r];
> >>          return __extents_prod(__exts, __sta_prod, 0, __r);
> >>         }
> >>
> >> @@ -420,8 +439,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>         constexpr typename _Extents::index_type
> >>         __rev_prod(const _Extents& __exts, size_t __r) noexcept
> >>         {
> >> +       constexpr auto __sta_exts = __static_extents<_Extents>();
> >>          constexpr size_t __rank = _Extents::rank();
> >> -       size_t __sta_prod = _RevProd<_Extents>::_S_value[__r];
> >> +       size_t __sta_prod = _RevProd<__sta_exts>::_S_value[__r];
> >>          return __extents_prod(__exts, __sta_prod, __r + 1, __rank);
> >>         }
> >>
> >> --
> >> 2.50.0
> >>
> >>
> >
>
>

Reply via email to