Сколько стоит накладные расходы интеллектуальных указателей по сравнению с обычными указателями в C++?
сколько стоит накладные смарт-указатели, по сравнению с обычными указателями в 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);
4 ответа:
std::unique_ptr
имеет накладные расходы памяти, только если вы предоставляете его с некоторыми нетривиальными deleter.
std::shared_ptr
всегда имеет память для счетчика ссылок, хотя это очень мало.
std::unique_ptr
имеет время накладных расходов только во время конструктора (если он должен скопировать предоставленный deleter и/или null-инициализировать указатель) и во время деструктора (чтобы уничтожить принадлежащий объект).
std::shared_ptr
имеет накладные расходы времени в конструкторе (для создания счетчика ссылок), в деструкторе (чтобы уменьшить счетчик ссылок и, возможно, уничтожить объект) и в операторе присваивания (чтобы увеличить счетчик ссылок). Благодаря гарантиям потокобезопасностиstd::shared_ptr
, эти приращения / убывания являются атомарными, что добавляет еще некоторые накладные расходы.обратите внимание, что ни один из них не имеет временных затрат при разыменовании (при получении ссылки на собственный объект), в то время как эта операция, по-видимому, является наиболее распространенной для указателей.
подводя итог, есть некоторые накладные расходы, но это не следует замедлять код, если вы постоянно не создаете и не уничтожаете интеллектуальные указатели.
как и при любой производительности кода, единственным действительно надежным способом получения достоверной информации является мера и/или проверить машинный код.
тем не менее, простое рассуждение говорит, что
вы можете ожидать некоторых накладных расходов в отладочных сборках, так как, например,
operator->
должен быть выполнен как вызов функции, чтобы вы могли войти в него (это, в свою очередь, связано с общим отсутствием поддержки для маркировки классов и функций как non-debug).на
shared_ptr
вы можете ожидать некоторых накладных расходов при первоначальном создании, так как это включает динамическое выделение блока управления, а динамическое выделение очень медленнее, чем любая другая базовая операция в C++ (используйтеmake_shared
когда это практически возможно, чтобы минимизировать накладные расходы).и для
shared_ptr
есть некоторые минимальные накладные расходы в поддержании счетчика ссылок, например, при передачеshared_ptr
по значению, но такого нет накладные расходы дляunique_ptr
.имея в виду первый пункт выше, когда вы измеряете, сделайте это как для отладки, так и для выпуска сборок.
Международный комитет по стандартизации C++ опубликовал технический отчет о работе, но это было в 2006 году, перед
unique_ptr
и . Тем не менее, умные указатели были старой шляпой в тот момент, поэтому в докладе рассматривалось и это. Цитирование соответствующих часть:"если доступ к значению через тривиальный интеллектуальный указатель значительно медленнее, чем доступ к нему с помощью обычного указателя компилятор неэффективно обрабатывает абстракцию. В в прошлом большинство компиляторов имели значительные штрафы за абстракцию и несколько текущих компиляторов еще делать. Однако, по крайней мере, два компилятора сообщалось абстракция штрафы ниже 1% и еще один штраф 3%, так что устранение такого рода накладных расходов является ну в рамках современного искусства"
как сообщил догадаться, "в состояние" был достигнут с самыми популярными компиляторами сегодня, по состоянию на начало 2014 года.
мой ответ отличается от других, и мне действительно интересно, если они когда-либо профилированный код.
shared_ptr имеет значительные накладные расходы для создания из-за его выделения памяти для блока управления (который сохраняет счетчик ссылок и список указателей на все слабые ссылки). Он также имеет огромные накладные расходы памяти из-за этого и того факта, что std::shared_ptr всегда является кортежем указателей 2 (один к объекту, один к блоку управления).
Если вы проходите shared_pointer к функции в качестве параметра значения, то это будет по крайней мере в 10 раз медленнее, чем обычный вызов и создать много кодов в сегменте кода для размотки стека. Если вы передадите его по ссылке, вы получите дополнительную косвенность, которая также может быть довольно хуже с точки зрения производительности.
вот почему вы не должны этого делать, если функция действительно не участвует в управлении собственностью. В противном случае используйте "shared_ptr.получить.")( Он не предназначен, чтобы убедиться, что ваш объект не убит во время обычного вызова функции.
Если вы сходите с ума и используете shared_ptr на небольших объектах, таких как абстрактное синтаксическое дерево в компиляторе или на небольших узлах в любой другой структуре графа, вы увидите огромное падение производительности и огромное увеличение памяти. Я видел систему парсера, которая была переписана вскоре после выхода C++14 на рынок и до того, как программист научился правильно использовать интеллектуальные указатели. Переписывание было на величину медленнее, чем старый код.
Это не серебро пуля и сырые указатели тоже не плохи по определению. Плохие программисты-это плохо, а плохой дизайн-это плохо. Дизайн с осторожностью, дизайн с четким владением в виду и попытаться использовать shared_ptr в основном на границе API подсистемы.
Если вы хотите узнать больше, вы можете посмотреть, как Николай М. Josuttis хорошо говорит о " реальной цене общих указателей в C++"https://vimeo.com/131189627
Он углубляется в детали реализации и архитектуру процессора для записи барьеры, атомные замки etc. после прослушивания вы никогда не будете говорить об этой функции дешево. Если вам просто нужно доказательство величины медленнее, пропустите первые 48 минут и посмотрите, как он запускает пример кода, который работает до 180 раз медленнее (скомпилированный с-O3) при использовании общего указателя везде.
другими словами, Будет ли мой код медленнее, если я использую интеллектуальные указатели, и если да, то насколько медленнее?
медленнее? Скорее всего, нет, если вы не создаете огромный индекс с помощью shared_ptrs, и у вас недостаточно памяти до такой степени, что ваш компьютер начинает морщиться, как старая леди, падающая на землю невыносимой силой издалека.
что бы сделать ваш код медленнее-это вялый поиск, ненужная обработка цикла, огромные копии данных, и много операций записи на диск (например, сотни).
преимущества умного указателя все связаны с управлением. но нужны ли накладные расходы? это зависит от вашей реализации. Предположим, вы повторяете массив из 3 фаз, каждая фаза имеет массив из 1024 элементов. Создание
Но ты действительно хочешь это сделать?smart_ptr
для этого процесса может быть излишним, так как после завершения итерации вы будете знать, что вам нужно стереть его. Таким образом, вы можете получить дополнительную память от не использования аsmart_ptr
...одна утечка памяти может привести к тому, что ваш продукт будет иметь точку сбоя во времени (скажем, ваша программа утекает 4 мегабайта каждый час, потребуется несколько месяцев, чтобы сломать компьютер, тем не менее, он сломается, вы это знаете, потому что утечка есть).
это как сказать: "вы программное обеспечение гарантируется в течение 3 месяцев, а затем, позвоните мне для обслуживания."
Так что в конце концов это действительно вопрос... вы можете справиться с этим риском? использование необработанного указателя для обработки индексирования сотен различных объектов стоит потерять контроль над памятью.
Если ответ да, то использовать исходный указатель.
Если вы даже не хотите рассматривать его, a
smart_ptr
Это хорошее, жизнеспособное и удивительное решение.