2016-02-01 17 views
6

Przeczytałem pytanie What's the performance penalty of weak_ptr?, ale moje własne testy pokazują różne wyniki.Dlaczego dzwonienie przez weak_ptr jest tak wolne?

Robię delegatów z inteligentnymi wskazówkami. Poniższy prosty kod odtwarza problemy z wydajnością z weak_ptr. Czy ktoś może mi powiedzieć, dlaczego?

#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; 
} 

a test:

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; 
} 

Wyniki:

$ ./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 

Jak widać, weak_ptr jest 10 razy wolniej niż shared_ptr z kopiowaniem i std::mem_fn i 60 razy wolniej niż przy użyciu surowego ptr lub shared_ptr.get()

+7

Czy testowałeś zoptymalizowaną kompilację? – TartanLlama

+0

Tak, używam g ++ -O3 -std = C++ 11 do budowania mojego testu. – user2807083

+2

'weak_ptr' musi wykonać bezpieczne pobieranie wątku' shared_ptr', które jest spowolnione. Powinieneś używać 'weak_ptr', kiedy nie możesz się dowiedzieć, czy obiekt współdzielony został zniszczony czy nie. W przeciwnym razie użyj * surowego wskaźnika *. – Galik

Odpowiedz

5

Próbując odtworzyć test zdałem sobie sprawę, że optymalizator może eliminować więcej, niż powinien. To, co zrobiłem, polegało na wykorzystaniu liczb losowych w celu pokonania nadmiernej optymalizacji, a wyniki te wydają się realistyczne, ponieważ std::weak_ptr jest około trzy razy wolniejszy od wskaźnika std::shared_ptr lub jego surowego wskaźnika.

obliczyć sumę kontrolną w każdym teście, aby zapewnić wszystkim robią tę samą pracę:

#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); 
} 

kompilatora flagi:

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

Przykâadowa:

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

To nie odpowiada na pytanie. Pytanie, które czytałem, brzmi: "co spowalnia slow_ptr?" Nie "dlaczego ten kod nie pokazuje, że weak_ptr jest wolne?" –

+1

@MatthewJamesBriggs Sposób, w jaki czytam pytanie, brzmi: "Dlaczego jest wolny w moich konkretnych testach", ponieważ łączy się z pytaniem, które już wyjaśnia, dlaczego jest wolne. Ale OP jest zaskoczony, że ** jego ** testy dają * jeszcze wolniejszą * wydajność. I chce wiedzieć dlaczego. Tytuł brzmi: "Dlaczego dzwonienie przez weak_ptr jest ** więc ** wolne?" (nacisk na ** tak **) – Galik

+0

Och, widzę, nie jest to jasno określone w żaden sposób. –

Powiązane problemy