#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. Ах, большое спасибо! Я адаптирую свой тест, чтобы сделать сравнение более справедливым.