Комментарии 44
Слишком много таблиц — для ленивых ( прокрутивших для вывода) не сразу понятно какой же самый быстрый
Хотелось бы более подробного вывода — вот например почему указатель на some_obj настолько медленнее?
Хотелось бы более подробного вывода — вот например почему указатель на some_obj настолько медленнее?
Просто — это же обычный указатель ( simple pointer ), какая разница на что он указывает для контейнера
Это выглядит очень подозрительно
Это выглядит очень подозрительно
указатель на some_obj по производительности идет рядом с указателем на std::string, т.к. основной нагрузочный вес составляет some_obj.s (справедливо при бОльшем заполнении контейнеров объектами содержащими указатель на тестовый тип данных)
что такое some_obj.s?
Я все равно не понимаю как это влияет на производительность
some_obj * x =…
int * y =…
Почему пуш в вектор х в два раза медленнее чем в y?
размер у указателей одинаковый
А если я приведу к void * и буду его пушить, то как тогда получится?
some_obj * x =…
int * y =…
Почему пуш в вектор х в два раза медленнее чем в y?
размер у указателей одинаковый
А если я приведу к void * и буду его пушить, то как тогда получится?
test_type_and_smart_pointer< _Ptr, T > x2( x );
Container cnt;
cnt.push_back( x );
здесь test_type_and_smart_pointer это объект, внутри которого указатель на динамически распределенную память под испытуемый тип. в вектор ложатся разные куски памяти.
Container cnt;
cnt.push_back( x );
здесь test_type_and_smart_pointer это объект, внутри которого указатель на динамически распределенную память под испытуемый тип. в вектор ложатся разные куски памяти.
щито?
контейнер получает указатель, ему глубоко наплевать на то какого он типа, и сохраняет он внутрь себя указатель
И так как указатели одинаквого размера, а больше никаких операций не производится, то и разницы от типа используемого как тип шаблона контейнера быть не должно
То что у вас разница в 2 раза это ерундитстика и антинаучно. У вас там какой нибудь конструктор небось замеряется ( я конечно вечером посмотрю подробно код, но сейчас я на работе) или вы видеоконвертер фоном запускали, но разница в2 раза это и ряджом с правдой не стояло
контейнер получает указатель, ему глубоко наплевать на то какого он типа, и сохраняет он внутрь себя указатель
И так как указатели одинаквого размера, а больше никаких операций не производится, то и разницы от типа используемого как тип шаблона контейнера быть не должно
То что у вас разница в 2 раза это ерундитстика и антинаучно. У вас там какой нибудь конструктор небось замеряется ( я конечно вечером посмотрю подробно код, но сейчас я на работе) или вы видеоконвертер фоном запускали, но разница в2 раза это и ряджом с правдой не стояло
Причем в подтверждение моих слов служит то, что чем сложнее конструктор тем больше время
long and char — самые быстрые
string дольше там надо что то делать в конструкторе
some_obj еще сложнее — там ведь надо сделать конструктор стринга и + еще поля есть
Попробуйте добавьте полей в some_obj у вас еще смешнее цифры получаться
long and char — самые быстрые
string дольше там надо что то делать в конструкторе
some_obj еще сложнее — там ведь надо сделать конструктор стринга и + еще поля есть
Попробуйте добавьте полей в some_obj у вас еще смешнее цифры получаться
char * s = new char[ 5 ];
s[0] = 'a'; s[1] = 'b';
std::vector< std::string > v;
v.push_back( s );
delete s;
std::string d = *v.begin();
>> d = 'ab';
учите c++, эмоции потом.
s[0] = 'a'; s[1] = 'b';
std::vector< std::string > v;
v.push_back( s );
delete s;
std::string d = *v.begin();
>> d = 'ab';
учите c++, эмоции потом.
и что? как это связано с моим вопросом? вы вообще читаете мои комментарии?
Если вы пытаетесь унизить мои знания си++ ( а они далеки от идеала поэтому я и пытаюсь разобраться )
то пишите развернутые ответы — ваш кусок кода для меня не имеет связи с моим замечанием о скорости работы
Если вы пытаетесь унизить мои знания си++ ( а они далеки от идеала поэтому я и пытаюсь разобраться )
то пишите развернутые ответы — ваш кусок кода для меня не имеет связи с моим замечанием о скорости работы
контейнер сохранит в себя указатель, если его попросят так vector< obj * >, а если его просят vector, то он СКОПИРУЕТ память занимаемую объектом, т.е. то что приходит например методом push_back. и писать куски памяти в рассматриваемом случае вектор будет последовательно (т.к. контейнер однопроходный).
попросят obj без звездочки
А что значит «контейнер однопроходной»?
Обращаться к удаленной памяти — сильно.
Статью прочитать не хватило внимание. Но мне показалось судя по таблице, что auto_ptr засовывается в vector. Это так?
нет.
если коменту ставят плюсы, тогда уведомлю: статья для мыслящих людей, а не ленивых, кому читать текст лень и понимать, что происходит в программе.
А где программа? Хорошо бы выложить полный код. А то вот я смотрю на это, и не понимаю… Как по мне, то это массив из auto_ptr:
комбинация параметров шаблонов достаточно ясна:
т.е.
auto f_0n = [&]() -> void
{ test_smart_ptr< auto_ptr< long >, long, vector< test_type_and_smart_pointer< auto_ptr< long >, long > >, false >( false, long_val ); };
т.е.
auto f_0n = [&]() -> void
{ test_smart_ptr< УмныйУказатель< ТипДанных>, ТипДанных, Контейнер< test_type_and_smart_pointer< УмныйУказатель< ТипДанных>, ТипДанных> >, false >( true, ЗначениеТипаДанных); };
auto b_16 = [&]() -> void
{ test_simple_ptr< ТипДанных, Контейнер< test_type_and_simple_pointer< ТипДанных*, ТипДанных> >, false >( true, ЗначениеТипаДанных); };
Акцент на том, что std::auto_ptr нельзя использовать в стандартных контейнерах (COAP)
так он и не используется в контейнере напрямую, ввиду семантики копирования и присваивания. см. определение шаблона test_type_and_smart_pointer.
не важно, напрямую он там или нет, важно что он входит в элемент контейнера как один из подэлементов, следовательно, при копировании элемента контейнера вызывается конструктор копирования и auto_ptr в том числе, а этого нельзя допускать ни в коем случае, т.к. контейнер считает что если он скопировал элемент А в элемент Б, то А остался неизменным (его ведь скопировали, а не переместили), но в случае auto_ptr это не так, т.к. конструктор копированрия auto_ptr его нифига не копирует а перемещает.
Раз используется VS2010 то зачем вы используете std::auto_ptr? Он уже deprecated, используйте вместо него std::unique_ptr.
Кстати неплохо бы в статье указать на какой конфигурации проводились замеры скорости.
Кстати неплохо бы в статье указать на какой конфигурации проводились замеры скорости.
Что-то сложно для понимания Ваш эксперимент. Хотелось уточнить и выссказать свои мысли.
Цель: Протестировать производительность операции вставки в конец контейнера объектов, содержащих смарт-поинтер на определенный тип.
По-моему мнению, есть несколько важных упущений.
Цель: Протестировать производительность операции вставки в конец контейнера объектов, содержащих смарт-поинтер на определенный тип.
По-моему мнению, есть несколько важных упущений.
- Практически во всех экспериментах существуют погрешности вычисления. Обычно его стараются уменьшить, а Вы увеличили за счёт использования собственного оператора копирования в объекте-обертке.
- std::vector имееть особенность которая влияет на производительность операции push_back. Заключается она в том, что перевыделение памяти происходит по достижению заполненности буфера, затем выделяется в 2 раза больший массив и туда копируются элементы. Несмотря на это общее амортизированное время вставки в конец будет O(1). Для того, чтобы учесть данную особенность необходимо было количество элементов контейнера выбирать случайным образом используя метод Монте-Карло.
- Выделение памяти в контейнерах происходит за счет аллокаторов, а копирование объектов инкапсулированно внутри самих объектов. В чем состоит зависимость(связность, корреляция) и почему ведется оценка данных вещей вместе? Не будет ли надёжней оценить их отдельно?
- Что дает эксперимент для реального проекта? За счет какой составляющей эксперимента должна производиться оптимизация (выбор контейнера, указателя, тип)? Я пока не вижу реальной пользы в этом эксперименте, приведите пример, пожалуйста.
ахахах, спасибо вам, я писал сверху то же самое но более сумбурно, на что получал бредовые несвязанные с моим вопросом комментарии с рандомными кусками кода и так и не смог добиться справедливости!
Я пытался объяснить что string и some_obj кладутся медленнее в случае simple_pointer, потому что замеряется вместе с конструктором, но меня не слушали
Я пытался объяснить что string и some_obj кладутся медленнее в случае simple_pointer, потому что замеряется вместе с конструктором, но меня не слушали
GetTickCount очень неточная функция с очень большим оверхедом, для более точных замеров нужно использовать QueryPerformanceCounter/QueryPerformanceFrequency, либо считать такты по rdtsc. перед началом замеров нужно привязать поток к конкретному ядру.
У вас в конструкторе test_type_and_smart_pointer<shared_ptr> фактически происходит
shared_ptr p;
p.reset(new Type());
хотяp(make_shared<Type>())
должно было бы быть гораздо эффективнее. Напишите специализацию этого шаблона для shared_ptr и померяйте ещё раз.Беда.
1.
auto_ptr нельзя совать в контейнеры, даже если он «внутри» другого класса, потому что его оператор копирования его не копирует а перемещает. Если хочется что-то типа auto_ptr, нужно использовать std::unique_ptr, он поддерживает move семантику, собственно и будет пользоваться контейнер.
2.
Сюрприз (assume x is shared_ptr)!
x.reset( new Type );
работает ровно в 2 раза медленнее, чем
x = std::make_shared();
(только вчера мерял)
Это происходит потому, что в случае
x.reset( new Type );
память выделяется 2 раза: один раз для Type, второй раз для счетчика ссылок, в случае с make_shared, память выделяется только один раз: для специального объекта, который будет хранить и Type и счетчик ссылок.
3.
Раз уж вы меряете производительность, нужно обязательно определить move конструктор для вашего «хранилища», и внутри вызывать move конструкторы как для std::string (чтоб память не копировать), так и для shared_ptr (потому что move конструктор для shared_ptr он, в отличие от конструктора копирования, lock free, а interlocked exchange — это очень медленно)
4.
А если хочется еще быстре нужно использовать small object allocator, это вам еще 30% скорости даст
5.
Это еще не вся беда, теперь подробно по коду
а.
std::string _name; — че это за фигня такая?? строки они МЕДЛЕННЫЕ, они память выделяют каждый раз при создании, вы фактически не Type тестировать будете, а строки, особенно если Type — это что-то вроде int.
Если очень хочется и имя выводить и тест, используйте «const TCHAR *» вместо строк, оно память выделять не будет. Нда, надеюсь, выводить имя — это для отладки, потому как std::cout << ...; это еще медленнее, чем std::string.
б.
test_type_and_smart_pointer< _Ptr, T > xx( «a002», msg, val );
test_type_and_smart_pointer< _Ptr, T > xx2( xx );
cnt.push_back( xx );
cnt.push_back( xx2 );
это тоже очень беда, так будет гораздо быстрее:
cnt.push_back( test_type_and_smart_pointer< _Ptr, T >( «a002», msg, val ) );
cnt.push_back( test_type_and_smart_pointer< _Ptr, T >( «a002», msg, val ) );
почему? потому что будет использоваться move constructor для _Ptr, который в 20-30 раз(!) быстрее конструктора копирования. test_type_and_smart_pointer естественно должен тоже реализовывать move contructor.
в.
Вы умудрились засунуть std::function внутрь цикла, надо template, std::function она медленнее, не намного, но может и повлиять на результаты.
г.
«В качестве объекта произвольного типа использован простейший класс без динамических выделений памяти.»
а s = «1231231231231231231232»; — это у вас что, как не динамическое выделение памяти под строку?!
6.
Тест в целом лишен смысле
у std::vector, std::list и std::deque есть определенные границы применимости, ничего там тестировать не надо, кроме того, вы тестируете «воздух» а не реальные данные.
Приведу пример, допустим вам нужен массив из интов на что-то около 100 элементов, и вы собираетесь по нему часто ходить последовательно, так вот вы будете очень удивлены, но вектор в данном случае будет раз в 10 быстрее списка. Почему?
взгляните:
for( auto it = cnt.begin(); it != cnt.end(); ++it ) if( *it == N ) break;
в случае вектора, процессор будет обращаться к одной и той же области памяти при поиске вашего N, в случае списке, каждый последующий элемент лежит в памяти «фиг знает где», а значит больше не работает процессорный кэш
7.
Ну и на последок, вам самому не странно, что согласно вашим «результатам» строки работают почти так же быстро как числа?
В общем, хотелось бы видеть хорошее понимание матчасти в таких статьях.
1.
auto_ptr нельзя совать в контейнеры, даже если он «внутри» другого класса, потому что его оператор копирования его не копирует а перемещает. Если хочется что-то типа auto_ptr, нужно использовать std::unique_ptr, он поддерживает move семантику, собственно и будет пользоваться контейнер.
2.
Сюрприз (assume x is shared_ptr)!
x.reset( new Type );
работает ровно в 2 раза медленнее, чем
x = std::make_shared();
(только вчера мерял)
Это происходит потому, что в случае
x.reset( new Type );
память выделяется 2 раза: один раз для Type, второй раз для счетчика ссылок, в случае с make_shared, память выделяется только один раз: для специального объекта, который будет хранить и Type и счетчик ссылок.
3.
Раз уж вы меряете производительность, нужно обязательно определить move конструктор для вашего «хранилища», и внутри вызывать move конструкторы как для std::string (чтоб память не копировать), так и для shared_ptr (потому что move конструктор для shared_ptr он, в отличие от конструктора копирования, lock free, а interlocked exchange — это очень медленно)
4.
А если хочется еще быстре нужно использовать small object allocator, это вам еще 30% скорости даст
5.
Это еще не вся беда, теперь подробно по коду
а.
std::string _name; — че это за фигня такая?? строки они МЕДЛЕННЫЕ, они память выделяют каждый раз при создании, вы фактически не Type тестировать будете, а строки, особенно если Type — это что-то вроде int.
Если очень хочется и имя выводить и тест, используйте «const TCHAR *» вместо строк, оно память выделять не будет. Нда, надеюсь, выводить имя — это для отладки, потому как std::cout << ...; это еще медленнее, чем std::string.
б.
test_type_and_smart_pointer< _Ptr, T > xx( «a002», msg, val );
test_type_and_smart_pointer< _Ptr, T > xx2( xx );
cnt.push_back( xx );
cnt.push_back( xx2 );
это тоже очень беда, так будет гораздо быстрее:
cnt.push_back( test_type_and_smart_pointer< _Ptr, T >( «a002», msg, val ) );
cnt.push_back( test_type_and_smart_pointer< _Ptr, T >( «a002», msg, val ) );
почему? потому что будет использоваться move constructor для _Ptr, который в 20-30 раз(!) быстрее конструктора копирования. test_type_and_smart_pointer естественно должен тоже реализовывать move contructor.
в.
Вы умудрились засунуть std::function внутрь цикла, надо template, std::function она медленнее, не намного, но может и повлиять на результаты.
г.
«В качестве объекта произвольного типа использован простейший класс без динамических выделений памяти.»
а s = «1231231231231231231232»; — это у вас что, как не динамическое выделение памяти под строку?!
6.
Тест в целом лишен смысле
у std::vector, std::list и std::deque есть определенные границы применимости, ничего там тестировать не надо, кроме того, вы тестируете «воздух» а не реальные данные.
Приведу пример, допустим вам нужен массив из интов на что-то около 100 элементов, и вы собираетесь по нему часто ходить последовательно, так вот вы будете очень удивлены, но вектор в данном случае будет раз в 10 быстрее списка. Почему?
взгляните:
for( auto it = cnt.begin(); it != cnt.end(); ++it ) if( *it == N ) break;
в случае вектора, процессор будет обращаться к одной и той же области памяти при поиске вашего N, в случае списке, каждый последующий элемент лежит в памяти «фиг знает где», а значит больше не работает процессорный кэш
7.
Ну и на последок, вам самому не странно, что согласно вашим «результатам» строки работают почти так же быстро как числа?
В общем, хотелось бы видеть хорошее понимание матчасти в таких статьях.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Тест производительности контейнеров и указателей на объекты