Сколько накладных расходов на интеллектуальные указатели по сравнению с обычными указателями на C ++?

Сколько накладных расходов на интеллектуальные указатели по сравнению с обычными указателями на C ++ 11? Другими словами, мой код будет медленнее, если я использую интеллектуальные указатели, и если да, то насколько медленнее?

В частности, я спрашиваю о C ++ 11 std::shared_ptr и std::unique_ptr .

Очевидно, что материал, который толкнул стек, будет больше (по крайней мере, я так думаю), потому что умный указатель также должен хранить свое внутреннее состояние (количество ссылок и т. Д.), На самом деле вопрос: сколько это будет повлиять на мою работу, если вообще?

Например, я возвращаю умный указатель из функции вместо обычного указателя:

 std::shared_ptr getValue(); // versus const Value *getValue(); 

Или, например, когда одна из моих функций принимает интеллектуальный указатель как параметр вместо обычного указателя:

 void setValue(std::shared_ptr val); // versus void setValue(const Value *val); 

    std::unique_ptr имеет накладные расходы памяти, только если вы предоставили ему некоторый нетривиальный дебетер.

    std::shared_ptr всегда имеет накладные расходы памяти для счетчика ссылок, хотя он очень мал.

    std::unique_ptr имеет временные издержки только во время конструктора (если он должен скопировать предоставленный делектор и / или инициализировать нуль указателем) и во время деструктора (для уничтожения принадлежащего ему объекта).

    std::shared_ptr имеет временные накладные расходы в конструкторе (для создания контрольного счетчика), в деструкторе (для уменьшения счетчика ссылок и, возможно, для уничтожения объекта) и в операторе присваивания (для увеличения счетчика ссылок). Благодаря гарантиям безопасности streamа std::shared_ptr эти приращения / декременты являются атомарными, что добавляет некоторые дополнительные накладные расходы.

    Обратите внимание, что ни один из них не имеет накладных расходов времени при разыменовании (при получении ссылки на принадлежащий ему объект), в то время как эта операция, по-видимому, является наиболее распространенной для указателей.

    Подводя итог, есть некоторые накладные расходы, но он не должен делать код медленным, если вы не будете постоянно создавать и уничтожать интеллектуальные указатели.

    Как и при всей производительности кода, единственным надежным средством получения жесткой информации является измерение и / или проверка машинного кода.

    Тем не менее, простые рассуждения говорят, что

    • Вы можете ожидать некоторые накладные расходы в отладочных assemblyх, поскольку, например, operator-> должен выполняться как вызов функции, чтобы вы могли в него вступить (это в свою очередь связано с отсутствием поддержки classов и функций маркировки как отладки).

    • Для shared_ptr вы можете ожидать некоторые накладные расходы при первоначальном создании, поскольку это связано с динамическим распределением блока управления, а динамическое распределение происходит намного медленнее, чем любая другая базовая операция в C ++ (при использовании практически невозможно использовать make_shared , чтобы минимизировать эти накладные расходы).

    • Кроме того, для shared_ptr существует некоторая минимальная накладная плата при поддержании подсчета ссылок, например, при передаче значения shared_ptr по значению, но нет таких накладных расходов для unique_ptr .

    Помните, что при первом измерении вы должны учитывать это как для отладки, так и для выпуска.

    Международный комитет по стандартизации С ++ опубликовал технический отчет об эффективности , но это было в 2006 году, прежде чем unique_ptr и shared_ptr были добавлены в стандартную библиотеку. Тем не менее, умные указатели были старой шляпой в этот момент, поэтому в докладе также было сказано. Цитируя соответствующую часть:

    «Если доступ к значению с помощью тривиального умного указателя значительно медленнее, чем доступ к нему с помощью обычного указателя, компилятор неэффективно обрабатывает абстракцию. Раньше у большинства компиляторов были значительные штрафные санкции за абстракции, и несколько современных компиляторов все еще делают. Однако, как сообщается, по меньшей мере у двух компиляторов были штрафы за абстракцию ниже 1%, а другой – штраф в размере 3%, поэтому устранение такого рода накладных расходов вполне соответствует уровню техники ”

    Как известно, «на современном уровне техники» было достигнуто с самыми популярными компиляторами сегодня, с начала 2014 года.

    Мой ответ отличается от других, и мне действительно интересно, если они когда-либо были профилированным кодом.

    shared_ptr имеет значительные накладные расходы для создания из-за его распределения памяти для блока управления (который держит счетчик ref и список указателей ко всем слабым ссылкам). Из-за этого у него также огромные издержки памяти и тот факт, что std :: shared_ptr всегда является 2 указательным кортежем (один для объекта, один для блока управления).

    Если вы передадите shared_pointer функции в качестве параметра значения, то она будет по меньшей мере в 10 раз медленнее, чем обычный вызов, и создаст много кодов в сегменте кода для разворачивания стека. Если вы передадите его по ссылке, вы получите дополнительное косвенное отношение, которое также может быть еще хуже с точки зрения производительности.

    Вот почему вы не должны этого делать, если функция действительно не участвует в управлении собственностью. В противном случае используйте «shared_ptr.get ()». Он не предназначен для обеспечения того, чтобы ваш объект не был убит во время обычного вызова функции.

    Если вы сойдете с ума и используете shared_ptr на небольших объектах, таких как абстрактное синтаксическое дерево в компиляторе или на небольших узлах в любой другой структуре графика, вы увидите огромное падение производительности и огромное увеличение памяти. Я видел систему парсера, которая была переписана вскоре после выхода C ++ 14 на рынок и до того, как программист научился правильно использовать интеллектуальные указатели. Переписывание было на величину медленнее, чем старый код.

    Это не серебряная пуля, а исходные указатели неплохо по определению. Плохие программисты плохой и плохой дизайн плохой. Дизайн с осторожностью, дизайн с понятным владением и попытка использовать shared_ptr в основном на границе API подсистемы.

    Если вы хотите узнать больше, вы можете посмотреть Николая М. Йосуттиса на хороший разговор о «реальной цене общих указателей на C ++» https://vimeo.com/131189627
    Он углубляется в детали реализации и архитектуру процессора для барьеров записи, атомных замков и т. Д., Когда вы слушаете, вы никогда не будете говорить об этой дешевой цене. Если вы просто хотите, чтобы показатель величины был медленнее, пропустите первые 48 минут и посмотрите, как он запускает примерный код, который работает в 180 раз медленнее (скомпилирован с -O3) при использовании общего указателя везде.

    Другими словами, мой код будет медленнее, если я использую интеллектуальные указатели, и если да, то насколько медленнее?

    Помедленнее? Скорее всего, нет, если вы не создаете огромный индекс с использованием shared_ptrs, и у вас недостаточно памяти, чтобы ваш компьютер начал морщиться, как старушка, которая упала на землю с невыносимой силой издалека.

    Что замедлит ваш код, это вялый поиск, ненужная обработка цикла, огромные копии данных и много операций записи на диск (например, сотни).

    Преимущества интеллектуального указателя связаны с управлением. Но нужны ли накладные расходы? Это зависит от вашей реализации. Предположим, вы повторяете массив из трех фаз, каждая фаза имеет массив из 1024 элементов. Создание smart_ptr для этого процесса может быть излишним, так как после завершения итерации вы узнаете, что вам нужно его стереть. Таким образом, вы можете получить дополнительную память, не используя smart_ptr

    Но вы действительно хотите это сделать?

    Одна утечка памяти может привести к сбою вашего продукта во времени (скажем, ваша программа протекает 4 мегабайта в час, потребуется несколько месяцев, чтобы сломать компьютер, тем не менее, он сломается, вы знаете, потому что там есть утечка) ,

    Это похоже на то, что «ваше программное обеспечение гарантировано на 3 месяца, а затем позвоните мне для обслуживания».

    Поэтому, в конце концов, это действительно вопрос … можете ли вы справиться с этим риском? использование неочищенного указателя для обработки индексирования по сотням разных объектов стоит потерять контроль над памятью.

    Если да, то используйте необработанный указатель.

    Если вы даже не хотите его рассматривать, smart_ptr – это хорошее, жизнеспособное и удивительное решение.

    Давайте будем гением компьютера.