On 05/09/20 09:34 +0100, Jonathan Wakely via Libstdc++ wrote:
On Sat, 5 Sep 2020 at 01:35, Sidney Marshall <sidn...@frontiernet.net> wrote:
Jonathan
I don't know if the following comments are useful or not but here goes:
Reviews of my patches are always welcome, thanks.
>The current std::gcd and std::chrono::duration::_S_gcd algorithms are
>both recursive. This is potentially expensive to evaluate in constant
>expressions, because each level of recursion makes a new copy of the
>function to evaluate. The maximum number of steps is bounded
>(proportional to the number of decimal digits in the smaller value) and
>so unlikely to exceed the limit for constexpr nesting, but the memory
>usage is still suboptimal. By using an iterative algorithm we avoid
>that compile-time cost. Because looping in constexpr functions is not
>allowed until C++14, we need to keep the recursive implementation in
>duration::_S_gcd for C++11 mode.
>
>For std::gcd we can also optimise runtime performance by using the
>binary GCD algorithm.
>
>libstdc++-v3/ChangeLog:
>
> * include/std/chrono (duration::_S_gcd): Use iterative algorithm
> for C++14 and later.
> * include/std/numeric (__detail::__gcd): Replace recursive
> Euclidean algorithm with iterative version of binary GCD algorithm.
> * testsuite/26_numerics/gcd/1.cc: Test additional inputs.
> * testsuite/26_numerics/gcd/gcd_neg.cc: Adjust dg-error lines.
> * testsuite/26_numerics/lcm/lcm_neg.cc: Likewise.
> * testsuite/experimental/numeric/gcd.cc: Test additional inputs.
> * testsuite/26_numerics/gcd/2.cc: New test.
>
>Tested powerpc64le-linux. Committed to trunk.
>
>-------------- next part --------------
>commit 3c219134152f645103f2fcd50735b177ccd76cde
>Author: Jonathan Wakely <jwak...@redhat.com>
>Date: Thu Sep 3 12:38:50 2020
>
> libstdc++: Optimise GCD algorithms
>
> The current std::gcd and std::chrono::duration::_S_gcd algorithms are
> both recursive. This is potentially expensive to evaluate in constant
> expressions, because each level of recursion makes a new copy of the
> function to evaluate. The maximum number of steps is bounded
> (proportional to the number of decimal digits in the smaller value) and
> so unlikely to exceed the limit for constexpr nesting, but the memory
> usage is still suboptimal. By using an iterative algorithm we avoid
> that compile-time cost. Because looping in constexpr functions is not
> allowed until C++14, we need to keep the recursive implementation in
> duration::_S_gcd for C++11 mode.
>
> For std::gcd we can also optimise runtime performance by using the
> binary GCD algorithm.
>
> libstdc++-v3/ChangeLog:
>
> * include/std/chrono (duration::_S_gcd): Use iterative algorithm
> for C++14 and later.
> * include/std/numeric (__detail::__gcd): Replace recursive
> Euclidean algorithm with iterative version of binary
> GCD algorithm.
> * testsuite/26_numerics/gcd/1.cc: Test additional inputs.
> * testsuite/26_numerics/gcd/gcd_neg.cc: Adjust dg-error lines.
> * testsuite/26_numerics/lcm/lcm_neg.cc: Likewise.
> * testsuite/experimental/numeric/gcd.cc: Test additional inputs.
> * testsuite/26_numerics/gcd/2.cc: New test.
>
>diff --git a/libstdc++-v3/include/std/chrono b/libstdc++-v3/include/std/chrono
>index 1682263fd9f..0e2efb2522b 100644
>--- a/libstdc++-v3/include/std/chrono
>+++ b/libstdc++-v3/include/std/chrono
>@@ -428,8 +428,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> _S_gcd(intmax_t __m, intmax_t __n) noexcept
> {
> // Duration only allows positive periods so we don't need to
>- // support negative values here (unlike __static_gcd and std::gcd).
>+ // handle negative values here (unlike __static_gcd and std::gcd).
>+#if __cplusplus >= 201402L
>+ while (__m != 0 && __n != 0)
Why are you testing for both __m != 0 && __n != 0 in the loop. Only
__n can be zero. A test for __m == 0 can be made out side of the loop
to save a possible division. Or is there something special about
compile time execution?
It was adapted (maybe too hastily) from the general purpose std::gcd
function, which does need to handle zeros. I didn't consider how to
improve it using extra knowledge about non-zero inputs (I only
considered that they can't be negative).
Neither value can be zero initially (duration only allows positive
ratios, and ratio doesn't allow zero denominators) so I suppose it
could be:
do
{
intmax_t __rem = __m % __n;
__m = __n;
__n = __rem;
} while (__n != 0)
return __m;
>+ {
>+ intmax_t __rem = __m % __n;
>+ __m = __n;
>+ __n = __rem;
>+ }
>+ return __m + __n;
If __n is zero then why not just return __m? Or, again, is there
something special about compile time execution?
If n is zero it *does* just return m, because m+n is zero. The loop
exits when one or both is zero, so m+n returns whichever is non-zero
(if any).
I wasn't considered that the loop can only exit when m is zero.
I've committed this simplification for duration::_S_gcd.
Thanks for the review and the suggestions.
Tested powerpc64le-linux, committed to trunk.
commit ec5096f48bbd7db83cbe94bdd3235c5808a5979a
Author: Jonathan Wakely <jwak...@redhat.com>
Date: Mon Sep 7 20:09:17 2020
libstdc++: Simplify chrono::duration::_S_gcd
We can simplify this constexpr function further because we know that
period::num >= 1 and period::den >= 1 so only the remainder can ever be
zero.
libstdc++-v3/ChangeLog:
* include/std/chrono (duration::_S_gcd): Use invariant that
neither value is zero initially.
diff --git a/libstdc++-v3/include/std/chrono b/libstdc++-v3/include/std/chrono
index 0e2efb2522b..afee7859c6d 100644
--- a/libstdc++-v3/include/std/chrono
+++ b/libstdc++-v3/include/std/chrono
@@ -430,17 +430,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// Duration only allows positive periods so we don't need to
// handle negative values here (unlike __static_gcd and std::gcd).
#if __cplusplus >= 201402L
- while (__m != 0 && __n != 0)
+ do
{
intmax_t __rem = __m % __n;
__m = __n;
__n = __rem;
}
- return __m + __n;
+ while (__n != 0);
+ return __m;
#else
// C++11 doesn't allow loops in constexpr functions, but this
// recursive version can be more expensive to evaluate.
- return (__m == 0) ? __n : (__n == 0) ? __m : _S_gcd(__n, __m % __n);
+ return (__n == 0) ? __m : _S_gcd(__n, __m % __n);
#endif
}