2016-02-01 9 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_ptrはコピーやstd::mem_fnで10回shared_ptrよりも遅くなると、生の使用よりも60倍遅くptrまたはshared_ptr.get()

+7

最適化されたビルドをテストしましたか? – TartanLlama

+0

はい、g ++ -O3 -std = C++ 11を使用してテストを構築しています – user2807083

+2

'weak_ptr'はスレッドの安全性を' shared_ptr'のバインドを遅くする必要があります。共有オブジェクトが破棄されたかどうかわからない場合にのみ、 'weak_ptr'を使うべきです。それ以外の場合は、* rawポインタ*を使用します。 – Galik

答えて

5

テストを再現しようとすると、オプティマイザがそれ以上のものを排除している可能性があることが分かりました。私がやったことは、過剰最適化を無効にするために乱数を利用することでした。これらの結果はstd::weak_ptrstd::shared_ptrまたは生ポインタより約3倍遅く現実的です。

#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質問を読んでいるのは、どうしてなぜ遅いのかを説明している質問にリンクしているからです。しかしOPは**彼の**テストがより遅い*パフォーマンスを生み出していることに驚いています。そして彼は理由を知りたい。タイトルは「なぜweak_ptr経由で呼び出すのが**遅いのですか?」です。 (**に重点を置いて**) – Galik

+0

ああ、私はそれがいずれかの方法を明確に述べていないことがわかります。 –

関連する問題