![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
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 ::
реклама |