* [patch] Reduce space and time overhead of std::thread
@ 2015-09-23 17:07 Jonathan Wakely
2015-09-29 13:24 ` Jonathan Wakely
0 siblings, 1 reply; 2+ messages in thread
From: Jonathan Wakely @ 2015-09-23 17:07 UTC (permalink / raw)
To: libstdc++, gcc-patches
[-- Attachment #1: Type: text/plain, Size: 444 bytes --]
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?
[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 7891 bytes --]
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
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [patch] Reduce space and time overhead of std::thread
2015-09-23 17:07 [patch] Reduce space and time overhead of std::thread Jonathan Wakely
@ 2015-09-29 13:24 ` Jonathan Wakely
0 siblings, 0 replies; 2+ messages in thread
From: Jonathan Wakely @ 2015-09-29 13:24 UTC (permalink / raw)
To: libstdc++, gcc-patches
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
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2015-09-29 12:55 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-23 17:07 [patch] Reduce space and time overhead of std::thread Jonathan Wakely
2015-09-29 13:24 ` Jonathan Wakely
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).