Увеличить стоимость разыменования shared_ptr

#c #boost #shared-ptr

#c #повысить #общий-ptr

Вопрос:

Я пытаюсь сравнить производительность между необработанными указателями, повысить shared_ptr и повысить weak_ptr. В части разыменования я ожидал, что shared_ptr и raw_ptr будут равны, но результаты показывают, что shared_ptr примерно в два раза медленнее. Для теста я создаю массив либо с указателями, либо с общими указателями на целые числа, а затем разыменовываю в цикле, подобном этому:

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

Полный код для теста можно найти здесь:
https://github.com/coolfluid/coolfluid3/blob/master/test/common/utest-ptr-benchmark.cpp

Тайминги тестирования для оптимизированной сборки без утверждений можно найти здесь: http://coolfluidsrv.vki.ac.be/cdash/testDetails.php?test=145592amp;build=7777

Интересующие значения — это «Разыменованное время» и «Время разыменования»

Я предполагаю, что тест может быть каким-то образом ошибочным, но мне не удалось выяснить, откуда берется разница. Профилирование показывает, что оператор * из shared_ptr встроен, просто кажется, что это занимает больше времени. Я дважды проверил, что утверждение boost отключено.

Я был бы очень признателен, если бы кто-нибудь мог объяснить, откуда может возникнуть разница.

Дополнительный автономный тест: https://gist.github.com/1335014

Комментарии:

1. 1, я тоже думал об этом на днях.

2. Эффекты кэша? shared_ptr больше, чем необработанный указатель, поэтому ваш массив будет охватывать больше строк кэша и займет больше времени для чтения.

3. На какой платформе вы выполняете эти тесты?

4. Почему массив указателей? Разве вы не должны разыменовывать один и тот же указатель много раз?

5. @BartJanssens: Я пас. Регулярная критика проще, чем конструктивная критика. 😉

Ответ №1:

Как сказал Алан Стоукс в своем комментарии, это связано с эффектами кэша. Общие указатели включают счетчик ссылок, что означает, что они физически больше в памяти, чем необработанный указатель. При хранении в непрерывном массиве вы получаете меньше указателей на строку кэша, что означает, что цикл должен выходить в основную память чаще, чем для необработанного указателя.

Вы можете наблюдать это поведение, выделяя целые числа в своем необработанном тесте указателя SIZE*2 , но также изменяя цикл удаления ссылок на stride by i =2 вместо i . Выполнение этого дало примерно те же результаты в моих тестах. Мой код для необработанного теста приведен ниже.

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

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

Кстати, использование boost::make_shared<int>(i) вместо PtrT(new int(I)) того, чтобы распределять счетчик ссылок и объект вместе в памяти, а не в отдельных местах. В моих тестах это улучшило производительность разыменования общего указателя примерно на 10-20%. Код для этого приведен ниже:

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

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

Мои результаты (x86-64 Unbuntu 11 VM):

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

Комментарии:

1. » Общие указатели включают счетчик ссылок «, фактически указатель на структуру данных с 2 счетчиками ссылок, указатель, удаление и, возможно, даже мьютекс (надеюсь, нет).

2. @любопытно, что размер равен 2 указателям — один на реальную полезную нагрузку, один на служебные данные. Но ничто из этого не влияет на стоимость простого сдерживания.

3. Ах, большое спасибо! Я адаптирую свой тест, чтобы сделать сравнение более справедливым.