Как сказал Алан Стоукс в своем комментарии, это связано с эффектами кэша.Общие указатели включают счетчик ссылок, что означает, что они физически больше в памяти, чем необработанный указатель.При хранении в непрерывном массиве вы получаете меньше указателей на строку кэша, что означает, что цикл должен выходить в основную память чаще, чем для необработанного указателя.
Вы можете наблюдать это поведение вваш необработанный тест указателя, выделяющий SIZE*2
дюйма, но также изменяющий цикл де-ссылки на шаг 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 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;
}
Кстати, использование 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 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;
}
Мои результаты (x86-64 Unbuntu 11 VM):
Original Raw: 6.93
New Raw: 12.9
Original Shared: 12.7
New Shared: 10.59