Насколько использование интеллектуальных указателей, особенно boost :: shared_ptr, стоит дороже по сравнению с голыми указателями с точки зрения времени и памяти? Лучше ли использовать голые указатели для требовательных к производительности частей игровых / встроенных систем? Вы бы порекомендовали использовать голые указатели или интеллектуальные указатели для компонентов, требующих высокой производительности?





Boost предоставляет различные интеллектуальные указатели. Как правило, как занятость памяти, которая зависит от типа интеллектуального указателя, так и производительность не должны быть проблемой. Для сравнения производительности вы можете проверить это http://www.boost.org/doc/libs/1_37_0/libs/smart_ptr/smarttests.htm.
Как видите, при сравнении производительности учитываются только построение, копирование и уничтожение, что означает, что разыменование интеллектуального указателя предположительно имеет те же затраты, что и необработанный указатель.
Следующий фрагмент демонстрирует отсутствие потери производительности при использовании shared_ptr<> вместо необработанного указателя:
#include <iostream>
#include <tr1/memory>
int main()
{
#ifdef USE_SHARED_PTR
std::tr1::shared_ptr<volatile int> i(new int(1));
#else
volatile int * i = new int(1);
#endif
long long int h = 0;
for(long long int j=0;j < 10000000000LL; j++)
{
h += *i;
}
std::cout << h << std::endl;
return 0;
}
Разыменование интеллектуальных указателей обычно тривиально, особенно для ускорения в режиме выпуска. Все проверки ускорения выполняются во время компиляции. (Умные указатели теоретически могут делать умные вещи в потоках). Остается еще много других операций. Никола упомянул строительство, копирование и разрушение. Но это далеко не полный комплект. Другие важные операции - это замена, присвоение и сброс в NULL. В основном любая операция, требующая смекалки.
Обратите внимание, что некоторые из этих операций исключаются некоторыми интеллектуальными указателями. Например. boost::scoped_ptr нельзя даже скопировать, не говоря уже о назначении. Поскольку остается меньше операций, реализация может быть оптимизирована для этого меньшего количества методов.
Фактически, с приближением TR1 вполне вероятно, что компиляторы лучше справятся с интеллектуальными указателями, чем с необработанными указателями. Например. возможно, что компилятор может доказать, что интеллектуальный некопируемый указатель не имеет псевдонима в некоторых ситуациях просто потому, что он не копируемый. Подумайте об этом: псевдонимы возникают, когда создаются два указателя, указывающие на один и тот же объект. Если первый указатель не может быть скопирован, как второй указатель будет указывать на тот же объект? (Есть способы обойти это тоже - оператор * должен возвращать lvalue)
Я согласен с Крисом Джефферсоном. никто не мешает вам сохранить его в другом месте, прежде чем поместить его в интеллектуальный указатель
Я написал небольшой ответ по поводу алиасинга здесь: stackoverflow.com/questions/270408/…
Вы действительно могли инициализировать scoped_ptr из необработанного указателя, и в этом случае компилятор не смог оптимизировать. Я хотел сказать, что это исключение. Компилятору необходимо исключить эти необычные случаи при оптимизации, так же как он должен исключить случаи & * MySmartPtr.
Умные указатели с подсчетом ссылок (наиболее распространенный тип) стоят дороже только при их копировании, создании и удалении. Эти дополнительные затраты могут быть существенными, если вы много копируете, потому что большинство из них являются потокобезопасными.
Если вам просто нужен указатель на "автоудаление", есть много злонамеренного auto_ptr или новый и блестящий (но пока не очень поддерживаемый) unique_ptr из C++ 0x.
Когда я последний раз тестировал VC6, компилятор не смог оптимизировать код с помощью интеллектуального указателя так же хорошо, как с помощью необработанного указателя. С тех пор все могло измениться.
Когда я последний раз тестировал старую версию boost (кажется, 1.34) с VC6, компилятор оптимизировал атомарное приращение refcount для weak_ptr. Это значительно ускорило работу, хотя и вызвало довольно много сбоев в многопоточных библиотеках.
Я понимаю, что VC6 по-прежнему активно используется в крупных проектах, которые нелегко переключить, но давайте будем честными: он не был зрелым компилятором до VC2003, второго выпуска в жизненном цикле продукта VS.NET.
Единственный способ справиться с проблемами производительности - профилировать код. В любом случае большая часть проблем с производительностью воображается; только профилирование укажет вам, в чем заключаются ваши узкие места.
Если окажется, что использование интеллектуальных указателей создает узкое место там, где необработанные указатели - нет, используйте необработанные указатели! А до тех пор я бы особо не беспокоился об этом; большинство операций с интеллектуальными указателями выполняется достаточно быстро. Вы, вероятно, слишком часто сравниваете строки (или что-то в этом роде), чтобы они имели значение.
Часто упускается из виду промежуточный вариант между std::vector<T*>, управляемым «вручную» (то есть необработанными указателями) и std::vector<boost::shared_ptr<T> >, в форме классов boost::ptr_container.
Они сочетают в себе производительность контейнера необработанных указателей с удобством контейнера интеллектуальных указателей (т. Е. Они обеспечивают функциональность, которую люди хотели бы предоставить в контейнерах STL std::auto_ptr, если бы это сработало).
К сожалению, ваша идея оптимизированных интеллектуальных указателей не сработает, по крайней мере, в C++. Вы могли сохранить указатель в другом месте, прежде чем поместить его в интеллектуальный указатель. Кроме того, легко (но не рекомендуется) получить необработанный указатель C обратно из интеллектуального указателя, выполнив & * smart_ptr;