为什么 `std::make_shared` 使用 `-fno-rtti` 执行两个单独的分配?
Why does `std::make_shared` perform two separate allocations with `-fno-rtti`?
#include <memory>
struct foo { };
int main() { std::make_shared<foo>(); }
g++7
和 clang++5
与 -fno-exceptions -Ofast
为上述代码生成的程序集:
如果 -fno-rtti
未 通过,则包含对 operator new
的单个调用。
包含 两个单独的调用 到 operator new
如果 -fno-rtti
通过。
这个很容易验证on gcc.godbolt.org (clang++5
version):
为什么会这样?为什么禁用 RTTI 会阻止 make_shared
统一 object 和 control block 分配?
Why does disabling RTTI prevent make_shared from unifying the object and control block allocations?
你可以从汇编程序中看到(只粘贴文本确实比链接和拍照更可取)统一版本不分配简单的 foo
而是分配 std::_Sp_counted_ptr_inplace
,而且该类型有一个 vtable(回想一下,它通常需要一个虚拟析构函数,以处理自定义删除器)
mov QWORD PTR [rax], OFFSET FLAT:
vtable for
std::_Sp_counted_ptr_inplace<foo, std::allocator<foo>,
(__gnu_cxx::_Lock_policy)2>+16
如果禁用 RTTI,它无法生成就地计数指针,因为它需要是虚拟的。
注意non-inplace版本仍然引用了一个vtable,但它似乎只是直接存储去虚拟化的析构函数地址。
当然,std::shared_ptr
会在编译器支持rtti
的前提下实现。但它可以在没有它的情况下实施。参见 。
从这个旧的 GCC 的 libstdc++ 中得到启发 #42019 bug. We can see that Jonathan Wakely 添加了一个修复程序,使这在没有 RTTI 的情况下成为可能。
在 GCC 的 libstdc++ 中,std::make_shared
uses the services of std::allocated_shared
使用了非标准构造函数(如代码所示,转载如下)。
如本 patch, from line 753 所示,您可以看到获取默认删除器只需要使用 typeid
如果启用了 RTTI 的服务,否则, 它需要不依赖于 RTTI 的单独分配。
编辑: 9 - 2017 年 5 月:删除了之前在此处发布的受版权保护的代码
我没有调查过 libcxx
,但我想相信他们做过类似的事情....
没有充分的理由。这看起来像是 libstdc++ 中的 QoI 问题。
使用 clang 4.0,libc++ does not have this issue., while libstdc++ does.
带有 RTTI 的 libstdc++ 实现依赖于 get_deleter
:
void* __p = _M_refcount._M_get_deleter(typeid(__tag));
_M_ptr = static_cast<_Tp*>(__p);
__enable_shared_from_this_helper(_M_refcount, _M_ptr, _M_ptr);
_M_ptr = static_cast<_Tp*>(__p);
一般来说,get_deleter
没有 RTTI 是不可能实现的。
它似乎在这个实现中使用删除位置和标签来存储T
。
基本上使用的RTTI版本get_deleter
。 get_deleter
依赖 RTTI。让 make_shared
在没有 RTTI
的情况下工作需要重写它,他们采用了一种简单的方法,导致它进行两次分配。
make_shared
统一了 T
和引用计数块。我想对于可变大小的删除器和可变大小的 T
事情变得令人讨厌,所以他们重新使用删除器的可变大小块来存储 T
.
修改后的(内部)get_deleter
不执行 RTTI 并返回 void*
可能足以完成他们需要的删除程序;但可能不会。
#include <memory>
struct foo { };
int main() { std::make_shared<foo>(); }
g++7
和 clang++5
与 -fno-exceptions -Ofast
为上述代码生成的程序集:
如果
-fno-rtti
未 通过,则包含对operator new
的单个调用。包含 两个单独的调用 到
operator new
如果-fno-rtti
通过。
这个很容易验证on gcc.godbolt.org (clang++5
version):
为什么会这样?为什么禁用 RTTI 会阻止 make_shared
统一 object 和 control block 分配?
Why does disabling RTTI prevent make_shared from unifying the object and control block allocations?
你可以从汇编程序中看到(只粘贴文本确实比链接和拍照更可取)统一版本不分配简单的 foo
而是分配 std::_Sp_counted_ptr_inplace
,而且该类型有一个 vtable(回想一下,它通常需要一个虚拟析构函数,以处理自定义删除器)
mov QWORD PTR [rax], OFFSET FLAT:
vtable for
std::_Sp_counted_ptr_inplace<foo, std::allocator<foo>,
(__gnu_cxx::_Lock_policy)2>+16
如果禁用 RTTI,它无法生成就地计数指针,因为它需要是虚拟的。
注意non-inplace版本仍然引用了一个vtable,但它似乎只是直接存储去虚拟化的析构函数地址。
当然,std::shared_ptr
会在编译器支持rtti
的前提下实现。但它可以在没有它的情况下实施。参见
从这个旧的 GCC 的 libstdc++ 中得到启发 #42019 bug. We can see that Jonathan Wakely 添加了一个修复程序,使这在没有 RTTI 的情况下成为可能。
在 GCC 的 libstdc++ 中,std::make_shared
uses the services of std::allocated_shared
使用了非标准构造函数(如代码所示,转载如下)。
如本 patch, from line 753 所示,您可以看到获取默认删除器只需要使用 typeid
如果启用了 RTTI 的服务,否则, 它需要不依赖于 RTTI 的单独分配。
编辑: 9 - 2017 年 5 月:删除了之前在此处发布的受版权保护的代码
我没有调查过 libcxx
,但我想相信他们做过类似的事情....
没有充分的理由。这看起来像是 libstdc++ 中的 QoI 问题。
使用 clang 4.0,libc++ does not have this issue., while libstdc++ does.
带有 RTTI 的 libstdc++ 实现依赖于 get_deleter
:
void* __p = _M_refcount._M_get_deleter(typeid(__tag));
_M_ptr = static_cast<_Tp*>(__p);
__enable_shared_from_this_helper(_M_refcount, _M_ptr, _M_ptr);
_M_ptr = static_cast<_Tp*>(__p);
一般来说,get_deleter
没有 RTTI 是不可能实现的。
它似乎在这个实现中使用删除位置和标签来存储T
。
基本上使用的RTTI版本get_deleter
。 get_deleter
依赖 RTTI。让 make_shared
在没有 RTTI
的情况下工作需要重写它,他们采用了一种简单的方法,导致它进行两次分配。
make_shared
统一了 T
和引用计数块。我想对于可变大小的删除器和可变大小的 T
事情变得令人讨厌,所以他们重新使用删除器的可变大小块来存储 T
.
修改后的(内部)get_deleter
不执行 RTTI 并返回 void*
可能足以完成他们需要的删除程序;但可能不会。