Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Performance

Boost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 17. Boost.Intrusive

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

НавязчивыйКонтейнеры обеспечивают улучшение скорости по сравнению с неинтрузивными контейнерами, главным образом потому, что:

  • Они минимизируют выделение/выделение памяти.
  • Они получают лучшую локальность памяти.

В этом разделе будут показаны тесты производительности, сравнивающие некоторые операции поповышению:навязчивый:списокиstd:Список:

  • Вставка с использованиемpush_backи разрушение контейнера покажет накладные расходы, связанные с выделением/выделением памяти.
  • Функция реверсапоказывает преимущества компактного представления памяти, которые могут быть достигнуты с помощью интрузивных контейнеров.
  • Тестысортировкиизаписидоступапокажут преимущество интрузивных контейнеров, минимизирующих доступ к памяти по сравнению с контейнерами указателей.

Учитывая объект типаT,boost::intrusive::listможет заменитьstd::список<T>, чтобы избежать распределения памяти накладных расходов, или он может заменитьstd::список<T*>, когда пользователь хочет контейнеры с полиморфными значениями или хочет разделить значения между несколькими контейнерами. Благодаря этой универсальности тесты производительности будут выполняться для 6 различных типов списков:

  • 3 назойливые списки, содержащие класс, названныйitest_class, каждый с различной политикой связыванияnormal_link,safe_link,auto_unlink.itest_classобъекты будут плотно упакованы вstd::вектор<itest_class>объект.
  • std::list<test_class>, гдеtest_classточно такой же, какitest_class, но без навязчивого крючка.
  • std::list<test_class*>. Список будет содержать указатели наtest_classобъекты, плотно упакованные вstd::вектор<test_class>объект. Назовем эту конфигурациюкомпактным указателем
  • std::list<test_class*>. Список будет содержать указатели наtest_classобъекты, принадлежащиеstd::list<test_class>объект. Назовем эту конфигурациюдисперсным указателем.

Какtest_class, так иitest_classявляются шаблонизированными классами, которые могут быть сконфигурированы с булевым для увеличения размера объекта. Таким образом, тесты могут быть выполнены с маленькими и большими объектами. Вот первая часть тестового кода, которая показывает определенияtest_classиitest_classклассов и некоторых других утилит:

//Iteration and element count defines
const int NumIter = 100;
const int NumElements   = 50000;
using namespace boost::intrusive;
template<bool BigSize>  struct filler        {  int dummy[10];   };
template <>             struct filler<false> {};
template<bool BigSize> //The object for non-intrusive containers
struct test_class :  private filler<BigSize>
{
   int i_;
   test_class()               {}
   test_class(int i) :  i_(i) {}
   friend bool operator <(const test_class &l, const test_class &r)  {  return l.i_ < r.i_;  }
   friend bool operator >(const test_class &l, const test_class &r)  {  return l.i_ > r.i_;  }
};
template <bool BigSize, link_mode_type LinkMode>
struct itest_class   //The object for intrusive containers
   :  public list_base_hook<link_mode<LinkMode> >,  public test_class<BigSize>
{
   itest_class()                                {}
   itest_class(int i) : test_class<BigSize>(i)  {}
};
template<class FuncObj> //Adapts functors taking values to functors taking pointers
struct func_ptr_adaptor  :  public FuncObj
{
   typedef typename FuncObj::first_argument_type*  first_argument_type;
   typedef typename FuncObj::second_argument_type* second_argument_type;
   typedef typename FuncObj::result_type           result_type;
   result_type operator()(first_argument_type a,  second_argument_type b) const
      {  return FuncObj::operator()(*a, *b); }
};

Как мы видим,test_class- очень простой класс, содержащийint.itest_class- это просто класс, который имеет базовый крюкlist_base_hook, а также происходит отtest_class.

func_ptr_adaptor— это просто адаптер функтора для преобразования объектов функций, принимающихтестовые_спискиобъекты для функционирования объектов, принимающих к ним указатели.

Полный тестовый код можно найти в исходном файлеperf_list.cpp.

Первый тест будет измерять преимущества, которые мы можем получить с помощью интрузивных контейнеров, избегая выделения памяти и распределения транзакций. Все объекты, которые должны быть вставлены в интрузивные контейнеры, выделены в одном вызове распределения, тогда какstd::списокдолжен будет выделить память для каждого объекта и распределить ее для каждого стирания (или разрушения контейнера).

Давайте сравним код, который будет выполняться для каждого типа контейнера для различных тестов ввода:

std::vector<typename ilist::value_type> objects(NumElements);
ilist l;
for(int i = 0; i < NumElements; ++i)
   l.push_back(objects[i]);
//Elements are unlinked in ilist's destructor
//Elements are destroyed in vector's destructor

