libstdc++: Optimize std::jthread construction
diff mbox series

Message ID 20191116214716.GA14465@redhat.com
State New
Headers show
Series
  • libstdc++: Optimize std::jthread construction
Related show

Commit Message

Jonathan Wakely Nov. 16, 2019, 9:47 p.m. UTC
This change avoids storing a copy of a stop_token object that isn't
needed and won't be passed to the callable object. This slightly reduces
memory usage when the callable doesn't use a stop_token. It also removes
indirection in the invocation of the callable in the new thread, as
there is no lambda and no additional calls to std::invoke.

It also adds some missing [[nodiscard]] attributes, and the non-member
swap overload for std::jthread.

	* include/std/thread (jthread::jthread()): Use nostopstate constant.
	(jthread::jthread(Callable&&, Args&&...)): Use helper function to
	create std::thread instead of indirection through a lambda. Use
	remove_cvref_t instead of decay_t.
	(jthread::joinable(), jthread::get_id(), jthread::native_handle())
	(jthread::hardware_concurrency()): Add nodiscard attribute.
	(swap(jthread&. jthread&)): Define hidden friend.
	(jthread::_S_create): New helper function for constructor.

Tested powerpc64le-linux, committed to trunk.

As discussed with Tom, we also plan to replace the use of shared_ptr
in stop_source and stop_token with something more lightweight, that
doesn't store a weak count and two copies of the pointer.
commit 650ad104007e2f3474d735ec642b7613886cfcfe
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Sat Nov 16 21:32:05 2019 +0000

    libstdc++: Optimize std::jthread construction
    
    This change avoids storing a copy of a stop_token object that isn't
    needed and won't be passed to the callable object. This slightly reduces
    memory usage when the callable doesn't use a stop_token. It also removes
    indirection in the invocation of the callable in the new thread, as
    there is no lambda and no additional calls to std::invoke.
    
    It also adds some missing [[nodiscard]] attributes, and the non-member
    swap overload for std::jthread.
    
            * include/std/thread (jthread::jthread()): Use nostopstate constant.
            (jthread::jthread(Callable&&, Args&&...)): Use helper function to
            create std::thread instead of indirection through a lambda. Use
            remove_cvref_t instead of decay_t.
            (jthread::joinable(), jthread::get_id(), jthread::native_handle())
            (jthread::hardware_concurrency()): Add nodiscard attribute.
            (swap(jthread&. jthread&)): Define hidden friend.
            (jthread::_S_create): New helper function for constructor.

Patch
diff mbox series

diff --git a/libstdc++-v3/include/std/thread b/libstdc++-v3/include/std/thread
index 93afa766d18..010921b2160 100644
--- a/libstdc++-v3/include/std/thread
+++ b/libstdc++-v3/include/std/thread
@@ -425,31 +425,17 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     using native_handle_type = std::thread::native_handle_type;
 
     jthread() noexcept
-    : _M_stop_source{ nostopstate_t{ } }
+    : _M_stop_source{nostopstate}
     { }
 
     template<typename _Callable, typename... _Args,
-             typename = std::enable_if_t<!std::is_same_v<std::decay_t<_Callable>, jthread>>>
-    explicit
-    jthread(_Callable&& __f, _Args&&... __args)
-      : _M_thread{[](stop_token __token, auto&& __cb, auto&&... __args)
-                  {
-                    if constexpr(std::is_invocable_v<_Callable, stop_token, _Args...>)
-                      {
-                        std::invoke(std::forward<decltype(__cb)>(__cb),
-                                    std::move(__token),
-                                    std::forward<decltype(__args)>(__args)...);
-                      }
-                    else
-                      {
-                        std::invoke(std::forward<decltype(__cb)>(__cb),
-                                    std::forward<decltype(__args)>(__args)...);
-                      }
-                  },
-                  _M_stop_source.get_token(),
-                  std::forward<_Callable>(__f),
-                  std::forward<_Args>(__args)...}
-    { }
+	     typename = enable_if_t<!is_same_v<remove_cvref_t<_Callable>,
+					       jthread>>>
+      explicit
+      jthread(_Callable&& __f, _Args&&... __args)
+      : _M_thread{_S_create(_M_stop_source, std::forward<_Callable>(__f),
+			    std::forward<_Args>(__args)...)}
+      { }
 
     jthread(const jthread&) = delete;
     jthread(jthread&&) noexcept = default;
@@ -476,7 +462,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       std::swap(_M_thread, __other._M_thread);
     }
 
-    bool
+    [[nodiscard]] bool
     joinable() const noexcept
     {
       return _M_thread.joinable();
@@ -494,19 +480,19 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_thread.detach();
     }
 
-    id
+    [[nodiscard]] id
     get_id() const noexcept
     {
       _M_thread.get_id();
     }
 
-    native_handle_type
+    [[nodiscard]] native_handle_type
     native_handle()
     {
       return _M_thread.native_handle();
     }
 
-    static unsigned
+    [[nodiscard]] static unsigned
     hardware_concurrency() noexcept
     {
       return std::thread::hardware_concurrency();
@@ -529,7 +515,24 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return get_stop_source().request_stop();
     }
 
+    friend void swap(jthread& __lhs, jthread& __rhs) noexcept
+    {
+      __lhs.swap(__rhs);
+    }
+
   private:
+    template<typename _Callable, typename... _Args>
+      static thread
+      _S_create(stop_source& __ssrc, _Callable&& __f, _Args&&... __args)
+      {
+	if constexpr(is_invocable_v<_Callable, stop_token, _Args...>)
+	  return thread{std::forward<_Callable>(__f), __ssrc.get_token(),
+			std::forward<_Args>(__args)...};
+	else
+	  return thread{std::forward<_Callable>(__f),
+			std::forward<_Args>(__args)...};
+      }
+
     stop_source _M_stop_source;
     std::thread _M_thread;
   };