Общие указатели и производительность - PullRequest
8 голосов
/ 12 октября 2009

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

Моя программа многопоточная, с использованием std :: tr1 :: shared_ptr

Ответы [ 7 ]

13 голосов
/ 13 октября 2009

Если ваше приложение передает около 700 байтовых XML-сообщений, которые могут содержаться в 65-байтовых сообщениях протокола Google или 85-байтовых сообщениях ASN.1, то это, вероятно, не имеет значения. Но если он обрабатывает миллион штук в секунду, я бы не отказался от затрат на добавление 2 циклов полной записи с изменением чтения (RMW) к передаче указателя.

Полная запись с изменяемым чтением имеет порядок 50 нс, поэтому два - 100 нс. Эта стоимость равна стоимости Lock-Inc и Lock-Dec - так же, как 2 CAS. Это половина резервирования и выпуска критической секции Windows. Это сравнивается с одним нажатием одного цикла машины (400 секунд PICO на машине с частотой 2,5 ГГц)

И это даже не включает другие затраты на аннулирование строки кэша, которая фактически содержит счетчик, влияние блокировки BUS на другие процессоры и т. Д. И т. Д.

Передача умных указателей по константной ссылке почти ВСЕГДА предпочтительнее. Если вызываемый объект не создает новый общий указатель, когда он хочет гарантировать или контролировать время жизни получателя , тогда это ошибка в вызываемом . Для того, чтобы спокойно и осторожно передавать поток, подсчитывая умные указатели по значению, нужно просто повысить производительность.

Использование указателей с подсчетом ссылок, без сомнения, упрощает время жизни, но передача общих указателей по значению, чтобы попытаться защитить от дефектов в вызываемом объекте, - полная и полная ерунда.

Чрезмерное использование подсчета ссылок может за короткое время превратить программу svelte, которая может обрабатывать 1 мм сообщений в секунду (mps), в толстую, которая обрабатывает 150k mps на том же оборудовании. Внезапно вам понадобится половина стойки серверов и 10000 долларов в год на электроэнергию.

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

Пример простого улучшения - скажем, если вы собираетесь разветвлять объект, и вы знаете ширину приращения разветвления (скажем, n) на n, а не индивидуальное приращение при каждом разветвлении.

Кстати, когда процессор видит префикс блокировки, он действительно говорит: «О, нет, это повредит».

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

10 голосов
/ 12 октября 2009

Практически невозможно правильно ответить на этот вопрос, учитывая данные. Единственный способ по-настоящему сказать, что вызывает проблемы с производительностью в вашем приложении, - запустить в программе профилировщик и проверить вывод.

При этом очень маловероятно, что shared_ptr вызывает замедление. Тип shared_ptr и многие ранние доморощенные варианты используются во все возрастающем количестве программ на C ++. Я сам использую их в своей работе (профессионал дома). Я потратил много времени на профилирование своих рабочих приложений, и shared_ptr даже близко не сталкивался с проблемой в моем коде или любом другом коде, работающем в приложении. Гораздо более вероятно, что ошибка в другом месте.

5 голосов
/ 13 октября 2009

Если ваша программа, похоже, имеет проблему с производительностью, вполне естественно начать угадывать, в чем может быть проблема, но если вы хотите сделать ставку, она почти на 100% может быть чем-то совершенно другим. Профилирование может найти проблему. Это метод, который я использую.

4 голосов
/ 12 октября 2009

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

Редактировать: Для тех, кому небезразлично, насколько медленная блокировка потоков может выполнять довольно простые операции, см. Тестирование Херба Саттера с несколькими реализациями строк CoW . Хотя его тестирование далеко от совершенства (например, он тестировал только на Windows), оно все же дает некоторое представление о том, какое замедление вы можете ожидать. В большинстве практических целей вы можете / могли бы думать о строке CoW как о чем-то вроде shared_ptr<charT>, с добавлением множества (не относящихся к делу) функций-членов.

3 голосов
/ 12 октября 2009

Очень маловероятно - вам придется тратить большую часть своего времени на передачу указателей.

Эффект общих ptrs, как правило, незначителен, и трудно даже создать граничный случай, когда они становятся проблемой (при условии правильной реализации и должным образом оптимизирующего компилятора).

Влияние общего ресурса:

  • увеличен размер выделения.
    Это будет иметь значение, только если у вас много общих указателей на очень маленькие объекты (скажем, десятки миллионов shared_ptr<int>) и / или вы работаете близко к пределу памяти. Существует небольшой потенциал для заметного снижения производительности, если дополнительные выделения превышают уровень кэша / NUMA во внутреннем цикле

  • увеличение количества выделений
    shared_ptr все покрытия объекта отслеживания (счетчик ссылок, слабый счетчик и средство удаления). Это оказывает давление на кучу и может вызвать общее замедление, если у вас большое общее количество выделений и освобождений.
    Этого можно избежать, используя make_shared, помещая референт и объект trackng в одно выделение

  • подсчет ссылок
    увеличивает стоимость копии указателя. В однопоточном приложении вы заметите, что в любом случае только вы тратите большую часть своего времени на копирование указателей. В многопоточных приложениях вам все равно потребуется высокий уровень конкуренции по тем же указателям.
    Во многих местах можно избежать стоимости копирования, передав shared_ptr<T> const &, например. в качестве аргумента функции.

  • разыменования
    Дополнительная стоимость разыменования равна нулю в сборке релиза оптимизирующего компилятора. Сборки отладки часто приравниваются к вызовам функций и дополнительным проверкам NULL. Тем не менее, особенно в отладочных сборках, вам придется тратить большую часть времени на разыменование указателей, чтобы это изменило ситуацию.


Без дополнительной информации мы не сможем вам помочь. Вам необходимо описать, что такое «проблемы с производительностью» (общая медлительность, некоторые операции, которые занимают много времени, много подкачки), и некоторые ключевые показатели - что делает ваше приложение, сколько существует умных указателей, как часто они копируются и какие другие операции вы выполняете помимо манипулирования умными указателями.

Или вы научитесь использовать монитор производительности и / или профилировщик, чтобы выяснить, что вызывает замедление и есть ли определенные узкие места.

0 голосов
/ 13 октября 2009

Не догадывайтесь о производительности: Профиль Ваш код.

0 голосов
/ 13 октября 2009

Единственное, что может снизить производительность, - это чрезмерная передача shared_ptr в качестве параметров функции. Решением для этого будет передача ссылок на shared_ptr. Однако это микрооптимизация, поэтому делайте это только тогда, когда это действительно необходимо

редактирование: Если подумать об этом, есть лучшие способы оптимизации:

  • При чрезмерной передаче указателя вы, вероятно, должны позволить объекту что-то делать, а не перетаскивать его.
  • Вы можете передать (постоянную) ссылку на объект вместо указателя
  • передать ссылку на указатель, когда указатель необходимо изменить
...