Това не е конкретно за 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
все още са в регистър rdx
и че дължината на низа трябва да бъде същата, 8
.
Именно след като виждам неща като тези, обичам да се доверявам на компилатора си и да избягвам да се опитвам да подобря кода с битова игра. Това не помага, освен ако профилирането не открие неочаквано тясно място.
(с участието на MSVC 10 и моята реализация на std::string)
person
Bo Persson
schedule
24.07.2012
memcpy
и да генерират същия код за присвояване на структура (или за цикъл, копиращ масив). - person Bo Persson   schedule 25.07.2012