2016-02-01 16 views
6

Tôi đã đọc câu hỏi What's the performance penalty of weak_ptr? nhưng các bài kiểm tra của riêng tôi cho thấy các kết quả khác nhau.Tại sao gọi qua weak_ptr quá chậm?

Tôi đang làm đại biểu với con trỏ thông minh. Mã đơn giản dưới đây cho thấy tái tạo các vấn đề hiệu suất với weak_ptr. Ai có thể cho tôi biết tại sao?

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

Và kiểm tra:

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

Kết quả:

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

Như bạn thấy, weak_ptr là chậm hơn 10 lần so với shared_ptr với việc sao chép và std::mem_fn và 60 lần chậm hơn so với sử dụng nguyên ptr hoặc shared_ptr.get()

+7

Bạn đã thử nghiệm một bản dựng được tối ưu hóa chưa? – TartanLlama

+0

Có, tôi đang sử dụng g ++ -O3 -std = C++ 11 để xây dựng thử nghiệm của mình – user2807083

+2

Một 'weak_ptr' cần phải thực hiện chuỗi an toàn của một' shared_ptr' ràng buộc của nó là chậm. Bạn chỉ nên sử dụng 'weak_ptr' khi bạn không biết liệu đối tượng được chia sẻ có bị hủy hay không. Nếu không, hãy sử dụng * con trỏ thô *. – Galik

Trả lời

5

Trong cố gắng để tái sản xuất thử nghiệm của bạn, tôi nhận ra rằng trình tối ưu hóa có thể loại bỏ nhiều hơn mức cần thiết. Điều tôi đã làm là sử dụng các số ngẫu nhiên để đánh bại tối ưu hóa quá mức và kết quả này có vẻ thực tế với std::weak_ptr chậm hơn khoảng ba lần so với std::shared_ptr hoặc con trỏ thô.

tôi tính toán một checksum trong mỗi bài kiểm tra để đảm bảo tất cả chúng đều làm cùng một công việc:

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

Compiler cờ:

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

Kết quả ví dụ:

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

Điều này không trả lời được câu hỏi. Câu hỏi, như tôi đọc nó, là "điều gì làm cho weak_ptr chậm?" Không phải "tại sao không [một số mã] cho thấy weak_ptr chậm?" –

+1

@MatthewJamesBriggs Cách tôi đọc câu hỏi là "Tại sao nó chậm trong các bài kiểm tra cụ thể của tôi", bởi vì anh ta liên kết với một câu hỏi đã giải thích tại sao nó chậm. Nhưng OP ngạc nhiên khi ** các bài kiểm tra ** của anh ấy cho hiệu suất * thậm chí chậm hơn *. Và anh ta muốn biết tại sao. Tiêu đề là "Tại sao gọi qua weak_ptr là ** vậy ** chậm?" (nhấn mạnh trên ** so **) – Galik

+0

Ồ, tôi hiểu, nó không được nói rõ một trong hai cách. –

Các vấn đề liên quan