C++ std::string移动的实现
我正在调查移动C++ std::string移动的实现,c++,string,move-semantics,C++,String,Move Semantics,我正在调查移动std::string的性能。在很长一段时间里,我一直认为字符串移动几乎是免费的,认为编译器将内联所有内容,并且只涉及一些廉价的赋值 事实上,我的移动思维模式是 string& operator=(string&& rhs) noexcept { swap(*this, rhs); return *this; } friend void swap(string& x, string& y) noexcept { //
std::string
的性能。在很长一段时间里,我一直认为字符串移动几乎是免费的,认为编译器将内联所有内容,并且只涉及一些廉价的赋值
事实上,我的移动思维模式是
string& operator=(string&& rhs) noexcept
{
swap(*this, rhs);
return *this;
}
friend void swap(string& x, string& y) noexcept
{
// for disposition only
unsigned char buf[sizeof(string)];
memcpy(buf, &x, sizeof(string));
memcpy(&x, &y, sizeof(string));
memcpy(&y, buf, sizeof(string));
}
据我所知,如果memcpy
更改为分配单个字段,则这是一种合法的实现
我非常惊讶地发现gcc实现了移动应用程序
这是一致的吗?同样重要的是,我不应该认为搬家几乎是免费的吗
令人困惑的是,达到了我的预期
虽然有一个可疑的
std::string::reserve
并不是一个确切的答案,但这是C++11std::string
的新实现,没有引用计数器,并且具有小字符串优化,这导致了大量的组装。特别是,小字符串优化导致4个分支处理移动分配的源和目标的4种不同长度组合
当添加-D_GLIBCXX_USE_cx11_ABI=0
选项时,将使用带有参考计数器的pre C++-11std::string
,并进行不小的字符串优化
我不应该认为搬家几乎是免费的吗 《谈话》第47页写道: 复制和移动的比较
- 许多人错误地认为搬家实际上是“免费的”
- 复制和移动之间的性能差异很大
- 对于基本类型(如int),复制或移动实际上是相同的
- 当只需要传输对象的一部分来传输整个值时,移动比复制快
我只分析了GCC的版本。发生了这样的事情:代码处理不同类型的分配器。如果分配器具有
\u S\u move\u assign
或\u S\u always\u equal
的特性,那么移动几乎是免费的,正如您所期望的那样。这是move操作符=
中的if
:
if (!__str._M_is_local()
&& (_Alloc_traits::_S_propagate_on_move_assign()
|| _Alloc_traits::_S_always_equal()))
// cheap move
else assign(__str);
如果条件为true(\u M\u is\u local()
表示小字符串,说明),则移动成本较低
如果为false,则调用normalassign
(不是移动的)。当以下情况之一发生时,即为这种情况:
- 字符串很小,因此
将执行一个简单的memcpy(廉价)assign
- 或者,分配器没有特征总是相等,也没有在移动分配时传播,因此分配将分配(不便宜)
另一方面,生成的代码不必要地庞大,我认为可以改进。它应该有一个单独的代码来处理通常的分配器,或者有一个更好的
assign
code(问题是assign
没有检查\u M\u is\u local()
,但它会进行容量检查,因此编译器无法决定是否需要分配,因此它会不必要地将分配代码路径放入可执行文件中-您可以查看源代码中的确切细节)。使用SSO(短字符串优化)移动可以复制,因为您无法移动数组。@NathanOliver我知道,但是memcpy
已经涵盖了这一点。至少,我希望有一个大小为sizeof(string)
的缓冲区来临时复制数组的内容,而不是创建一个临时stringAdd`-stdlib=libc++`到clang窗口。你仍然在libc++中发现了一个bug<代码>基本\u字符串::\u清除\u和\u收缩()应标记为noexcept
。通过添加该选项,移动分配操作符可以很好地进行清理。\uu clear\u和\u shrink
显然是最近的一次编辑。我在看主干libc++的顶端:\u Alloc\u traits::\u S\u propagate\u on\u move\u assign
和\u Alloc\u traits::\u S\u everys\u equal
是constepr
函数。这些分支不应该在编译时被删除而不存在于程序集中吗?@MaximeGroushkin:它们不存在于程序集中。它首先在locality属性上分支(我在回答中包含的代码),因此assign
代码必须在中编译。然后,assign
代码不检查locality属性,而是检查容量(从assign
的角度来看,这是可以理解的,因为容量在那里很重要,而不是位置)。编译器不够聪明(可以理解),无法将这两件事关联在一起,因此它将分配代码路径放入exe中。您可能需要提到\u M_is_local
是用于小字符串优化的。