Сколько накладных расходов на интеллектуальные указатели по сравнению с обычными указателями в C ++ 11? Другими словами, будет ли мой код медленнее, если я использую интеллектуальные указатели, и если да, то насколько медленнее?
В частности, я спрашиваю о C ++ 11 std::shared_ptr
и std::unique_ptr
.
Очевидно, что материал, помещенный в стек, будет больше (по крайней мере, я так думаю), потому что интеллектуальный указатель также должен хранить свое внутреннее состояние (количество ссылок и т. Д.), Вопрос действительно в том, сколько это будет повлияет ли вообще на мою работу?
Например, я возвращаю умный указатель из функции вместо обычного указателя:
std::shared_ptr<const Value> getValue();
// versus
const Value *getValue();
Или, например, когда одна из моих функций принимает в качестве параметра интеллектуальный указатель вместо обычного указателя:
void setValue(std::shared_ptr<const Value> val);
// versus
void setValue(const Value *val);
источник
std::unique_ptr
илиstd::shared_ptr
?Ответы:
std::unique_ptr
имеет накладные расходы на память, только если вы предоставите ему какой-нибудь нетривиальный удалитель.std::shared_ptr
всегда есть накладные расходы памяти для счетчика ссылок, хотя они очень маленькие.std::unique_ptr
имеет накладные расходы времени только во время работы конструктора (если он должен скопировать предоставленный удалитель и / или инициализировать указатель нулевым значением) и во время деструктора (чтобы уничтожить принадлежащий объект).std::shared_ptr
имеет накладные расходы времени в конструкторе (для создания счетчика ссылок), в деструкторе (для уменьшения счетчика ссылок и, возможно, уничтожения объекта) и в операторе присваивания (для увеличения счетчика ссылок). Из-за гарантий безопасности потоковstd::shared_ptr
, эти приращения / уменьшения являются атомарными, что приводит к дополнительным накладным расходам.Обратите внимание, что ни у одного из них нет накладных расходов времени на разыменование (получение ссылки на принадлежащий объект), в то время как эта операция кажется наиболее распространенной для указателей.
Подводя итог, можно сказать, что есть некоторые накладные расходы, но они не должны замедлять код, если вы не будете постоянно создавать и уничтожать интеллектуальные указатели.
источник
unique_ptr
не имеет накладных расходов в деструкторе. Он делает то же самое, что и необработанный указатель.std::unique_ptr
? Если вы создаете astd::unique_ptr<int>
, внутренняяint*
инициализируетсяnullptr
, нравится вам это или нет.Как и в случае с производительностью любого кода, единственный действительно надежный способ получения достоверной информации - это измерение и / или проверка машинного кода.
Тем не менее, простые рассуждения говорят, что
Вы можете ожидать некоторых накладных расходов в отладочных сборках, поскольку, например, он
operator->
должен выполняться как вызов функции, чтобы вы могли войти в него (это, в свою очередь, из-за общего отсутствия поддержки для маркировки классов и функций как неотладочных).Поскольку
shared_ptr
вы можете ожидать некоторые накладные расходы при первоначальном создании, поскольку это включает в себя динамическое выделение блока управления, а динамическое выделение выполняется намного медленнее, чем любая другая базовая операция в C ++ (используйте,make_shared
когда это практически возможно, чтобы минимизировать эти накладные расходы).Также
shared_ptr
есть некоторые минимальные накладные расходы на поддержание счетчика ссылок, например, при передачеshared_ptr
по значению, но таких накладных расходов нет дляunique_ptr
.Помня о первом пункте выше, при измерении делайте это как для отладочной, так и для выпускной сборок.
Комитет по стандартизации C ++ Международного опубликовал технический отчет о работе , но это было в 2006 году, до того
unique_ptr
иshared_ptr
были добавлены к стандартной библиотеке. Тем не менее, умные указатели на тот момент были уже устаревшей, поэтому в отчете учитывалось и это. Цитата из соответствующей части:Как обоснованное предположение, по состоянию на начало 2014 года, наиболее популярные компиляторы достигли «уровня современного уровня техники».
источник
Мой ответ отличается от других, и мне действительно интересно, профилировали ли они когда-нибудь код.
shared_ptr имеет значительные накладные расходы на создание из-за выделения памяти для блока управления (который хранит счетчик ссылок и список указателей на все слабые ссылки). Он также имеет огромные накладные расходы на память из-за этого и того факта, что std :: shared_ptr всегда является кортежем с двумя указателями (один на объект, один на блок управления).
Если вы передадите shared_pointer функции в качестве параметра значения, тогда он будет как минимум в 10 раз медленнее, чем обычный вызов, и создаст много кодов в сегменте кода для раскрутки стека. Если вы передадите его по ссылке, вы получите дополнительное косвенное обращение, которое также может быть значительно хуже с точки зрения производительности.
Вот почему вам не следует этого делать, если функция действительно не участвует в управлении собственностью. В противном случае используйте shared_ptr.get (). Он не предназначен для того, чтобы ваш объект не был убит во время обычного вызова функции.
Если вы сойдете с ума и используете shared_ptr на небольших объектах, таких как абстрактное синтаксическое дерево в компиляторе или на небольших узлах в любой другой структуре графа, вы увидите огромное падение производительности и огромное увеличение памяти. Я видел систему синтаксического анализа, которая была переписана вскоре после того, как C ++ 14 вышел на рынок, и до того, как программист научился правильно использовать интеллектуальные указатели. Переписывание шло намного медленнее, чем старый код.
Это не серебряная пуля, и необработанные указатели тоже неплохи по определению. Плохие программисты - это плохо, а плохой дизайн - это плохо. Проектируйте с осторожностью, создавайте с четкой принадлежностью и старайтесь использовать shared_ptr в основном на границе API подсистемы.
Если вы хотите узнать больше, вы можете посмотреть хороший доклад Николая М. Йосуттиса о «Реальной цене общих указателей в C ++» https://vimeo.com/131189627
Он углубляется в детали реализации и архитектуру ЦП для барьеров записи, атомарных замки и т. д. после прослушивания вы никогда не скажете о дешевизне этой функции. Если вы просто хотите получить доказательство того, что величина медленнее, пропустите первые 48 минут и посмотрите, как он запускает пример кода, который работает до 180 раз медленнее (скомпилированный с -O3) при повсеместном использовании общего указателя.
источник
std::make_shared()
? Кроме того, я нахожу демонстрации вопиющего неправильного использования немного скучными ...Другими словами, будет ли мой код медленнее, если я использую интеллектуальные указатели, и если да, то насколько медленнее?
Помедленнее? Скорее всего, нет, если только вы не создаете огромный индекс с помощью shared_ptrs и у вас недостаточно памяти до такой степени, что ваш компьютер начинает морщиться, как старуха, которая падает на землю невыносимой силой издалека.
Что могло бы сделать ваш код медленнее, так это вялый поиск, ненужная обработка циклов, огромные копии данных и множество операций записи на диск (например, сотни).
Все преимущества умного указателя связаны с управлением. Но нужны ли накладные расходы? Это зависит от вашей реализации. Допустим, вы выполняете итерацию по массиву из 3 фаз, каждая фаза имеет массив из 1024 элементов. Создание
Но вы действительно хотите этого?smart_ptr
для этого процесса может быть излишним, поскольку после завершения итерации вы будете знать, что вам нужно его стереть. Таким образом, вы можете получить дополнительную память, не используяsmart_ptr
...Единственная утечка памяти может привести к тому, что у вашего продукта возникнет момент сбоя (допустим, ваша программа пропускает 4 мегабайта каждый час, на поломку компьютера уйдут месяцы, тем не менее, он сломается, вы это знаете, потому что утечка есть) .
Это все равно что сказать: «У вас гарантия на программное обеспечение 3 месяца, тогда звоните мне в сервис».
Так что, в конце концов, это действительно вопрос ... сможете ли вы справиться с этим риском? действительно ли использование необработанного указателя для обработки индексации сотен различных объектов стоит потери контроля над памятью.
Если да, используйте необработанный указатель.
Если вы даже не хотите об этом думать,
smart_ptr
это хорошее, жизнеспособное и отличное решение.источник
smart_ptr
они действительно полезны для больших командThats why you should not do this unless the function is really involved in ownership management
... отличный ответ, спасибо, проголосовали заПросто для ознакомления и просто для
[]
оператора, он примерно в 5 раз медленнее, чем необработанный указатель, как показано в следующем коде, который был скомпилирован с использованиемgcc -lstdc++ -std=c++14 -O0
и выдал этот результат:Я начинаю изучать C ++, и я подумал: вам всегда нужно знать, что вы делаете, и уделять больше времени тому, чтобы узнать, что другие сделали в вашем C ++.
РЕДАКТИРОВАТЬ
Как сообщил @Mohan Kumar, я предоставил более подробную информацию. Версия gcc:
7.4.0 (Ubuntu 7.4.0-1ubuntu1~14.04~ppa1)
Приведенный выше результат был получен при-O0
использовании, однако, когда я использую флаг '-O2', я получил следующее:Затем сместился на
clang version 3.9.0
,-O0
было:-O2
был:Результат clang
-O2
потрясающий.источник
-O0
и не отлаживайте код. Выход будет крайне неэффективным . Всегда используйте хотя бы-O2
(или в-O3
настоящее время, потому что некоторая векторизация не выполняется-O2
)free
вызов в тест malloc иdelete[]
для new (или сделать переменнуюa
статической), потому чтоunique_ptr
s вызываютсяdelete[]
под капотом в своих деструкторах.