|  | 
|      | 
|  | 
| PerformanceBoost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 17. Boost.Intrusive
  
   | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Контейнер | Время в нас / итерация (маленький объект / большой объект) | Нормализованное время (маленький объект / большой объект) | 
|---|---|---|
| 
 | 5000 / 22500 | 1/1 | 
| 
 | 7812 / 32187 | 1.56/1.43 | 
| 
 | 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
| Контейнер | Время в нас / итерация (маленький объект / большой объект) | Нормализованное время (маленький объект / большой объект) | 
|---|---|---|
| 
 | 4375/22187 | 1/1 | 
| 
 | 7812 / 32812 | 1,78 / 1,47 | 
| 
 | 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)
| Контейнер | Время в нас / итерация (маленький объект / большой объект) | Нормализованное время (маленький объект / большой объект) | 
|---|---|---|
| 
 | 4792 / 20495 | 1/1 | 
| 
 | 7709 / 30803 | 1,60 / 1,5 | 
| 
 | 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
| Контейнер | Время в нас / итерация (маленький объект / большой объект) | Нормализованное время (маленький объект / большой объект) | 
|---|---|---|
| 
 | 2656 / 10625 | 1/1.83 | 
| 
 | 2812 / 10937 | 1,05 / 1,89 | 
| 
 | 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
| Контейнер | Время в нас / итерация (маленький объект / большой объект) | Нормализованное время (маленький объект / большой объект) | 
|---|---|---|
| 
 | 2656 / 10781 | 1 / 2.22 | 
| 
 | 2656 / 10781 | 1 / 2.22 | 
| 
 | 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)
| Контейнер | Время в нас / итерация (маленький объект / большой объект) | Нормализованное время (маленький объект / большой объект) | 
|---|---|---|
| 
 | 2742 / 10847 | 1/3.41 | 
| 
 | 2742 / 10847 | 1/3.41 | 
| 
 | 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
| Контейнер | Время в нас / итерация (маленький объект / большой объект) | Нормализованное время (маленький объект / большой объект) | 
|---|---|---|
| 
 | 16093 / 38906 | 1/1 | 
| 
 | 16093 / 39062 | 1/1 | 
| 
 | 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
| Контейнер | Время в нас / итерация (маленький объект / большой объект) | Нормализованное время (маленький объект / большой объект) | 
|---|---|---|
| 
 | 15000 / 39218 | 1/1 | 
| 
 | 15156 / 39531 | 1.01 / 1.01 | 
| 
 | 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)
| Контейнер | Время в нас / итерация (маленький объект / большой объект) | Нормализованное время (маленький объект / большой объект) | 
|---|---|---|
| 
 | 18003 / 40795 | 1/1 | 
| 
 | 18003 / 41017 | 1/1 | 
| 
 | 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
| Контейнер | Время в нас / итерация (маленький объект / большой объект) | Нормализованное время (маленький объект / большой объект) | 
|---|---|---|
| 
 | 2031 / 8125 | 1/1 | 
| 
 | 2031 / 8281 | 1/1.01 | 
| 
 | 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
| Контейнер | Время в нас / итерация (маленький объект / большой объект) | Нормализованное время (маленький объект / большой объект) | 
|---|---|---|
| 
 | 2343 / 8281 | 1/1 | 
| 
 | 2500 / 8281 | 1.06/1 | 
| 
 | 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)
| Контейнер | Время в нас / итерация (маленький объект / большой объект) | Нормализованное время (маленький объект / большой объект) | 
|---|---|---|
| 
 | 2286 / 8468 | 1/1,1 | 
| 
 | 2381 / 8412 | 1.04 / 1.09 | 
| 
 | 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.
Статья Performance раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 17. Boost.Intrusive может быть полезна для разработчиков на c++ и boost.
:: Главная :: Chapter 17. Boost.Intrusive ::
| реклама |