LINUX.ORG.RU

std::shared_ptr и оверхеды

 ,


0

3

решил проверить оверхеднутость shared_ptr, и набросал минимальный тестик

https://godbolt.org/z/dzEv1r6s1

там в функции test менейте #ifdef с нолика не единичку, переключая варианты.

с ручным управлением получаем (нолик в ifdef) - 9 строк асма всего.

с прогрессивно автоматическим получаем (1 в ifdef) - число строк не поддается подсчету.

в того кто скажет, что там должен быть unique_ptr, бросьте камень.

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

живите теперь с этим.

к дорогим растафанам будет просьба - напишите это же на русте, чисто сравнить кодик и оверхеды.

★★★

Последнее исправление: alysnix (всего исправлений: 3)
Ответ на: комментарий от Werenter

Так shared это вроде для многопотока

нет. shared - это подсчет ссылок, и удаления, когда счетчик равен нулю. это когда клиент не знает - где ставить delete. как в обычном коде, там и многопотоке(где все сложней).

alysnix ★★★
() автор топика

Наброшу немного. Тормозит не только сам std::shared_ptr, но и сама аллокация по сравнению с той же жабой. Ну, там jemalloc/tcmalloc/mimalloc немного помогают, но все же тормозят…

А теперь о серьезном. Сколько косвенных адресаций в std::shared_ptr? Сдается мне, что две. А заглядывать в этот, простите, не самый красивый код в stdlib не охота.

Для примера в растовском Rc сидит всего одна (!!) косвенная адресация. Да и еще нет атомиков.

Что получается? Раст выигрывает даже на меньшем уровне косвенности у плюсов. А если код однопоточный, где можно использовать Rc вместо Arc, то раст выигрывает у плюсов еще и за счет того, что нет атомиков.

В общем, набросил

anonymous
()
Ответ на: комментарий от anonymous

Тормозит не только сам std::shared_ptr

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

А теперь о серьезном. Сколько косвенных адресаций в std::shared_ptr? Сдается мне, что две. А заглядывать в этот, простите, не самый красивый код в stdlib не охота.

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

Что получается? Раст выигрывает даже на меньшем уровне косвенности у плюсов. А если код однопоточный, где можно использовать Rc вместо Arc, то раст выигрывает у плюсов еще и за счет того, что нет атомиков.

вот и прошу написать то же самое на расте. чтобы один раз увидеть.

у нас в деревне никто раста не знает, потому сам не можу.

alysnix ★★★
() автор топика
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

shared - это подсчет ссылок, и удаления, когда счетчик равен нулю. это когда клиент не знает - где ставить delete. как в обычном коде, там и многопотоке(где все сложней).

Поэтому правильнее использовать интрузивные счётчики. С ними можно выбирать, где нужен обычный инт, а где атомарный, плюс оверхеда при передаче самих «умных» указателей нет по сравнению с обычными

annulen ★★★★★
()
Ответ на: комментарий от annulen

Типичные оптимизации вроде инлайнов

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

оптимизация тут по размеру(-Os) мало что дает. визуально длина кода не меняется. где у них счетчик асм строк,чтобы посмотреть - не понятно, как то все не очень там интуитивно.

несколько сокращает код запрет исключений. ну там на процентов <10 становится меньше. все равно это очень много. на код без смартов запрет исключений не влияет никак.

alysnix ★★★
() автор топика
Ответ на: комментарий от annulen

Поэтому правильнее использовать интрузивные счётчики. С ними можно выбирать, где нужен обычный инт, а где атомарный, плюс оверхеда при передаче самих «умных» указателей нет по сравнению с обычными

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

alysnix ★★★
() автор топика
Ответ на: комментарий от alysnix

одна. сам shared_ptr

А ты в этом уверен, что у shared_ptr в C++ ровно одна косвенная адресация? Как ты считал?

Раз - мы от объекта умного указателя переходим в общий блок. Два - мы в общем блоке достаем реальный объект. Получается, что два.

В код заглянул - написано непонятно. Точно неохота разбираться.


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

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

Понимаешь?

anonymous
()

shared_ptr - это:

  • указатель на объект,
  • указатель allocator,
  • указатель deleter,
  • счетчик,
  • слабый счетчик.

И все это надо атомарно использовать.

anonymous
()
Ответ на: комментарий от annulen

это перебор. это в мусоросборке так делают.

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

но и это не спасает. ссылки надо считать, и без разницы где они лежат.

alysnix ★★★
() автор топика
Ответ на: комментарий от alysnix

ссылки надо считать, и без разницы где они лежат.

А выбор тут не велик. Если для объекта требуется расшаренное владение, то нужен либо подсчёт ссылок, либо GC, и у каждого стула свои недостатки.

annulen ★★★★★
()
Ответ на: комментарий от anonymous

Раз - мы от объекта умного указателя переходим в общий блок. Два - мы в общем блоке достаем реальный объект. Получается, что два.

без смарта 
p->_x = 100;

со смартом
smart_p->_phys_p->_x = 100;

одна косвенность, чувак.

alysnix ★★★
() автор топика
Ответ на: комментарий от annulen

А выбор тут не велик.

ну это понятно. вопрос в эффективной реализации.

опять же все «продвинутые» орут в голос об использовании смартов в с++. когда надо и когда не надо.

но если посмотреть в код, становится страшно.

alysnix ★★★
() автор топика
Ответ на: комментарий от alysnix

Так у тебя со смартом для С++ в твоем же примере две косвенных адресации! Я же утверждаю, что в расте здесь будет одна!

