我正在为游戏设计一个对象结构,在我的案例中,最自然的组织变成了一棵树。作为智能指针的忠实粉丝,我只使用shared_ptr
。但是,在这种情况下,树中的孩子将需要访问它的父母(例如 - 地图上的生物需要能够访问他们的父母的数据。
拥有的方向当然是地图拥有它的存在,因此拥有它们的共享指针。为了从存在中访问地图数据,我们需要一个指向父节点的指针 - 智能指针的方式是使用引用,而不是weak_ptr
。
然而,我曾经读到锁定weak_ptr
是一项昂贵的操作 - 可能这不再是真的 - 但考虑到weak_ptr
会经常被锁定,我担心这个设计注定表现不佳。
因此问题:
锁定weak_ptr会有什么性能损失?它有多重要?
答案 0 :(得分:13)
来自Boost 1.42源代码(<boost/shared_ptr/weak_ptr.hpp>
第155行):
shared_ptr<T> lock() const // never throws
{
return shared_ptr<element_type>( *this, boost::detail::sp_nothrow_tag() );
}
ergo,James McNellis的评论是正确的;这是复制构建shared_ptr
的成本。
答案 1 :(得分:7)
对于我自己的项目,我能够通过添加来显着提高性能
在任何提升包括之前#define BOOST_DISABLE_THREADS
。
这避免了我的项目中的weak_ptr :: lock的spinlock / mutex开销
一个主要的瓶颈。由于该项目不是多线程的,所以我可以做到这一点。
答案 2 :(得分:2)
使用/取消引用shared_ptr几乎就像访问原始ptr一样,与常规指针访问相比,锁定weak_ptr是一项“繁重的”操作,因为此代码必须具有“线程意识”可以正常工作,以防其他线程触发释放该指针所引用的对象。至少,它必须执行某种互锁/原子操作,根据定义,它比常规的内存访问要慢得多。
和往常一样,查看发生情况的一种方法是inspect generated code:
#include <memory>
class Test
{
public:
void test();
};
void callFuncShared(std::shared_ptr<Test>& ptr)
{
if (ptr)
ptr->test();
}
void callFuncWeak(std::weak_ptr<Test>& ptr)
{
if (auto p = ptr.lock())
p->test();
}
void callFuncRaw(Test* ptr)
{
if (ptr)
ptr->test();
}
通过shared_ptr和原始指针进行访问是相同的。由于shared_ptr
是作为参考传递的,因此我们需要加载参考值,这就是为什么差异仅是shared_ptr版本的一次额外加载。
callFuncShared:
callFuncWeak:
通过weak_ptr
进行调用会产生10倍多的代码,充其量必须经过锁定的compare-exchange,与解引用raw或shared_ptr相比,它本身将花费10倍以上的CPU时间:
仅当共享计数器不为零时,它才能将指针加载到实际对象并使用它(通过调用对象或创建shared_ptr
)。