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

>
>
> On 8/11/25 15:29, Tomasz Kaminski wrote:
> > On Mon, Aug 11, 2025 at 3:23 PM Luc Grosheintz <luc.groshei...@gmail.com
> >
> > wrote:
> >
> >> Prior to this commit, the partial producs of static extents in <mdspan>
> >> was done in a loop that calls a function that computes the partial
> >> product. The complexity is quadratic in the rank.
> >>
> >> This commit removes the quadratic complexity.
> >>
> >> libstdc++-v3/ChangeLog:
> >>
> >>          * include/std/mdspan (__static_prod): Delete.
> >>          (__fwd_partial_prods): Compute at compile-time in O(rank), not
> >>          O(rank**2).
> >>          (__rev_partial_prods): Ditto.
> >>          (__size): Inline __static_prod.
> >>
> >> Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com>
> >>
> > Thanks for submitting the patch. Few small suggestions below.
> >
> >> ---
> >>   libstdc++-v3/include/std/mdspan | 33 +++++++++++++++------------------
> >>   1 file changed, 15 insertions(+), 18 deletions(-)
> >>
> >> diff --git a/libstdc++-v3/include/std/mdspan
> >> b/libstdc++-v3/include/std/mdspan
> >> index 8f974257e96..e68275e3807 100644
> >> --- a/libstdc++-v3/include/std/mdspan
> >> +++ b/libstdc++-v3/include/std/mdspan
> >> @@ -256,27 +256,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>         __static_extents() noexcept
> >>         { return _Extents::_S_storage::_S_static_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[__i];
> >> -           __prod *= __ext == dynamic_extent ? size_t(1) : __ext;
> >> -         }
> >> -       return __prod;
> >> -      }
> >> -
> >>       // Pre-compute: \prod_{i = 0}^r _Extents[i], for r = 0,..., n
> >> (exclusive)
> >>       template<array _Extents>
> >>         constexpr auto __fwd_partial_prods = [] consteval
> >>          {
> >> +         auto __static_or_one = [](size_t __ext)
> >> +         { return __ext == dynamic_extent ? size_t(1) : __ext; };
> >> +
> >>            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>(0, __r);
> >> +         __ret[0] = 1;
> >> +         for (size_t __r = 0; __r < __rank - 1; ++__r)
> >> +           __ret[__r + 1] = __static_or_one(_Extents[__r]) *
> __ret[__r];
> >>
> > Any reason for using an internal lambda here....
> >
> >>            return __ret;
> >>          }();
> >>
> >> @@ -284,10 +275,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>       template<array _Extents>
> >>         constexpr auto __rev_partial_prods = [] consteval
> >>          {
> >> +         auto __static_or_one = [](size_t __ext)
> >> +         { return __ext == dynamic_extent ? size_t(1) : __ext; };
> >> +
> >>            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);
> >> +         __ret[__rank-1] = 1;
> >> +         for (size_t __r = __rank-1; __r > 0; --__r)
> >> +           __ret[__r - 1] = __static_or_one(_Extents[__r]) *
> __ret[__r];
> >>
> > ...and here...
>
> Because of the duplication of code? I was anticipating a review
> in that would ask to inline to reduce symbols. It also took a
> while until I found the almost passable name (and by that time I was
> already mentally stuck on two lambdas). More than happy to create a
> function __static_or_one.
>
And personally, I find an if (not dynamic) multiply much clearer way to
express
what we are computing there: only multiply extents that are static.
Multiplying by 1 is a bit roundabound way to express that.


> >
> >>            return __ret;
> >>          }();
> >>
> >> @@ -519,7 +514,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>         {
> >>          constexpr auto& __sta_exts = __static_extents<_Extents>();
> >>
> > I would change this type to span<const size_t>, we do not need a array
> > reference
> > in this case.
> >   constexpr span<const size_t>  __sta_exts./
>
> I'll change it, consistency is good.
>
> >
> >>          constexpr size_t __rank = _Extents::rank();
> >> -       constexpr size_t __sta_prod = __static_prod<__sta_exts>(0,
> __rank);
> >> +       size_t __sta_prod = 1;
> >> +       for(auto __ext : __sta_exts)
> >> +         __sta_prod *= __ext == dynamic_extent ? size_t(1) : __ext;
> >>
> > ...and ternary operator here? I would prefer to use:
> > if (__ext != dynamic_extent)
> >    __sta_prod *= __ext;
> > And:
> > for(size_t __r = 0; __r < __rank; ++__r)
> >     if (size_t __ext = _Extents[__i]; __ext != dynamic_extent)
> >       __sta_prod *= __ext;
> >
>
> The ternary is shorter and since its the product of all static extents,
> everything is known at compile time and the whole block is replaced by a
> constant.
>
I just find an if much more readable, and expressing the intent in both
cases,
I would also make sure that the computation is done at compile time, by
having:
constexpr size_t _sta_prod = [] {
   size_t __res;
   for(auto __ext : __static_extents<_Extents>())
       if (__ext != dynamic_extent)
         __res *= __ext;
    return __res;
}();


> >>          return __extents_prod(__exts, __sta_prod, 0, __rank);
> >>         }
> >>
> >> --
> >> 2.50.0
> >>
> >>
> >
>
>

Reply via email to