2011-11-02 14 views
13

Próbuję porównać wydajność między surowymi wskaźnikami, boost shared_ptr i boost weak_ptr. Na części do dereferencji oczekiwałem, że shared_ptr i raw_ptr są równe, ale wyniki pokazują, że shared_ptr jest około dwa razy wolniejsza. Do testu tworzę tablicę albo wskaźników lub współdzielonych wskazówki do wskazówki, a następnie dereferencing w pętli jak ta:Podnieś shared_ptr koszt dereferencji

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

Kod pełny dla testu można znaleźć tutaj: https://github.com/coolfluid/coolfluid3/blob/master/test/common/utest-ptr-benchmark.cpp

czasy testowe dla zoptymalizowanej budowie bez twierdzeń można znaleźć tutaj: http://coolfluidsrv.vki.ac.be/cdash/testDetails.php?test=145592&build=7777

wartości to są „DerefShared czas” i „DerefRaw czas”

Zgaduję, że test może być wadliwy, ale nie udało mi się ustalić, skąd się bierze różnica. Profilowanie pokazuje, że operator * z shared_ptr zostaje wstawiony, wydaje się, że zajmuje to więcej czasu. Sprawdziłem dwukrotnie, czy asercja wzmocnienia jest wyłączona.

Byłbym bardzo wdzięczny, gdyby ktoś mógł wyjaśnić, skąd może pochodzić różnica.

Dodatkowa samodzielna Test: https://gist.github.com/1335014

+0

+1, myślałem o tym na drugi dzień też. –

+7

Efekty pamięci podręcznej? shared_ptr jest większy niż surowy wskaźnik, więc twoja tablica pokryje więcej linii pamięci podręcznej i będzie potrzebować więcej czasu na czytanie. –

+0

Na jakiej platformie są uruchomione te testy? – SoapBox

Odpowiedz

10

Jak Alan Stokes powiedział w swoim komentarzu, to ze względu na efekty skrytek. Współdzielone wskaźniki obejmują licznik odwołań, co oznacza, że ​​są fizycznie większe w pamięci niż surowy wskaźnik. Gdy jest przechowywany w sąsiadującej tablicy, dostajesz mniej wskaźników na linię pamięci podręcznej, co oznacza, że ​​pętla musi wychodzić do pamięci głównej częściej niż w przypadku wskaźnika surowego.

Możesz zaobserwować to zachowanie przez, w teście wskaźnika surowego, przydzielanie SIZE*2 ints, ale także zmianę pętli de-reference na krok i+=2 zamiast ++i. Wykonanie tego przyniosło w przybliżeniu takie same wyniki w moich testach. Mój kod dla surowego testu znajduje się poniżej.

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

Nawiasem mówiąc, stosując boost::make_shared<int>(i) zamiast PtrT(new int(I)) przydziela licznika odniesienia i obiektu razem w pamięci, a nie w różnych miejscach. W moich testach poprawiło to wydajność dzielonego wskaźnika dereferencji o około 10-20%. Kodeks, który znajduje się poniżej:

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

moje wyniki (x86-64 Unbuntu 11 VM):

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

"_Shared Pointers zawierają licznik referencji, _" właściwie wskaźnik do struktury danych z 2 licznikami referencji, wskaźnikiem, deleterem, a może nawet muteksem (mam nadzieję, że nie). – curiousguy

+3

@curious Rozmiar to 2 wskaźniki - jeden do rzeczywistej ładowności, a jeden do narzutu. Ale nic z tego nie wpływa na koszt zwykłego odstraszania. –

+0

Ah, wielkie dzięki! Zmienię swój test, aby porównanie było bardziej sprawiedliwe. –