如何以更好的性能传递和共享 shared_ptr 所有权?
How to pass and share shared_ptr ownership with a better performance?
例如
class A
{
public:
// Option 1
void setI_1(shared_ptr<int> i) { m_i = i; }
// Option 2
void setI_2(shared_ptr<int> i) { m_i = move(i); }
// Option 3
void setI_3(shared_ptr<int> const& i) { m_i = i; }
private:
shared_ptr<int> m_i;
};
通常哪个选项更好?
我在 Visual Studio 2017 年做基准测试。选项 2 在我测试的所有情况下都给了我最好的性能。在某些情况下,选项 3 的性能与选项 2 相似,但在某些情况下更差。谢谢!
在你的情况下,选项 2 是最好的情况。您可能希望将指针移动到 class,这样当您不再需要它时,就不需要一次又一次地增加和减少 reference-counter:
{
shared_ptr<int> ptr = std::make_shared<int>(42);
A a;
a.setI_2(std::move(ptr)); // I no longer need ptr, so I can move it
};
指针移动到参数列表,ptr
是 null/the 参数用 r-value-reference 引用 ptr
初始化,因此 ptr 为空。然后从参数列表中移到成员中,参数为空。你看,没有(需要)副本,这意味着 reference-counter.
的变化
我更喜欢 T const&
仅适用于仅从引用读取且不用于初始化另一个变量的函数。如果在示例中使用了带有引用的第三个选项,则需要复制指针,因为 setI_3 的主体在赋值语句中有一个副本(没有移动)。
第一个选项显然是最差的,因为指针被复制了多次。
当您将 shared_ptr
与 unique_ptr
交换时,编译器会强制您使用尽可能快的方式 - 移动。
我要添加第四个选项:
// Option 4
void setI_4(shared_ptr<int>&& i) { m_i = move(i); }
请注意,即使 i
是 r-value-reference,它在代码中使用时也不是 r-value-reference,因此在没有移动时会被复制;
第四个选项仅与第二个选项不同,传递的参数始终需要是一个 r-value-reference,而在第二个选项中,i
本身就是一个 pointer-variable在第四个 i
中是一个引用(函数主体可以用它做任何事情,但它存在于函数之外(std::move 允许这样做))。 parameter-variable 是从调用者的 parameter-tuple 中的表达式构造的,如果 shared_ptr
的构造函数自动复制指针(这意味着 reference-counter 增加)不是 r-value-reference.
setI_2的调用方式如下:
{
shared_ptr<int> ptr = std::make_shared<int>(42);
/// ptr owns 42
A a;
{ // a.setI_2(std::move(ptr));
// initializing the parameter
shared_ptr<int> i{std::move(ptr)};
// now i owns 42 and ptr no longer owns it and points to null
{ // the body of the function
a.m_i = std::move(i);
// now a.m_i owns 42 and i no longer owns it and points to null
};
};
};
例如
class A
{
public:
// Option 1
void setI_1(shared_ptr<int> i) { m_i = i; }
// Option 2
void setI_2(shared_ptr<int> i) { m_i = move(i); }
// Option 3
void setI_3(shared_ptr<int> const& i) { m_i = i; }
private:
shared_ptr<int> m_i;
};
通常哪个选项更好?
我在 Visual Studio 2017 年做基准测试。选项 2 在我测试的所有情况下都给了我最好的性能。在某些情况下,选项 3 的性能与选项 2 相似,但在某些情况下更差。谢谢!
在你的情况下,选项 2 是最好的情况。您可能希望将指针移动到 class,这样当您不再需要它时,就不需要一次又一次地增加和减少 reference-counter:
{
shared_ptr<int> ptr = std::make_shared<int>(42);
A a;
a.setI_2(std::move(ptr)); // I no longer need ptr, so I can move it
};
指针移动到参数列表,ptr
是 null/the 参数用 r-value-reference 引用 ptr
初始化,因此 ptr 为空。然后从参数列表中移到成员中,参数为空。你看,没有(需要)副本,这意味着 reference-counter.
我更喜欢 T const&
仅适用于仅从引用读取且不用于初始化另一个变量的函数。如果在示例中使用了带有引用的第三个选项,则需要复制指针,因为 setI_3 的主体在赋值语句中有一个副本(没有移动)。
第一个选项显然是最差的,因为指针被复制了多次。
当您将 shared_ptr
与 unique_ptr
交换时,编译器会强制您使用尽可能快的方式 - 移动。
我要添加第四个选项:
// Option 4
void setI_4(shared_ptr<int>&& i) { m_i = move(i); }
请注意,即使 i
是 r-value-reference,它在代码中使用时也不是 r-value-reference,因此在没有移动时会被复制;
第四个选项仅与第二个选项不同,传递的参数始终需要是一个 r-value-reference,而在第二个选项中,i
本身就是一个 pointer-variable在第四个 i
中是一个引用(函数主体可以用它做任何事情,但它存在于函数之外(std::move 允许这样做))。 parameter-variable 是从调用者的 parameter-tuple 中的表达式构造的,如果 shared_ptr
的构造函数自动复制指针(这意味着 reference-counter 增加)不是 r-value-reference.
setI_2的调用方式如下:
{
shared_ptr<int> ptr = std::make_shared<int>(42);
/// ptr owns 42
A a;
{ // a.setI_2(std::move(ptr));
// initializing the parameter
shared_ptr<int> i{std::move(ptr)};
// now i owns 42 and ptr no longer owns it and points to null
{ // the body of the function
a.m_i = std::move(i);
// now a.m_i owns 42 and i no longer owns it and points to null
};
};
};