2016-02-01 56 views
6

我已閱讀What's the performance penalty of weak_ptr?這個問題,但我自己的測試顯示不同的結果。爲什麼通過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_ptrshared_ptr與複製和std::mem_fn和60比使用原始慢10倍速度較慢ptr或shared_ptr.get()

+7

您是否測試過優化版本? – TartanLlama

+0

是的,我正在使用g ++ -O3 -std = C++ 11來構建我的測試 – user2807083

+2

'weak_ptr'需要線程安全地獲取'shared_ptr',它的綁定速度很慢。當你不知道共享對象是否被銷燬時,你應該只使用'weak_ptr'。否則使用*原始指針*。 – Galik

回答

5

爲了重現你的測試,我意識到優化器可能會消除更多的應用。我所做的是利用隨機數來打敗過度優化,這些結果看起來很現實,std::weak_ptrstd::shared_ptr原始指針慢三倍。

我在每次測試計算校驗,以確保他們都做同樣的工作:

#include <chrono> 
#include <memory> 
#include <vector> 
#include <iomanip> 
#include <iostream> 

#define OUT(m) do{std::cout << m << '\n';}while(0) 

class Timer 
{ 
    using clk = std::chrono::steady_clock; 
    using microseconds = std::chrono::microseconds; 

    clk::time_point tsb; 
    clk::time_point tse; 

public: 

    void clear() { tsb = tse = clk::now(); } 
    void start() { tsb = clk::now(); } 
    void stop() { tse = clk::now(); } 

    friend std::ostream& operator<<(std::ostream& o, const Timer& timer) 
    { 
     return o << timer.secs(); 
    } 

    // return time difference in seconds 
    double secs() const 
    { 
     if(tse <= tsb) 
      return 0.0; 
     auto d = std::chrono::duration_cast<microseconds>(tse - tsb); 
     return d.count()/1000000.0; 
    } 
}; 

Timer timer; 

constexpr auto N = 100000000U; 

int main() 
{ 
    std::srand(std::time(0)); 

    std::vector<int> random_ints; 
    for(auto i = 0U; i < 1024; ++i) 
     random_ints.push_back(std::rand() % (i + 1)); 

    std::shared_ptr<int> sptr = std::make_shared<int>(std::rand() % 100); 
    int* rptr = sptr.get(); 
    std::weak_ptr<int> wptr = sptr; 

    unsigned sum = 0; 

    sum = 0; 
    timer.start(); 
    for(auto i = 0U; i < N; ++i) 
    { 
     sum += random_ints[i % random_ints.size()] * *sptr; 
    } 
    timer.stop(); 

    OUT("sptr: " << sum << " " << timer); 

    sum = 0; 
    timer.start(); 
    for(auto i = 0U; i < N; ++i) 
    { 
     sum += random_ints[i % random_ints.size()] * *rptr; 
    } 
    timer.stop(); 

    OUT("rptr: " << sum << " " << timer); 

    sum = 0; 
    timer.start(); 
    for(auto i = 0U; i < N; ++i) 
    { 
     sum += random_ints[i % random_ints.size()] * *wptr.lock(); 
    } 
    timer.stop(); 

    OUT("wptr: " << sum << " " << timer); 
} 

編譯器標誌:

g++ -std=c++14 -O3 -g0 -D NDEBUG -o bin/timecpp src/timecpp.cpp 

輸出示例:

sptr: 3318793206 1.30389 // shared pointer 
rptr: 3318793206 1.2751 // raw pointer 
wptr: 3318793206 3.13879 // weak pointer 
+0

這不回答這個問題。正如我所讀到的,這個問題是「是什麼讓weak_ptr變慢?」不是「爲什麼[某些代碼]不顯示weak_ptr太慢?」 –

+1

@MatthewJamesBriggs我讀到這個問題的方式是「爲什麼它在我的特定測試中很慢」,因爲他鏈接到一個問題,這個問題已經解釋了爲什麼它很慢。但是令人驚訝的是,他的**測試正在產生*甚至更慢的性能。他想知道爲什麼。標題是「爲什麼通過weak_ptr調用是如此緩慢?」 (強調**所以**) – Galik

+0

哦,我明白了,這兩種方式都沒有明確說明。 –