Message ID | 20150923161841.GC2969@redhat.com |
---|---|
State | New |
Headers | show |
On 23/09/15 17:18 +0100, Jonathan Wakely wrote: >For PR 65393 I avoided some unnecessary shared_ptr copies while >launching a std::thread. This goes further and avoids shared_ptr >entirely, using unique_ptr instead. This reduces the memory overhead >of a std::thread by 32 bytes (on 64-bit) and avoids any >reference-count updates. > >The downside is it exports some new symbols, and we have to keep the >old code for backwards compatibility, but I think it's worth doing. > >Does anybody disagree? Tested powerpc64le-linux and x86_64-dragonfly4.1. Committed to trunk. >commit 2d7e89aae8ac12dd7a6b2083e5169679c1200cc5 >Author: Jonathan Wakely <jwakely@redhat.com> >Date: Thu Mar 12 13:23:23 2015 +0000 > > Reduce space and time overhead of std::thread > > PR libstdc++/65393 > * config/abi/pre/gnu.ver: Export new symbols. > * include/std/thread (thread::_State, thread::_State_impl): New types. > (thread::_M_start_thread): Add overload taking unique_ptr<_State>. > (thread::_M_make_routine): Remove. > (thread::_S_make_state): Add. > (thread::_Impl_base, thread::_Impl, thread::_M_start_thread) > [_GLIBCXX_THREAD_ABI_COMPAT] Only declare conditionally. > * src/c++11/thread.cc (execute_native_thread_routine): Rename to > execute_native_thread_routine_compat and re-define to use _State. > (thread::_State::~_State()): Define. > (thread::_M_make_thread): Define new overload. > (thread::_M_make_thread) [_GLIBCXX_THREAD_ABI_COMPAT]: Only define old > overloads conditionally. > >diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver >index d42cd37..08d9bc6 100644 >--- a/libstdc++-v3/config/abi/pre/gnu.ver >+++ b/libstdc++-v3/config/abi/pre/gnu.ver >@@ -1870,6 +1870,11 @@ GLIBCXX_3.4.22 { > # std::uncaught_exceptions() > _ZSt19uncaught_exceptionsv; > >+ # std::thread::_State::~_State() >+ _ZT[ISV]NSt6thread6_StateE; >+ _ZNSt6thread6_StateD[012]Ev; >+ _ZNSt6thread15_M_start_threadESt10unique_ptrINS_6_StateESt14default_deleteIS1_EEPFvvE; >+ > } GLIBCXX_3.4.21; > > # Symbols in the support library (libsupc++) have their own tag. >diff --git a/libstdc++-v3/include/std/thread b/libstdc++-v3/include/std/thread >index ebbda62..c67ec46 100644 >--- a/libstdc++-v3/include/std/thread >+++ b/libstdc++-v3/include/std/thread >@@ -60,9 +60,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > class thread > { > public: >+ // Abstract base class for types that wrap arbitrary functors to be >+ // invoked in the new thread of execution. >+ struct _State >+ { >+ virtual ~_State(); >+ virtual void _M_run() = 0; >+ }; >+ using _State_ptr = unique_ptr<_State>; >+ > typedef __gthread_t native_handle_type; >- struct _Impl_base; >- typedef shared_ptr<_Impl_base> __shared_base_type; > > /// thread::id > class id >@@ -92,29 +99,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > operator<<(basic_ostream<_CharT, _Traits>& __out, thread::id __id); > }; > >- // Simple base type that the templatized, derived class containing >- // an arbitrary functor can be converted to and called. >- struct _Impl_base >- { >- __shared_base_type _M_this_ptr; >- >- inline virtual ~_Impl_base(); >- >- virtual void _M_run() = 0; >- }; >- >- template<typename _Callable> >- struct _Impl : public _Impl_base >- { >- _Callable _M_func; >- >- _Impl(_Callable&& __f) : _M_func(std::forward<_Callable>(__f)) >- { } >- >- void >- _M_run() { _M_func(); } >- }; >- > private: > id _M_id; > >@@ -133,16 +117,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > thread(_Callable&& __f, _Args&&... __args) > { > #ifdef GTHR_ACTIVE_PROXY >- // Create a reference to pthread_create, not just the gthr weak symbol >- _M_start_thread(_M_make_routine(std::__bind_simple( >- std::forward<_Callable>(__f), >- std::forward<_Args>(__args)...)), >- reinterpret_cast<void(*)()>(&pthread_create)); >+ // Create a reference to pthread_create, not just the gthr weak symbol. >+ auto __depend = reinterpret_cast<void(*)()>(&pthread_create); > #else >- _M_start_thread(_M_make_routine(std::__bind_simple( >- std::forward<_Callable>(__f), >- std::forward<_Args>(__args)...))); >+ auto __depend = nullptr; > #endif >+ _M_start_thread(_S_make_state( >+ std::__bind_simple(std::forward<_Callable>(__f), >+ std::forward<_Args>(__args)...)), >+ __depend); > } > > ~thread() >@@ -190,23 +173,48 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > hardware_concurrency() noexcept; > > private: >+ template<typename _Callable> >+ struct _State_impl : public _State >+ { >+ _Callable _M_func; >+ >+ _State_impl(_Callable&& __f) : _M_func(std::forward<_Callable>(__f)) >+ { } >+ >+ void >+ _M_run() { _M_func(); } >+ }; >+ >+ void >+ _M_start_thread(_State_ptr, void (*)()); >+ >+ template<typename _Callable> >+ static _State_ptr >+ _S_make_state(_Callable&& __f) >+ { >+ using _Impl = _State_impl<_Callable>; >+ return _State_ptr{new _Impl{std::forward<_Callable>(__f)}}; >+ } >+#if _GLIBCXX_THREAD_ABI_COMPAT >+ public: >+ struct _Impl_base; >+ typedef shared_ptr<_Impl_base> __shared_base_type; >+ struct _Impl_base >+ { >+ __shared_base_type _M_this_ptr; >+ virtual ~_Impl_base() = default; >+ virtual void _M_run() = 0; >+ }; >+ >+ private: > void > _M_start_thread(__shared_base_type, void (*)()); > > void > _M_start_thread(__shared_base_type); >- >- template<typename _Callable> >- shared_ptr<_Impl<_Callable>> >- _M_make_routine(_Callable&& __f) >- { >- // Create and allocate full data structure, not base. >- return std::make_shared<_Impl<_Callable>>(std::forward<_Callable>(__f)); >- } >+#endif > }; > >- inline thread::_Impl_base::~_Impl_base() = default; >- > inline void > swap(thread& __x, thread& __y) noexcept > { __x.swap(__y); } >diff --git a/libstdc++-v3/src/c++11/thread.cc b/libstdc++-v3/src/c++11/thread.cc >index 906cafa..e116afa 100644 >--- a/libstdc++-v3/src/c++11/thread.cc >+++ b/libstdc++-v3/src/c++11/thread.cc >@@ -23,6 +23,7 @@ > // <http://www.gnu.org/licenses/>. > > >+#define _GLIBCXX_THREAD_ABI_COMPAT 1 > #include <thread> > #include <system_error> > #include <cerrno> >@@ -75,8 +76,33 @@ namespace std _GLIBCXX_VISIBILITY(default) > extern "C" void* > execute_native_thread_routine(void* __p) > { >+ thread::_State_ptr __t{ static_cast<thread::_State*>(__p) }; >+ >+ __try >+ { >+ __t->_M_run(); >+ } >+ __catch(const __cxxabiv1::__forced_unwind&) >+ { >+ __throw_exception_again; >+ } >+ __catch(...) >+ { >+ std::terminate(); >+ } >+ >+ return nullptr; >+ } >+ >+#if _GLIBCXX_THREAD_ABI_COMPAT >+ extern "C" void* >+ execute_native_thread_routine_compat(void* __p) >+ { > thread::_Impl_base* __t = static_cast<thread::_Impl_base*>(__p); > thread::__shared_base_type __local; >+ // Now that a new thread has been created we can transfer ownership of >+ // the thread state to a local object, breaking the reference cycle >+ // created in thread::_M_start_thread. > __local.swap(__t->_M_this_ptr); > > __try >@@ -94,10 +120,13 @@ namespace std _GLIBCXX_VISIBILITY(default) > > return nullptr; > } >+#endif > } > > _GLIBCXX_BEGIN_NAMESPACE_VERSION > >+ thread::_State::~_State() = default; >+ > void > thread::join() > { >@@ -127,6 +156,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > } > > void >+ thread::_M_start_thread(_State_ptr state, void (*)()) >+ { >+ const int err = __gthread_create(&_M_id._M_thread, >+ &execute_native_thread_routine, >+ state.get()); >+ if (err) >+ __throw_system_error(err); >+ state.release(); >+ } >+ >+#if _GLIBCXX_THREAD_ABI_COMPAT >+ void > thread::_M_start_thread(__shared_base_type __b) > { > if (!__gthread_active_p()) >@@ -144,15 +185,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > thread::_M_start_thread(__shared_base_type __b, void (*)()) > { > auto ptr = __b.get(); >+ // Create a reference cycle that will be broken in the new thread. > ptr->_M_this_ptr = std::move(__b); > int __e = __gthread_create(&_M_id._M_thread, >- &execute_native_thread_routine, ptr); >+ &execute_native_thread_routine_compat, ptr); > if (__e) > { >- ptr->_M_this_ptr.reset(); >+ ptr->_M_this_ptr.reset(); // break reference cycle, destroying *ptr. > __throw_system_error(__e); > } > } >+#endif > > unsigned int > thread::hardware_concurrency() noexcept
commit 2d7e89aae8ac12dd7a6b2083e5169679c1200cc5 Author: Jonathan Wakely <jwakely@redhat.com> Date: Thu Mar 12 13:23:23 2015 +0000 Reduce space and time overhead of std::thread PR libstdc++/65393 * config/abi/pre/gnu.ver: Export new symbols. * include/std/thread (thread::_State, thread::_State_impl): New types. (thread::_M_start_thread): Add overload taking unique_ptr<_State>. (thread::_M_make_routine): Remove. (thread::_S_make_state): Add. (thread::_Impl_base, thread::_Impl, thread::_M_start_thread) [_GLIBCXX_THREAD_ABI_COMPAT] Only declare conditionally. * src/c++11/thread.cc (execute_native_thread_routine): Rename to execute_native_thread_routine_compat and re-define to use _State. (thread::_State::~_State()): Define. (thread::_M_make_thread): Define new overload. (thread::_M_make_thread) [_GLIBCXX_THREAD_ABI_COMPAT]: Only define old overloads conditionally. diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index d42cd37..08d9bc6 100644 --- a/libstdc++-v3/config/abi/pre/gnu.ver +++ b/libstdc++-v3/config/abi/pre/gnu.ver @@ -1870,6 +1870,11 @@ GLIBCXX_3.4.22 { # std::uncaught_exceptions() _ZSt19uncaught_exceptionsv; + # std::thread::_State::~_State() + _ZT[ISV]NSt6thread6_StateE; + _ZNSt6thread6_StateD[012]Ev; + _ZNSt6thread15_M_start_threadESt10unique_ptrINS_6_StateESt14default_deleteIS1_EEPFvvE; + } GLIBCXX_3.4.21; # Symbols in the support library (libsupc++) have their own tag. diff --git a/libstdc++-v3/include/std/thread b/libstdc++-v3/include/std/thread index ebbda62..c67ec46 100644 --- a/libstdc++-v3/include/std/thread +++ b/libstdc++-v3/include/std/thread @@ -60,9 +60,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION class thread { public: + // Abstract base class for types that wrap arbitrary functors to be + // invoked in the new thread of execution. + struct _State + { + virtual ~_State(); + virtual void _M_run() = 0; + }; + using _State_ptr = unique_ptr<_State>; + typedef __gthread_t native_handle_type; - struct _Impl_base; - typedef shared_ptr<_Impl_base> __shared_base_type; /// thread::id class id @@ -92,29 +99,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION operator<<(basic_ostream<_CharT, _Traits>& __out, thread::id __id); }; - // Simple base type that the templatized, derived class containing - // an arbitrary functor can be converted to and called. - struct _Impl_base - { - __shared_base_type _M_this_ptr; - - inline virtual ~_Impl_base(); - - virtual void _M_run() = 0; - }; - - template<typename _Callable> - struct _Impl : public _Impl_base - { - _Callable _M_func; - - _Impl(_Callable&& __f) : _M_func(std::forward<_Callable>(__f)) - { } - - void - _M_run() { _M_func(); } - }; - private: id _M_id; @@ -133,16 +117,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION thread(_Callable&& __f, _Args&&... __args) { #ifdef GTHR_ACTIVE_PROXY - // Create a reference to pthread_create, not just the gthr weak symbol - _M_start_thread(_M_make_routine(std::__bind_simple( - std::forward<_Callable>(__f), - std::forward<_Args>(__args)...)), - reinterpret_cast<void(*)()>(&pthread_create)); + // Create a reference to pthread_create, not just the gthr weak symbol. + auto __depend = reinterpret_cast<void(*)()>(&pthread_create); #else - _M_start_thread(_M_make_routine(std::__bind_simple( - std::forward<_Callable>(__f), - std::forward<_Args>(__args)...))); + auto __depend = nullptr; #endif + _M_start_thread(_S_make_state( + std::__bind_simple(std::forward<_Callable>(__f), + std::forward<_Args>(__args)...)), + __depend); } ~thread() @@ -190,23 +173,48 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION hardware_concurrency() noexcept; private: + template<typename _Callable> + struct _State_impl : public _State + { + _Callable _M_func; + + _State_impl(_Callable&& __f) : _M_func(std::forward<_Callable>(__f)) + { } + + void + _M_run() { _M_func(); } + }; + + void + _M_start_thread(_State_ptr, void (*)()); + + template<typename _Callable> + static _State_ptr + _S_make_state(_Callable&& __f) + { + using _Impl = _State_impl<_Callable>; + return _State_ptr{new _Impl{std::forward<_Callable>(__f)}}; + } +#if _GLIBCXX_THREAD_ABI_COMPAT + public: + struct _Impl_base; + typedef shared_ptr<_Impl_base> __shared_base_type; + struct _Impl_base + { + __shared_base_type _M_this_ptr; + virtual ~_Impl_base() = default; + virtual void _M_run() = 0; + }; + + private: void _M_start_thread(__shared_base_type, void (*)()); void _M_start_thread(__shared_base_type); - - template<typename _Callable> - shared_ptr<_Impl<_Callable>> - _M_make_routine(_Callable&& __f) - { - // Create and allocate full data structure, not base. - return std::make_shared<_Impl<_Callable>>(std::forward<_Callable>(__f)); - } +#endif }; - inline thread::_Impl_base::~_Impl_base() = default; - inline void swap(thread& __x, thread& __y) noexcept { __x.swap(__y); } diff --git a/libstdc++-v3/src/c++11/thread.cc b/libstdc++-v3/src/c++11/thread.cc index 906cafa..e116afa 100644 --- a/libstdc++-v3/src/c++11/thread.cc +++ b/libstdc++-v3/src/c++11/thread.cc @@ -23,6 +23,7 @@ // <http://www.gnu.org/licenses/>. +#define _GLIBCXX_THREAD_ABI_COMPAT 1 #include <thread> #include <system_error> #include <cerrno> @@ -75,8 +76,33 @@ namespace std _GLIBCXX_VISIBILITY(default) extern "C" void* execute_native_thread_routine(void* __p) { + thread::_State_ptr __t{ static_cast<thread::_State*>(__p) }; + + __try + { + __t->_M_run(); + } + __catch(const __cxxabiv1::__forced_unwind&) + { + __throw_exception_again; + } + __catch(...) + { + std::terminate(); + } + + return nullptr; + } + +#if _GLIBCXX_THREAD_ABI_COMPAT + extern "C" void* + execute_native_thread_routine_compat(void* __p) + { thread::_Impl_base* __t = static_cast<thread::_Impl_base*>(__p); thread::__shared_base_type __local; + // Now that a new thread has been created we can transfer ownership of + // the thread state to a local object, breaking the reference cycle + // created in thread::_M_start_thread. __local.swap(__t->_M_this_ptr); __try @@ -94,10 +120,13 @@ namespace std _GLIBCXX_VISIBILITY(default) return nullptr; } +#endif } _GLIBCXX_BEGIN_NAMESPACE_VERSION + thread::_State::~_State() = default; + void thread::join() { @@ -127,6 +156,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } void + thread::_M_start_thread(_State_ptr state, void (*)()) + { + const int err = __gthread_create(&_M_id._M_thread, + &execute_native_thread_routine, + state.get()); + if (err) + __throw_system_error(err); + state.release(); + } + +#if _GLIBCXX_THREAD_ABI_COMPAT + void thread::_M_start_thread(__shared_base_type __b) { if (!__gthread_active_p()) @@ -144,15 +185,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION thread::_M_start_thread(__shared_base_type __b, void (*)()) { auto ptr = __b.get(); + // Create a reference cycle that will be broken in the new thread. ptr->_M_this_ptr = std::move(__b); int __e = __gthread_create(&_M_id._M_thread, - &execute_native_thread_routine, ptr); + &execute_native_thread_routine_compat, ptr); if (__e) { - ptr->_M_this_ptr.reset(); + ptr->_M_this_ptr.reset(); // break reference cycle, destroying *ptr. __throw_system_error(__e); } } +#endif unsigned int thread::hardware_concurrency() noexcept