Сколько стоит накладные расходы интеллектуальных указателей по сравнению с обычными указателями в 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 63

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 Это хорошее, жизнеспособное и удивительное решение.