2011-11-02 35 views
13

Tôi đang cố gắng so sánh hiệu suất giữa các con trỏ thô, tăng shared_ptr và tăng weak_ptr. Trên phần dereferencing, tôi dự kiến ​​shared_ptr và raw_ptr là bằng nhau, nhưng kết quả cho thấy shared_ptr là khoảng hai lần như chậm. Đối với các bài kiểm tra, tôi đang tạo ra một mảng với một trong hai con trỏ hoặc gợi ý chia sẻ để ints, và sau đó dereferencing trong một vòng lặp như thế này:Boost shared_ptr dereference cost

int result; 
for(int i = 0; i != 100; ++i) 
{ 
    for(int i = 0; i != SIZE; ++i) 
    result += *array[i]; 
} 

Mã đầy đủ cho các thử nghiệm có thể được tìm thấy ở đây: https://github.com/coolfluid/coolfluid3/blob/master/test/common/utest-ptr-benchmark.cpp

timings thử nghiệm cho một build tối ưu mà không khẳng định có thể được tìm thấy ở đây: http://coolfluidsrv.vki.ac.be/cdash/testDetails.php?test=145592&build=7777

các giá trị quan tâm là "thời gian DerefShared" và "thời gian DerefRaw"

Tôi đoán thử nghiệm có thể bị thiếu sót bằng cách nào đó, nhưng tôi đã thất bại trong việc tìm ra sự khác biệt đến từ đâu. Profiling hiển thị toán tử * từ shared_ptr được inlined, nó dường như mất nhiều thời gian hơn. Tôi kiểm tra lại rằng khẳng định tăng đã tắt.

Tôi sẽ rất biết ơn nếu có ai có thể giải thích sự khác biệt có thể đến từ đâu.

bổ sung độc lập kiểm tra: https://gist.github.com/1335014

+0

+1, tôi đã suy nghĩ về việc này một ngày khác nữa. –

+7

Hiệu ứng bộ nhớ cache? shared_ptr lớn hơn con trỏ thô, vì vậy mảng của bạn sẽ bao gồm nhiều dòng bộ nhớ cache hơn và mất nhiều thời gian hơn để đọc. –

+0

Bạn đang chạy các thử nghiệm này trên nền tảng nào? – SoapBox

Trả lời

10

Như Alan Stokes cho biết trong comment của mình, điều này là do hiệu ứng bộ nhớ cache. Các con trỏ chia sẻ bao gồm số tham chiếu, có nghĩa là chúng có kích thước vật lý lớn hơn trong bộ nhớ so với con trỏ thô. Khi được lưu trữ trong một mảng liền kề, bạn sẽ nhận được ít con trỏ trên mỗi dòng bộ nhớ cache, điều đó có nghĩa là vòng lặp phải chuyển sang bộ nhớ chính thường xuyên hơn so với con trỏ thô.

Bạn có thể quan sát hành vi này bằng, trong thử nghiệm con trỏ thô, phân bổ SIZE*2 ints, nhưng cũng thay đổi vòng tham chiếu để di chuyển theo i+=2 thay vì ++i. Làm điều này mang lại kết quả tương tự trong các thử nghiệm của tôi. Mã của tôi cho thử nghiệm thô dưới đây.

#include <iostream> 
#include <boost/timer.hpp> 

#define SIZE 1000000 

typedef int* PtrT; 

int do_deref(PtrT* array) 
{ 
    int result = 0; 
    for(int i = 0; i != 1000; ++i) 
    { 
     for(int i = 0; i != SIZE*2; i+=2) 
      result += *array[i]; 
    } 

    return result; 
} 

int main(void) 
{ 
    PtrT* array = new PtrT[SIZE*2]; 
    for(int i = 0; i != SIZE*2; ++i) 
     array[i] = new int(i); 
    boost::timer timer; 
    int result = do_deref(array); 
    std::cout << "deref took " << timer.elapsed() << "s" << std::endl; 
    return result; 
} 

Ngẫu nhiên, sử dụng boost::make_shared<int>(i) thay vì PtrT(new int(I)) phân bổ số lượng tài liệu tham khảo và các đối tượng với nhau trong bộ nhớ hơn là tại các địa điểm riêng biệt. Trong các bài kiểm tra của tôi, điều này cải thiện hiệu suất của dereference chia sẻ con trỏ khoảng 10-20%. Mã cho rằng là dưới đây:

#include <iostream> 
#include <boost/timer.hpp> 
#include <boost/shared_ptr.hpp> 
#include <boost/make_shared.hpp> 
#define SIZE 1000000 

typedef boost::shared_ptr<int> PtrT; 

int do_deref(PtrT* array) 
{ 
    int result = 0; 
    for(int j = 0; j != 1000; ++j) 
    { 
     for(int i = 0; i != SIZE; ++i) 
      result += *array[i]; 
    } 

    return result; 
} 

int main(void) 
{ 
    PtrT* array = new PtrT[SIZE]; 
    for(int i = 0; i != SIZE; ++i) 
     array[i] = boost::make_shared<int>(i); 
    boost::timer timer; 
    int result = do_deref(array); 
    std::cout << "deref took " << timer.elapsed() << "s" << std::endl; 
    return result; 
} 

Kết quả của tôi (x86-64 Unbuntu 11 VM):

Original Raw: 6.93 
New Raw: 12.9 
Original Shared: 12.7 
New Shared: 10.59 
+4

"_Shared con trỏ bao gồm một số tham chiếu, _" thực sự là một con trỏ đến một cấu trúc dữ liệu với 2 số ref, một con trỏ, một deleter, và thậm chí có thể là một mutex (hy vọng là không). – curiousguy

+3

@curious Kích thước là 2 con trỏ - một con trỏ đến trọng tải thực, một đến phần trên. Nhưng không ai trong số đó ảnh hưởng đến chi phí của một sự răn đe đơn giản. –

+0

Ah, cảm ơn rất nhiều! Tôi sẽ điều chỉnh thử nghiệm của mình để làm cho việc so sánh công bằng hơn. –

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