Для интрузивных контейнеров все значения создаются в векторе и после этого вставляются в интрузивный список.

stdlist l;
for(int i = 0; i < NumElements; ++i)
   l.push_back(typename stdlist::value_type(i));
//Elements unlinked and destroyed in stdlist's destructor

Для стандартного списка элементы отталкиваются с помощью push_back().

std::vector<typename stdlist::value_type> objects(NumElements);
stdptrlist l;
for(int i = 0; i < NumElements; ++i)
   l.push_back(&objects[i]);
//Pointers to elements unlinked and destroyed in stdptrlist's destructor
//Elements destroyed in vector's destructor

Для стандартного компактного списка указателей элементы создаются в векторе и отталкиваются в списке указателей с помощью push_back().

stdlist objects;  stdptrlist l;
for(int i = 0; i < NumElements; ++i){
   objects.push_back(typename stdlist::value_type(i));
   l.push_back(&objects.back());
}
//Pointers to elements unlinked and destroyed in stdptrlist's destructor
//Elements unlinked and destroyed in stdlist's destructor

Длядисперсного списка указателейэлементы создаются в списке и выталкиваются обратно в списке указателей с помощью push_back().

Это время в микросекундах для каждого случая и нормированное время:

Table 17.2. Back insertion + destruction times for Visual C++ 7.1 / Windows XP

Контейнер

Время в нас / итерация (маленький объект / большой объект)

Нормализованное время (маленький объект / большой объект)

normal_linkintrusive list

5000 / 22500

1/1

safe_linkназойливый список

7812 / 32187

1.56/1.43

auto_unlinkназойливый список

10156 / 41562

2.03/1.84

Стандартный список

26875 / 97500

5.37 / 4.33

Стандартный список компактных указателей

76406 / 86718

15.28 / 3.85

Стандартный дисперсный указатель

146562 / 175625

29.31 / 7.80


Table 17.3. Back insertion + destruction times for GCC 4.1.1 / MinGW over Windows XP

Контейнер

Время в нас / итерация (маленький объект / большой объект)

Нормализованное время (маленький объект / большой объект)

normal_linkintrusive list

4375/22187

1/1

safe_linkназойливый список

7812 / 32812

1,78 / 1,47

auto_unlinkназойливый список

10468 / 42031

2.39/1.89

Стандартный список

81250 / 98125

18.57 / 4.42

Стандартный список компактных указателей

83750 / 94218

19.14 / 4.24

Стандартный дисперсный указатель

155625 / 175625

35.57 / 7.91


Table 17.4. Back insertion + destruction times for GCC 4.1.2 / Linux Kernel 2.6.18 (OpenSuse 10.2)

Контейнер

Время в нас / итерация (маленький объект / большой объект)

Нормализованное время (маленький объект / большой объект)

normal_linkintrusive list

4792 / 20495

1/1

safe_linkназойливый список

7709 / 30803

1,60 / 1,5

auto_unlinkназойливый список

10180 / 41183

2.12 / 2.0

Стандартный список

17031 / 32586

3.55 / 1.58

Стандартный список компактных указателей

27221 / 34823

5.68 / 1.69

Стандартный дисперсный указатель

102272 / 60056

21.34/2.93


Результаты логичны: навязчивые списки требуют лишь одного распределения. Время разрушениянормального_linkнавязчивого контейнера тривиально (сложность:O)), тогда какбезопасного_linkиавто_unlinkнавязчивые контейнеры должны помещать крючки стертых значений в состояние по умолчанию (сложность:ONumElements). Вот почемунормальный_linkнавязчивый список сияет в этом тесте.

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

Тест Linux показывает, что стандартные контейнеры очень хорошо работают против навязчивых контейнеров с большими объектами. Почти та же версия GCC в MinGW работает хуже, поэтому, возможно, хороший распределитель памяти является причиной этих отличных результатов.

В следующем испытании измеряется время, необходимое для завершения вызовов функции членаобратно[]. Значенияtest_classиitest_classи списки создаются, как объяснено в предыдущем разделе.

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

Вот результаты:

Table 17.5. Reverse times for Visual C++ 7.1 / Windows XP

Контейнер

Время в нас / итерация (маленький объект / большой объект)

Нормализованное время (маленький объект / большой объект)

normal_linkintrusive list

2656 / 10625

1/1.83

safe_linkназойливый список

2812 / 10937

1,05 / 1,89

auto_unlinkназойливый список

2710 / 10781

1,02 / 1,86

Стандартный список

5781 / 14531

2.17 / 2.51

Стандартный список компактных указателей

5781 / 5781

2.17/1

Стандартный дисперсный указатель

10781 / 15781

