Как стать автором
Обновить

Комментарии 44

Слишком много таблиц — для ленивых ( прокрутивших для вывода) не сразу понятно какой же самый быстрый
Хотелось бы более подробного вывода — вот например почему указатель на some_obj настолько медленнее?
Просто — это же обычный указатель ( simple pointer ), какая разница на что он указывает для контейнера
Это выглядит очень подозрительно
в контейнер помешаются сами объекты, а не указатели (см. код test_simple_ptr). в объектах динамически была выделена память на тестовый тип, которой владеет испытуемый указатель.
указатель на some_obj по производительности идет рядом с указателем на std::string, т.к. основной нагрузочный вес составляет some_obj.s (справедливо при бОльшем заполнении контейнеров объектами содержащими указатель на тестовый тип данных)
что такое some_obj.s?
Простите, но приведен код, из которого всё ясно. some_obj.s — это строка, которую содержит в себе some_obj. То есть суммируются затраты на создание строки и собственно some_obj.
что такое some_obj.s?
не туда написал
s член класса some_obj
Я все равно не понимаю как это влияет на производительность

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 это объект, внутри которого указатель на динамически распределенную память под испытуемый тип. в вектор ложатся разные куски памяти.
щито?
контейнер получает указатель, ему глубоко наплевать на то какого он типа, и сохраняет он внутрь себя указатель
И так как указатели одинаквого размера, а больше никаких операций не производится, то и разницы от типа используемого как тип шаблона контейнера быть не должно
То что у вас разница в 2 раза это ерундитстика и антинаучно. У вас там какой нибудь конструктор небось замеряется ( я конечно вечером посмотрю подробно код, но сейчас я на работе) или вы видеоконвертер фоном запускали, но разница в2 раза это и ряджом с правдой не стояло
Причем в подтверждение моих слов служит то, что чем сложнее конструктор тем больше время
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++, эмоции потом.
и что? как это связано с моим вопросом? вы вообще читаете мои комментарии?
Если вы пытаетесь унизить мои знания си++ ( а они далеки от идеала поэтому я и пытаюсь разобраться )
то пишите развернутые ответы — ваш кусок кода для меня не имеет связи с моим замечанием о скорости работы
контейнер сохранит в себя указатель, если его попросят так vector< obj * >, а если его просят vector, то он СКОПИРУЕТ память занимаемую объектом, т.е. то что приходит например методом push_back. и писать куски памяти в рассматриваемом случае вектор будет последовательно (т.к. контейнер однопроходный).
попросят obj без звездочки
так вы в вектор тогда не указатель пихаете а объект, и конечно ваш тест неверен, это все объясняет
смотрите ниже — у меня скорости одинаковые при заполнении вектора 100 000 записей
тест комплексный. дело то в том, что у контейнеров разные аллокаторы.
Вся беда в том что подпись simple_pointer невалидна в вашем тесте ( у вас там вообще не pointer )
даже не знаю, что и сказать… предложу только под отладкой пройти по коду, чтобы понять в чем заключается данный тест.
А что значит «контейнер однопроходной»?
очепятался, говорилось про вектор, что он однонаправленный.
А это что за зверь? Я знаю только однонаправленные итераторы у которых отсутствует операция декремента и минус.
имел ввиду что элементы вектора лежат последовательно в смежных областях памяти.
Обращаться к удаленной памяти — сильно.
да но при чем здесь то, что писал я?
Простите, не увидел, что вектор из стрингов.
берем массив из 100000 объектов типа указатель на инт
берем массив из 100000 объектов типа указатель на простой объект
суем их в вектор

вот у меня на машине скорости выполнения одинаковы ( с точностью до погрешности) но у меня не Windows
Статью прочитать не хватило внимание. Но мне показалось судя по таблице, что 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, потому что замеряется вместе с конструктором, но меня не слушали
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.
Ну и на последок, вам самому не странно, что согласно вашим «результатам» строки работают почти так же быстро как числа?

В общем, хотелось бы видеть хорошее понимание матчасти в таких статьях.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории