public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r13-2230] libstdc++: Optimize std::con/disjunction, __and_/__or_, etc
@ 2022-08-26 19:11 Patrick Palka
  0 siblings, 0 replies; only message in thread
From: Patrick Palka @ 2022-08-26 19:11 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:390f94eee1ae694901f896ac45bfb148f8126baa

commit r13-2230-g390f94eee1ae694901f896ac45bfb148f8126baa
Author: Patrick Palka <ppalka@redhat.com>
Date:   Fri Aug 26 15:10:57 2022 -0400

    libstdc++: Optimize std::con/disjunction, __and_/__or_, etc
    
    The internal type-level logical operator traits __and_ and __or_ seem to
    have high overhead for a couple of reasons:
    
      1. They are drop-in replacements for std::con/disjunction, which
         are rigidly specified to form a type that derives from the first
         type argument that caused the overall computation to short-circuit.
         In practice this inheritance property seems to be rarely needed;
         usually all we care about is the value of the overall result.
      2. Their recursive implementations instantiate O(N) class templates
         and form an inheritance chain of depth O(N).
    
    This patch gets rid of this inheritance property of __and_ and __or_
    (which seems to be unneeded in the library except indirectly by
    std::con/disjunction) which allows us to redefine them non-recursively
    as alias templates that yield either false_type or true_type via
    enable_if_t and partial ordering of a pair of function templates
    (alternatively we could use an equivalent partially specialized class
    template, but using function templates appears to be slightly more
    efficient).
    
    As for std::con/disjunction, it seems we need to keep implementing them
    via a recursive class template for sake of the inheritance property.
    But instead of using inheritance recursion, use a recursive member
    typedef that gets immediately flattened, so that specializations thereof
    now have O(1) instead of O(N) inheritance depth.
    
    In passing, redefine __not_ as an alias template for consistency with
    __and_ and __or_, and to remove a layer of indirection.
    
    Together these changes have a substantial effect on compile time and
    memory usage for code that heavily uses these internal type traits.
    For the following example (which tests constructibility between two
    compatible 257-element tuple types):
    
      #include <tuple>
    
      #define M(x) x, x
    
      using ty1 = std::tuple<M(M(M(M(M(M(M(M(int)))))))), int>;
      using ty2 = std::tuple<M(M(M(M(M(M(M(M(int)))))))), long>;
    
      static_assert(std::is_constructible_v<ty2, ty1>);
    
    memory usage improves ~27% from 440MB to 320MB and compile time improves
    ~20% from ~2s to ~1.6s (with -std=c++23).
    
    libstdc++-v3/ChangeLog:
    
            * include/std/type_traits (enable_if, __enable_if_t): Define them
            earlier.
            (__detail::__first_t): Define.
            (__detail::__or_fn, __detail::__and_fn): Declare.
            (__or_, __and_): Redefine as alias templates in terms of __or_fn
            and __and_fn.
            (__not_): Redefine as an alias template.
            (__detail::__disjunction_impl, __detail::__conjunction_impl):
            Define.
            (conjuction, disjunction): Redefine in terms of __disjunction_impl
            and __conjunction_impl.

Diff:
---
 libstdc++-v3/include/std/type_traits | 130 +++++++++++++++++++----------------
 1 file changed, 71 insertions(+), 59 deletions(-)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 14b029cec64..c2f5cb9c806 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -100,6 +100,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // Metaprogramming helper types.
 
+  // Primary template.
+  /// Define a member typedef `type` only if a boolean constant is true.
+  template<bool, typename _Tp = void>
+    struct enable_if
+    { };
+
+  // Partial specialization for true.
+  template<typename _Tp>
+    struct enable_if<true, _Tp>
+    { typedef _Tp type; };
+
+  // __enable_if_t (std::enable_if_t for C++11)
+  template<bool _Cond, typename _Tp = void>
+    using __enable_if_t = typename enable_if<_Cond, _Tp>::type;
+
   template<bool>
     struct __conditional
     {
@@ -127,56 +142,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     using __type_identity_t = typename __type_identity<_Tp>::type;
 
-  template<typename...>
-    struct __or_;
-
-  template<>
-    struct __or_<>
-    : public false_type
-    { };
-
-  template<typename _B1>
-    struct __or_<_B1>
-    : public _B1
-    { };
-
-  template<typename _B1, typename _B2>
-    struct __or_<_B1, _B2>
-    : public __conditional_t<_B1::value, _B1, _B2>
-    { };
+  namespace __detail
+  {
+    // A variadic alias template that resolves to its first argument.
+    template<typename _Tp, typename...>
+      using __first_t = _Tp;
 
-  template<typename _B1, typename _B2, typename _B3, typename... _Bn>
-    struct __or_<_B1, _B2, _B3, _Bn...>
-    : public __conditional_t<_B1::value, _B1, __or_<_B2, _B3, _Bn...>>
-    { };
+    // These are deliberately not defined.
+    template<typename... _Bn>
+      auto __or_fn(int) -> __first_t<false_type,
+				     __enable_if_t<!bool(_Bn::value)>...>;
 
-  template<typename...>
-    struct __and_;
+    template<typename... _Bn>
+      auto __or_fn(...) -> true_type;
 
-  template<>
-    struct __and_<>
-    : public true_type
-    { };
+    template<typename... _Bn>
+      auto __and_fn(int) -> __first_t<true_type,
+				      __enable_if_t<bool(_Bn::value)>...>;
 
-  template<typename _B1>
-    struct __and_<_B1>
-    : public _B1
-    { };
+    template<typename... _Bn>
+      auto __and_fn(...) -> false_type;
+  } // namespace detail
 
-  template<typename _B1, typename _B2>
-    struct __and_<_B1, _B2>
-    : public __conditional_t<_B1::value, _B2, _B1>
-    { };
+  // Like C++17 std::dis/conjunction, but usable in C++11 and resolves
+  // to either true_type or false_type which allows for a more efficient
+  // implementation that avoids recursive class template instantiation.
+  template<typename... _Bn>
+    using __or_ = decltype(__detail::__or_fn<_Bn...>(0));
 
-  template<typename _B1, typename _B2, typename _B3, typename... _Bn>
-    struct __and_<_B1, _B2, _B3, _Bn...>
-    : public __conditional_t<_B1::value, __and_<_B2, _B3, _Bn...>, _B1>
-    { };
+  template<typename... _Bn>
+    using __and_ = decltype(__detail::__and_fn<_Bn...>(0));
 
   template<typename _Pp>
-    struct __not_
-    : public __bool_constant<!bool(_Pp::value)>
-    { };
+    using __not_ = __bool_constant<!bool(_Pp::value)>;
   /// @endcond
 
 #if __cplusplus >= 201703L
@@ -186,18 +184,47 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     inline constexpr bool __or_v = __or_<_Bn...>::value;
   template<typename... _Bn>
     inline constexpr bool __and_v = __and_<_Bn...>::value;
+
+  namespace __detail
+  {
+    template<typename /* = void */, typename _B1, typename... _Bn>
+      struct __disjunction_impl
+      { using type = _B1; };
+
+    template<typename _B1, typename _B2, typename... _Bn>
+      struct __disjunction_impl<__enable_if_t<!bool(_B1::value)>, _B1, _B2, _Bn...>
+      { using type = typename __disjunction_impl<void, _B2, _Bn...>::type; };
+
+    template<typename /* = void */, typename _B1, typename... _Bn>
+      struct __conjunction_impl
+      { using type = _B1; };
+
+    template<typename _B1, typename _B2, typename... _Bn>
+      struct __conjunction_impl<__enable_if_t<bool(_B1::value)>, _B1, _B2, _Bn...>
+      { using type = typename __conjunction_impl<void, _B2, _Bn...>::type; };
+  } // namespace __detail
   /// @endcond
 
 #define __cpp_lib_logical_traits 201510L
 
   template<typename... _Bn>
     struct conjunction
-    : __and_<_Bn...>
+    : __detail::__conjunction_impl<void, _Bn...>::type
+    { };
+
+  template<>
+    struct conjunction<>
+    : true_type
     { };
 
   template<typename... _Bn>
     struct disjunction
-    : __or_<_Bn...>
+    : __detail::__disjunction_impl<void, _Bn...>::type
+    { };
+
+  template<>
+    struct disjunction<>
+    : false_type
     { };
 
   template<typename _Pp>
@@ -2219,23 +2246,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     using __decay_and_strip = __strip_reference_wrapper<__decay_t<_Tp>>;
   /// @endcond
 
-  // Primary template.
-  /// Define a member typedef `type` only if a boolean constant is true.
-  template<bool, typename _Tp = void>
-    struct enable_if
-    { };
-
-  // Partial specialization for true.
-  template<typename _Tp>
-    struct enable_if<true, _Tp>
-    { typedef _Tp type; };
-
   /// @cond undocumented
 
-  // __enable_if_t (std::enable_if_t for C++11)
-  template<bool _Cond, typename _Tp = void>
-    using __enable_if_t = typename enable_if<_Cond, _Tp>::type;
-
   // Helper for SFINAE constraints
   template<typename... _Cond>
     using _Require = __enable_if_t<__and_<_Cond...>::value>;

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-08-26 19:11 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-26 19:11 [gcc r13-2230] libstdc++: Optimize std::con/disjunction, __and_/__or_, etc Patrick Palka

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).