https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87514
Bug ID: 87514 Summary: std::make_shared could avoid virtual call to _M_get_deleter() Product: gcc Version: 9.0 Status: UNCONFIRMED Keywords: missed-optimization Severity: enhancement Priority: P3 Component: libstdc++ Assignee: unassigned at gcc dot gnu.org Reporter: redi at gcc dot gnu.org Target Milestone: --- In __shared_ptr(_Sp_make_shared_tag, const _Alloc&, _Args&&...) we do: // _M_ptr needs to point to the newly constructed object. // This relies on _Sp_counted_ptr_inplace::_M_get_deleter. #if __cpp_rtti void* __p = _M_refcount._M_get_deleter(typeid(__tag)); #else void* __p = _M_refcount._M_get_deleter(_Sp_make_shared_tag::_S_ti()); #endif _M_refcount._M_get_deleter does: void* _M_get_deleter(const std::type_info& __ti) const noexcept { return _M_pi ? _M_pi->_M_get_deleter(__ti) : nullptr; } which does an indirect virtual call _Sp_counted_ptr_inplace::_M_get_deleter. In the __shared_ptr(_Sp_make_shared_tag ...) constructor we know the dynamic type of *_M_refcount._M_pi is definitely _Sp_counted_ptr_inplace and so could theoretically avoid the dynamic dispatch. This would require breaking encapsulation to allow __shared_ptr to access the private __shared_count::_M_pi member. Is it worth it? It's possible this can already be devirtualized by the compiler, but only when optimising. If it isn't, then this might help: @@ -653,9 +652,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typedef _Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp> _Sp_cp_type; typename _Sp_cp_type::__allocator_type __a2(__a); auto __guard = std::__allocate_guarded(__a2); - _Sp_cp_type* __mem = __guard.get(); - ::new (__mem) _Sp_cp_type(__a, std::forward<_Args>(__args)...); - _M_pi = __mem; + _M_pi = ::new (__guard.get()) + _Sp_cp_type(__a, std::forward<_Args>(__args)...); __guard = nullptr; }