移动 std::shared_ptr 的开销?

问题描述 投票:0回答:1

这是一个 C++ 片段。 Func1 生成一个共享对象,直接移至 Func2 中。我们认为Func3中不应该有开销。将此代码片段放入 Compiler Explorer 中,我们发现 MSVC 的代码比 clang 或 gcc 短 2-3 倍。为什么会这样,也可以使用 clang/gcc 获得更短的代码吗?

看起来 Func3 生成了异常处理代码来清理临时共享对象。

#include <memory>

std::shared_ptr<double> Func1();
void Func2 (std::shared_ptr<double> s);

void Func3()
{
  Func2(Func1());
}
c++ move shared-ptr overhead-minimization
1个回答
0
投票

问题归结为平台 ABI,并且通过完全不透明的类型可以更好地说明:

struct A {
    A(const A&);
    A(A&&);
    ~A();
};

A make() noexcept;
void take(A) noexcept;

void foo() {
    take(make());
}

参见编译器资源管理器中的比较

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()
    产生类型为
    A
  • 的纯右值
  • 这被输入到
    take(A)
    的参数中
    • 发生强制复制省略,因此不会调用复制/移动构造函数
  • 只有 GCC 和 clang 会在调用站点销毁
    A

MSVC 相反会销毁临时

A
(或者在您的情况下,
std::shared_ptr
在被调用者内部,而不是在调用站点。您看到的额外代码是
std::shared_ptr
析构函数的内联版本。

最终,您应该不会看到任何重大的性能影响。在一种情况下,破坏发生在调用站点,而在另一种情况下,破坏发生在被调用者内部。必须进行大量的工作,无论付出还是承担,只是在不同的地点进行。

© www.soinside.com 2019 - 2024. All rights reserved.