4.05/2.72


Table 17.6. Reverse times for GCC 4.1.1 / MinGW over Windows XP

Контейнер

Время в нас / итерация (маленький объект / большой объект)

Нормализованное время (маленький объект / большой объект)

normal_linkintrusive list

2656 / 10781

1 / 2.22

safe_linkназойливый список

2656 / 10781

1 / 2.22

auto_unlinkназойливый список

2812 / 10781

1.02 / 2.22

Стандартный список

4843 / 12500

1,82 / 2,58

Стандартный список компактных указателей

4843 / 4843

1,82 / 1

Стандартный дисперсный указатель

9218 / 12968

3.47/2.67


Table 17.7. Reverse times for GCC 4.1.2 / Linux Kernel 2.6.18 (OpenSuse 10.2)

Контейнер

Время в нас / итерация (маленький объект / большой объект)

Нормализованное время (маленький объект / большой объект)

normal_linkintrusive list

2742 / 10847

1/3.41

safe_linkназойливый список

2742 / 10847

1/3.41

auto_unlinkназойливый список

2742 / 11027

1/3.47

Стандартный список

3184 / 10942

1.16 / 3.44

Стандартный список компактных указателей

3207 / 3176

1.16/1

Стандартный дисперсный указатель

5814 / 13381

2.12 / 4.21


Для небольших объектов результаты показывают, что компактное хранение значений в интрузивных контейнерах улучшает локализацию, а реверсирование происходит быстрее, чем в стандартных контейнерах, значения которых могут быть рассеяны в памяти, поскольку каждое значение независимо распределено. Обратите внимание, что дисперсный список указателей (список указателей на значения, выделенные в другом списке) страдает больше, потому что узлы списка указателей могут быть более дисперсными, так как выделения из обоих списков чередуются в коде:

//Object list (holding `test_class`)
stdlist objects;
//Pointer list (holding `test_class` pointers)
stdptrlist l;
for(int i = 0; i < NumElements; ++i){
   //Allocation from the object list
   objects.push_back(stdlist::value_type(i));
   //Allocation from the pointer list
   l.push_back(&objects.back());
}

Для больших объектов выигрывает список компактных указателей, потому что тест на разворот не требует доступа к значениям, хранящимся в другом контейнере. Поскольку все выделения для узлов этого списка указателей, вероятно, будут близкими (поскольку до создания списка указателей другого распределения в процессе нет), локальность лучше, чем с назойливыми контейнерами. Распределенный список указателей, как и с небольшими значениями, имеет плохую локализацию.

В следующем испытании измеряется время, необходимое для завершения вызовов на функцию членаPredpred. Значенияtest_classиitest_classи списки создаются, как объясняется в первом разделе. Значения будут сортироваться по восходящему и нисходящему порядку каждой итерации. Например, еслиlявляется списком:

for(int i = 0; i < NumIter; ++i){
   if(!(i % 2))
      l.sort(std::greater<stdlist::value_type>());
   else
      l.sort(std::less<stdlist::value_type>());
}

Для списка указателей объект функции будет адаптирован с использованиемfunc_ptr_adaptor:

for(int i = 0; i < NumIter; ++i){
   if(!(i % 2))
      l.sort(func_ptr_adaptor<std::greater<stdlist::value_type> >());
   else
      l.sort(func_ptr_adaptor<std::less<stdlist::value_type> >());
}

Обратите внимание, что для списков указателейсортвозьмет функциональный объект, которыйполучит доступ кзначениям test_class, хранящимся в другом списке или векторе, поэтому списки указателей будут страдать от дополнительного опосредования: им потребуется доступ кзначения test_class, хранящиеся в другом контейнере для сравнения двух элементов.

Вот результаты:

Table 17.8. Sort times for Visual C++ 7.1 / Windows XP

Контейнер

Время в нас / итерация (маленький объект / большой объект)

Нормализованное время (маленький объект / большой объект)

normal_linkintrusive list

16093 / 38906

1/1

safe_linkназойливый список

16093 / 39062

1/1

auto_unlinkназойливый список

16093 / 38906

1/1

Стандартный список

32343 / 56406

2.0 / 1.44

Стандартный список компактных указателей

33593 / 46093

2.08 / 1.18

Стандартный дисперсный указатель

46875 / 68593

2.91 / 1.76


Table 17.9. Sort times for GCC 4.1.1 / MinGW over Windows XP

Контейнер

Время в нас / итерация (маленький объект / большой объект)

Нормализованное время (маленький объект / большой объект)

normal_linkintrusive list

15000 / 39218

1/1

safe_linkназойливый список

15156 / 39531

1.01 / 1.01

auto_unlinkназойливый список

15156 / 39531

1.01 / 1.01