Или мы просто недопоняли друг друга? Мы же ведь про оверхед умных указателей толкуем? Вот я и говорю, что у умного указателя в плюсах, скорее всего, 2 косвенных адресации, как у тебя самого в примере

anonymous
()

Писец, вы тут чего, пьяные что ли? Один в цикле что ли создаёт шаред_птры, другой косвенные адресации подсчитывает. Бесполезная и бессмысленная дрочка на производительность, которой не будет в реальности. Только лишь для потрепаться о синтетическом тесте в вакууме.

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

anonymous
()
Ответ на: комментарий от alysnix

Ну, ладно! Я чувствую, что придем к соглашению. Тогда по твоей схеме в расте, вообще, не будет косвенных адресаций! Понимаешь?!

Ну, а так-то любое обращение по адресу - это косвенная адресация. Но это уже терминологический вопрос. Опустим его.

Итого, по твоему методу подсчета получается, что у std::shared_ptr в С++ будет 1 косвенная адресация, а у раста - 0. Просто нуль!

Нет причин для оверхеда

anonymous
()
Ответ на: комментарий от anonymous

просто закинь растовый вариант. что гадать - как оно в расте.

вариант естессно в godbolt.


создается указатель на обьект, сделанный по new. в обьекте есть поле _x. ему приваивается 100.

вариант со счетчиком ссылок и unsafe вариант - ручками делаем, ручками утилизируем.

alysnix ★★★
() автор топика
Ответ на: комментарий от alysnix

А если нет цикла, то и вот и оверхед будет в районе 100500 доли процента в общей произовдительности программы. Никто не будет создавать в цикле шаред_птр, это бредовый кейс. От чего тебе больно? От несколько десятков доп байт в бинарь? Это МК? Растаманы вон вообще всю свою стд в бинарь вшивают

anonymous
()
Ответ на: комментарий от anonymous

От чего тебе больно? От несколько десятков доп байт в бинарь?

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

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

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

alysnix ★★★
() автор топика
Ответ на: комментарий от alysnix

Ну, с чего ты взял, что я гадал на счет раста? Как раз там-то код простой, и читать его легко.

Нужно смотреть реализацию умной ссылки Rc.

Итак, в общий блок дописывается сам объект. Там сидит объект, а не указатель, как было бы в С++. Я же уже выше все написал, что нужно - объект переносится в конец общего блока. Перемещается.

Поэтому и получается, что по твоей методике подсчета у раста ровно 0 косвенных обращений.

Ну, ладно. Пойду займусь другими делами. Мне надоело

anonymous
()

К вопросу о косвенности. ЕМНИП, объект shared_ptr держит два указателя: на контрольный блок и на сами данные. Как раз, чтобы сразу ходить в объект, а не через промежуточный шаг с подглядыванием в контрольный блок.

ox55ff ★★★★★
()
Ответ на: комментарий от anonymous

Поэтому и получается, что по твоей методике подсчета у раста ровно 0 косвенных обращений.

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

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

страшен сам кодовый наворот, что будет исполняться кроме этой лишней косвенности.

alysnix ★★★
() автор топика
Ответ на: комментарий от alysnix

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

Эффект низкой базы. Ну вот сколько ты раз в программе создаш шаред_птр? Пусть это будет очень жирная софтина, и нас там таких случаев 100, на каждый по 20 байт кода, в сумме 2 КБ. Ты серьезно считаешь это проблемой? Это даже для львиной доли современных МК - смешно (а большинство прошивок МК - сильно проще, и таких количества там не будет)

anonymous
()
Ответ на: комментарий от anonymous

Ну вот сколько ты раз в программе создаш шаред_птр?

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

alysnix ★★★
() автор топика

нифига там не 9 строчек, с «ручным управлением» компилятор аллокацию в куче похоже выбросил, а то что есть подготовка к принтф-у, переделывай тест на более честный

zurg
()
Последнее исправление: zurg (всего исправлений: 1)
Ответ на: комментарий от alysnix

Для МК вполне разумный подход. Но не потому, что бинарь не влезет, а просто потому что прошивка с большой вероятностью относительно проста, и тащить туда весь этот высокоуровневый сахар - лишнее в большинстве случаев

anonymous
()
Ответ на: комментарий от ox55ff

кстати да. указатель на данные лежит прям в поинтере. так что лишней косвенности нет. вот только копировать придется 16 байт вместо 8.

от есть передача его по значению в функцию и всякие там присваивания растут вдвое по времени.

alysnix ★★★
() автор топика

Так это любой цпп код таким становится, как пример:

https://godbolt.org/z/dq8sqb71o

Вместо ifdef можно много окошек навставлять, с разными компиляторами и опциями.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)

Ну и в расте нет прямого аналога shared_ptr, есть пачка комбинаций из Arc<> и друго типа (можно просто Arc если достаточно иммутабельности), и в любом случае они защищают ещё и данные от гонок, а не только счётчик как shared_ptr.

zurg
()
Ответ на: комментарий от zurg

нифига там не 9 строчек, с «ручным управлением» компилятор аллокацию в куче похоже выбросил, а то что есть подготовка к принтф-у, переделывай тест на более честный

согласен, недоглядел.

https://godbolt.org/z/7s5zd79fe

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

ну стало 15 честных команд для ручной аллокации. а для автоматической - по прежнему код немеряный.

alysnix ★★★
() автор топика
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от anonymous

ну вообще-то это не про сам с++, а про реализацию неких библиотечных фич.

это не повод отказываться от плюсов, а повод критически посмотреть на использование таких фич, в такой реализации.

alysnix ★★★
() автор топика