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;
        }

Reply via email to