Стандартный список

34218 / 56875

2.28/1.45

Стандартный список компактных указателей

35468 / 49218

2.36 / 1.25

Стандартный дисперсный указатель

47656 / 70156

3.17 / 1.78


Table 17.10. Sort times for GCC 4.1.2 / Linux Kernel 2.6.18 (OpenSuse 10.2)

Контейнер

Время в нас / итерация (маленький объект / большой объект)

Нормализованное время (маленький объект / большой объект)

normal_linkintrusive list

18003 / 40795

1/1

safe_linkназойливый список

18003 / 41017

1/1

auto_unlinkназойливый список

18230 / 40941

1.01 / 1

Стандартный список

26273 / 49643

1.45 / 1.21

Стандартный список компактных указателей

28540 / 43172

1.58/1.05

Стандартный дисперсный указатель

35077 / 57638

1,94 / 1,41


Результаты показывают, что интрузивные контейнеры быстрее, чем стандартные. Мы видим, что список указателей, содержащий указатели на значения, хранящиеся в векторе, довольно быстр, поэтому дополнительное опосредование, необходимое для доступа к значению, сводится к минимуму, потому что все значения плотно хранятся, улучшая кэширование. Дисперсный список, с другой стороны, медленнее, потому что непрямое обращение к значениям доступа, хранящимся в списке объектов, дороже, чем доступ к значениям, хранящимся в векторе.

Следующий тест измеряет время, необходимое для повторения всех элементов списка, и увеличивает значение внутреннегоi_элемента:

stdlist::iterator it(l.begin()), end(l.end());
for(; it != end; ++it)
   ++(it->i_);

Значенияtest_classиitest_classи списки создаются, как объясняется в первом разделе. Обратите внимание, что для списков указателей итерация будет иметь дополнительное опосредование: им потребуется доступ к значениямtest_class, хранящимся в другом контейнере:

stdptrlist::iterator it(l.begin()), end(l.end());
for(; it != end; ++it)
   ++((*it)->i_);

Вот результаты:

Table 17.11. Write access times for Visual C++ 7.1 / Windows XP

Контейнер

Время в нас / итерация (маленький объект / большой объект)

Нормализованное время (маленький объект / большой объект)

normal_linkintrusive list

2031 / 8125

1/1

safe_linkназойливый список

2031 / 8281

1/1.01

auto_unlinkназойливый список

2031 / 8281

1/1.01

Стандартный список

4218 / 10000

2.07 / 1.23

Стандартный список компактных указателей

4062 / 8437

2.0 / 1.03

Стандартный дисперсный указатель

8593 / 13125

4.23/1.61


Table 17.12. Write access times for GCC 4.1.1 / MinGW over Windows XP

Контейнер

Время в нас / итерация (маленький объект / большой объект)

Нормализованное время (маленький объект / большой объект)

normal_linkintrusive list

2343 / 8281

1/1

safe_linkназойливый список

2500 / 8281

1.06/1

auto_unlinkназойливый список

2500 / 8281

1.06/1

Стандартный список

4218 / 10781

1.8 / 1.3

Стандартный список компактных указателей

3906 / 8281

1,66/1

Стандартный дисперсный указатель

8281 / 13750

3.53 / 1.66


Table 17.13. Write access times for GCC 4.1.2 / Linux Kernel 2.6.18 (OpenSuse 10.2)

Контейнер

Время в нас / итерация (маленький объект / большой объект)

Нормализованное время (маленький объект / большой объект)

normal_linkintrusive list

2286 / 8468

1/1,1

safe_linkназойливый список

2381 / 8412

1.04 / 1.09

auto_unlinkназойливый список

2301 / 8437

1.01 / 1.1

Стандартный список

3044 / 9061

1.33 / 1.18

Стандартный список компактных указателей

2755 / 7660

1.20/1

Стандартный дисперсный указатель

6118 / 12453

2.67/1.62


Как и в случае с тестом доступа к чтению, результаты показывают, что интрузивные контейнеры превосходят все другие контейнеры, если значения плотно упакованы в вектор. Распределенный список снова самый медленный.

Интрузивные контейнеры могут предложить преимущества производительности, которые не могут быть достигнуты с эквивалентными неинтрузивными контейнерами. Улучшения локализации памяти заметны, когда вставленные объекты малы. Минимизация вызовов выделения/выделения памяти также является важным фактором, и интрузивные контейнеры делают это простым, если все объекты, подлежащие вставке в интрузивные контейнеры, распределяются с использованиемstd::вектораилиstd::deque.


PrevUpHomeNext

Статья Performance раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 17. Boost.Intrusive может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: Chapter 17. Boost.Intrusive ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 17:36:58/0.034267902374268/1