Re: [PATCH] libstdc++: Optimize chrono year::is_leap

Francisco Muniz <[email protected]>
Newsgroups gmane.comp.gcc.patches,gmane.comp.gcc.libstdc++.devel
Message-ID <CAO9xfXejBi4bvTXFUXRmOuFJ8TS+QB4Crft6raER6jYJBsTV_Q@mail.gmail.com>
Thanks, I had only checked scalar code generation initially.  In an
isolated scalar test on x86_64, the new form is 7 instructions versus  10
for the existing form, but I can reproduce that benchmark results depend on
optimization context.  With GCC 17 -O3 generic, the loop favors the
existing form, while with -fno-tree-vectorize or -march=native the new form
is faster on my machine.

Given that, I have to do more benchmarking before claiming this is a
performance improvement, focus was mostly on number of instructions


Em qui., 30 de jul. de 2026 às 12:11, Jonathan Wakely <[email protected]>
escreveu:

> On Thu, 30 Jul 2026 at 01:58, Francisco Muniz wrote:
> >
> > Use Falk Hueffner's leap-year test for year::is_leap after shifting
> > the valid std::chrono::year range by a multiple of 400. The shift
> > preserves divisibility by 4, 100, and 400, and the unsigned conversion
> > gives the intended modulo 2^32 arithmetic.
> >
> > Idea by Cassio Neri: add 32800, which is 82 * 400, to shift the signed
> > year range into the supported non-negative range.
>
> This is interesting, but your new code is slower than Cassio's code
> when I benchmark it. Have you done your own benchmarking?
>
> >
> > Tested on x86_64-pc-linux-gnu:
> >   make -j$(nproc) all-gcc
> >   make -j$(nproc) all-target-libstdc++-v3
> >   make check RUNTESTFLAGS='conformance.exp=std/time/year/1.cc'
> >   make check RUNTESTFLAGS='conformance.exp=std/time/year/2.cc'
> >
> > libstdc++-v3/ChangeLog:
> >
> >         * include/std/chrono (year::is_leap): Use Hueffner leap-year
> >         test after biasing the year by a multiple of 400.
> >
> > Signed-off-by: Francisco Muniz <[email protected]>
> > ---
> >  libstdc++-v3/include/std/chrono | 31 ++++++++-----------------------
> >  1 file changed, 8 insertions(+), 23 deletions(-)
> >
> > diff --git a/libstdc++-v3/include/std/chrono
> b/libstdc++-v3/include/std/chrono
> > index 692fd6025e7..4483914c08b 100644
> > --- a/libstdc++-v3/include/std/chrono
> > +++ b/libstdc++-v3/include/std/chrono
> > @@ -904,29 +904,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >        constexpr bool
> >        is_leap() const noexcept
> >        {
> > -       // Testing divisibility by 100 first gives better performance
> [1], i.e.,
> > -       //     return _M_y % 100 == 0 ? _M_y % 400 == 0 : _M_y % 16 == 0;
> > -       // Furthermore, if _M_y % 100 == 0, then _M_y % 400 == 0 is
> equivalent
> > -       // to _M_y % 16 == 0, so we can simplify it to
> > -       //     return _M_y % 100 == 0 ? _M_y % 16 == 0 : _M_y % 4 == 0.
> // #1
> > -       // Similarly, we can replace 100 with 25 (which is good since
> > -       // _M_y % 25 == 0 requires one fewer instruction than _M_y % 100
> == 0
> > -       // [2]):
> > -       //     return _M_y % 25 == 0 ? _M_y % 16 == 0 : _M_y % 4 == 0.
> // #2
> > -       // Indeed, first assume _M_y % 4 != 0.  Then _M_y % 16 != 0 and
> hence,
> > -       // _M_y % 4 == 0 and _M_y % 16 == 0 are both false.  Therefore,
> #2
> > -       // returns false as it should (regardless of _M_y % 25.) Now
> assume
> > -       // _M_y % 4 == 0.  In this case, _M_y % 25 == 0 if, and only if,
> > -       // _M_y % 100 == 0, that is, #1 and #2 are equivalent.  Finally,
> #2 is
> > -       // equivalent to
> > -       //     return (_M_y & (_M_y % 25 == 0 ? 15 : 3)) == 0.
> > -
> > -       // References:
> > -       // [1] https://github.com/cassioneri/calendar
> > -       // [2] https://godbolt.org/z/55G8rn77e
> > -       // [3]
> https://gcc.gnu.org/pipermail/libstdc++/2021-June/052815.html
> > -
> > -       return (_M_y & (_M_y % 25 == 0 ? 15 : 3)) == 0;
> > +       // Shift into the range supported by Falk Hueffner's leap-year
> test:
> > +       //
> hueffner.de/falk/blog/a-leap-year-check-in-three-instructions.html
> > +       // Adding a multiple of 400 preserves divisibility by 4, 100,
> and 400.
> > +       // Idea by Cassio Neri: add 32800 (82 * 400).
> > +       // The conversion to uint32_t gives the algorithm's intended
> modulo 2^32
> > +       // arithmetic.
> > +       const auto __y = static_cast<uint32_t>(_M_y) + 32800u;
> > +       return ((__y * 1073750999u) & 3221352463u) <= 126976u;
> >        }
> >
> >        explicit constexpr
> > --
> > 2.47.3
> >
>
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.