【发布时间】:2016-02-01 15:49:42
【问题描述】:
我已阅读问题What's the performance penalty of weak_ptr?,但我自己的测试显示不同的结果。
我正在使用智能指针制作代表。下面显示的简单代码重现了weak_ptr 的性能问题。谁能告诉我为什么?
#include <chrono>
#include <functional>
#include <iostream>
#include <memory>
#include <stdint.h>
#include <string>
#include <utility>
struct Foo
{
Foo() : counter(0) { incrStep = 1;}
void bar()
{
counter += incrStep;
}
virtual ~Foo()
{
std::cout << "End " << counter << std::endl;
}
private:
uint64_t counter;
uint64_t incrStep;
};
void pf(const std::string &md, const std::function<void()> &g)
{
const auto st = std::chrono::high_resolution_clock::now();
g();
const auto ft = std::chrono::high_resolution_clock::now();
const auto del = std::chrono::duration_cast<std::chrono::milliseconds>(ft - st);
std::cout << md << " \t: \t" << del.count() << std::endl;
}
还有测试:
int main(int , char** )
{
volatile size_t l = 1000000000ULL;
size_t maxCounter = l;
auto a = std::make_shared<Foo>();
std::weak_ptr<Foo> wp = a;
pf("call via raw ptr ", [=](){
for (size_t i = 0; i < maxCounter; ++i)
{
auto p = a.get();
if (p)
{
p->bar();
}
}
});
pf("call via shared_ptr ", [=](){
for (size_t i = 0; i < maxCounter; ++i)
{
if (a)
{
a->bar();
}
}
});
pf("call via weak_ptr ", [=](){
std::shared_ptr<Foo> p;
for (size_t i = 0; i < maxCounter; ++i)
{
p = wp.lock();
if (p)
{
p->bar();
}
}
});
pf("call via shared_ptr copy", [=](){
volatile std::shared_ptr<Foo> p1 = a;
std::shared_ptr<Foo> p;
for (size_t i = 0; i < maxCounter; ++i)
{
p = const_cast<std::shared_ptr<Foo>& >(p1);
if (p)
{
p->bar();
}
}
});
pf("call via mem_fn ", [=](){
auto fff = std::mem_fn(&Foo::bar);
for (size_t i = 0; i < maxCounter; ++i)
{
fff(a.get());
}
});
return 0;
}
结果:
$ ./test
call via raw ptr : 369
call via shared_ptr : 302
call via weak_ptr : 22663
call via shared_ptr copy : 2171
call via mem_fn : 2124
End 5000000000
如您所见,weak_ptr 复制时比 shared_ptr 慢 10 倍,std::mem_fn 比使用原始 ptr 或 shared_ptr.get() 慢 60 倍
【问题讨论】:
-
您是否测试了优化的构建?
-
A
weak_ptr需要对shared_ptr进行线程安全的获取,这肯定会很慢。当您不知道共享对象是否已被销毁时,您应该只使用weak_ptr。否则使用 原始指针. -
有点 OT:当我用 gcc v5.3.0 尝试这个时,
mem_fn部分完全没有时间,这表明它已经将千万次调用优化为一个简单的一次性增量柜台。所以我将计数器更改为volatile,然后 raw_ptr 和 shared_ptr 案例花费的时间与 shared_ptr 复制和 mem_fn 相同。我将看看您的编译器如何优化 raw_ptr 和 shared_ptr 案例。 (使用 v4.9,我得到的结果与您的相似。) -
另外,用clang-3.6(和libc++)编译,结果是0、0、23178、20972、0。同样,把0变成合理的数字(2280、2406、23071、20110、 2415)。但有趣的是,锁定一个weak_ptr 和复制一个shared_ptr 之间的区别消失了。
-
我认为您看到了优化的怪癖。 weak_ptr 情况是唯一在编译时无法推断函数调用次数的情况。
标签: c++ performance c++11 g++