накладные расходы на перемещение std::shared_ptr?
Вот фрагмент C++.Func1
генерирует общий объект, который непосредственно перемещается вFunc2
. Мы считаем, что в . Поместив этот фрагмент в Compiler Explorer, мы видим в 2-3 раза более короткий код с MSVC по сравнению с clang или GCC. Почему это так и можно ли получить более короткий код с помощью clang/GCC?
Это выглядит какFunc3
генерирует код обработки исключений для очистки временного общего объекта.
#include <memory>
std::shared_ptr<double> Func1();
void Func2 (std::shared_ptr<double> s);
void Func3()
{
Func2(Func1());
}
1 ответ
Проблема сводится к ABI платформы и лучше иллюстрируется совершенно непрозрачным типом:
struct A {
A(const A&);
A(A&&);
~A();
};
A make() noexcept;
void take(A) noexcept;
void foo() {
take(make());
}
См. сравнение в Compiler Explorer.
Выход MSVC
void foo(void) PROC
push ecx
push ecx
push esp
call A make(void)
add esp, 4
call void take(A)
add esp, 8
ret 0
void foo(void) ENDP
Вывод GCC (clang очень похож)
foo():
sub rsp, 24
lea rdi, [rsp+15]
call make()
lea rdi, [rsp+15]
call take(A)
lea rdi, [rsp+15]
call A::~A() [complete object destructor]
add rsp, 24
ret
Если тип имеет нетривиальный деструктор, вызывающая сторона вызывает этот деструктор после возврата к нему управления (в том числе, когда вызывающая сторона генерирует исключение).
- Itanium C++ ABI §3.1.2.3 Нетривиальные параметры
Объяснение
Здесь происходит следующее:
-
make()
возвращает значение типа - это подается в параметр
take(A)
- происходит обязательное копирование, поэтому конструкторы копирования/перемещения не вызываются.
- только GCC и clang уничтожают на месте вызова
Вместо этого MSVC уничтожает временныйA
(или в вашем случае ) внутри вызываемого абонента, а не на месте вызова. Дополнительный код, который вы видите, представляет собой встроенную версиюstd::shared_ptr
деструктор.
В конце концов, вы не должны увидеть какого-либо серьезного влияния на производительность. Однако, еслиFunc2
сбрасывает/освобождает общий указатель, то, к сожалению, большая часть кода деструктора на месте вызова не работает. Эта проблема ABI аналогична проблеме сstd::unique_ptr
:
Существует также языковая проблема, связанная с порядком уничтожения параметров функции и выполнением деструктора. Для простоты это игнорируется в данной статье, но полное решение "
unique_ptr
так же дешево пройтиT*
"придется решить и эту проблему.
Смотрите также
Агнер Туман. - Соглашения о вызовах для разных компиляторов C++ и операционных систем.