这不是具体的,swap
而是一个示例,表明低级优化可能不值得麻烦。无论如何,编译器通常都会弄清楚。
当然,这是我最喜欢的例子,编译器非常幸运,但无论如何我们不应该认为编译器是愚蠢的,我们可以通过一些简单的技巧轻松改进生成的代码。
我的测试代码是 - 构造一个 std::string 并复制它。
std::string whatever = "abcdefgh";
std::string whatever2 = whatever;
第一个构造函数看起来像这样
basic_string(const value_type* _String,
const allocator_type& _Allocator = allocator_type() ) : _Parent(_Allocator)
{
const size_type _StringSize = traits_type::length(_String);
if (_MySmallStringCapacity < _StringSize)
{
_AllocateAndCopy(_String, _StringSize);
}
else
{
traits_type::copy(_MySmallString._Buffer, _String, _StringSize);
_SetSmallStringCapacity();
_SetSize(_StringSize);
}
}
生成的代码是
std::string whatever = "abcdefgh";
000000013FCC30C3 mov rdx,qword ptr [string "abcdefgh" (13FD07498h)]
000000013FCC30CA mov qword ptr [whatever],rdx
000000013FCC30D2 mov byte ptr [rsp+347h],0
000000013FCC30DA mov qword ptr [rsp+348h],8
000000013FCC30E6 mov byte ptr [rsp+338h],0
这里traits_type::copy
包含对 的调用memcpy
,它被优化为整个字符串的单个寄存器副本(仔细选择以适应)。编译器还将调用strlen
转换为编译时间8
。
然后我们将它复制到一个新的字符串中。复制构造函数看起来像这样
basic_string(const basic_string& _String)
: _Parent(std::allocator_traits<allocator_type>::select_on_container_copy_construction(_String._MyAllocator))
{
if (_MySmallStringCapacity < _String.size())
{
_AllocateAndCopy(_String);
}
else
{
traits_type::copy(_MySmallString._Buffer, _String.data(), _String.size());
_SetSmallStringCapacity();
_SetSize(_String.size());
}
}
结果只有 4 条机器指令:
std::string whatever2 = whatever;
000000013FCC30EE mov qword ptr [whatever2],rdx
000000013FCC30F6 mov byte ptr [rsp+6CFh],0
000000013FCC30FE mov qword ptr [rsp+6D0h],8
000000013FCC310A mov byte ptr [rsp+6C0h],0
请注意,优化器会记住char
's 仍在寄存器rdx
中,并且字符串长度必须相同,8
。
正是在看到这样的事情之后,我才喜欢相信我的编译器,并避免尝试用一些小技巧来改进代码。它没有帮助,除非分析发现意外的瓶颈。
(具有 MSVC 10 和我的 std::string 实现)