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)
Ответ на: комментарий от anonymous

Ты видимо никогда не видел в perf’е одну красную строчку lock cmpxchg в неправильно выбранного аллокаторе из-за которой новая 256 ядерная тачка работает как третий пень (literally, если что)

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

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

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

разговор был об оверхеде от shared_ptr. и этот оверхед не имеет прямого отношения к атомикам(он будет и без атомиков там)

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

Так-то да, но в контексте теста это был самый простой и сравнимый с ручным управлением способ пихнуть что-нибудь в кучу, но можно как и у ТС сделать, раскидываясь сыроуказателями и ничего не проверяя: https://godbolt.org/z/P59ca1Mxq , разница только в отсутствии проверки успешности аллокации.

Arc<RefCell> тоже довольно бесполезная конструкция, но она ближе всего по свойствам в том числе и errorpron-овости к shared_ptr ( но даже она не допустит UB), а обычно используемые Arc<Mutex>, Arc<RwLock> уже довольно далеко ушли от shared_ptr

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

В смысле такой же тяжёлый и с возможностью UB? Ну да, раст гибкий системный язык, соответсвенно, сделать плохо тоже позволяет

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

Так зачем такой Arc нужен, если он тяжелый и с возможностью UB? :)

Arc это обычный класс и я могу написать свой MyArc и использовать вместо стандартного? Или он как-то поддерживается компилятором?

По аналогии с std::unique_ptr, плюсовый компилятор может выкинуть класс и свести это к new|delete.

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

и подсчёт ссылок потокобезопасный. Т.е. создать референс в другом потоке - безопасно.

Для подсчёта ссылок без потокобезопасности только ручками или в boost.

В любом случае, код, который считает счётчики - это тоже код. Для new/delete его не будет.

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

в общем блоке сидит не указатель на объект, а сам объект!

std::make_shared именно так и делает: выделяется общий блок на данные и контролльный + выравнивание. Но ты покажи ассемблер. Потому как даже в таком подходе улучшается дело в плане дружественности к кешу: данные все рядом, но разыменования всё равно будет два:

mem_ptr -> [control_block,(alignment),object]

что бы обратиться к контрольному блоку, нужно косвенно перейти по mem_ptr, прочитать счётчики. Что бы перейти к данным, нужно косвенно перейти mem_ptr + sizeof(control_block) + alignment, что бы получить доступ к объекту.

С другой стороны: доступ к контрольному блоку нужен только при копировании или разрушении объекта умного указателя: одно разыменование. Что бы прочитать счётчики.

При разрушении объекта всегда будет два разыменования: сначала к контрольному блоку, потом к объекту: что бы передать его в деструктор. А вот память освобождается уже одной операцией. Если в Rust нет аналога деструктора (некой сущности, которая может вызываться для чистки ресурсов и т.п.), то да, в Rust при разрешении данных нет нужды во втором разыменовании, можно сразу освобождать блок памяти. Но так ли это?

При уже созданном объекте нет нужды обращаться к контрольному блоку: одно разыменование.

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

Если в Rust нет аналога деструктора

Все там есть и работает аналогично С++. Вообще std::sync::Arc устроен ровно также как std::shared_ptr из C++ созданный с помощью std::make_shared за исключением одного лишнего указателя на данные (в Arc он не нужен так как нет варианта что данные не в одном куске с блоком счетчиков). В общем раст вариант на размер одного указателя меньше, во всем остальном абсолютно одинаково.

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

Все-таки чуть соврал тут std::shared_ptr и оверхеды (комментарий)

В C++ std::shared_ptr это структура из двух указателей первая на блок счетчиков, вторая на данные. В случае std::make_shared просто эти два указателя на один и тот же блок памяти с разным смещением.

В rust std::sync::Arc это структура только из одного указателя на область памяти в которой идет сначала блок счетчиков и потом сразу данные.

В результате количество разыменований при обращении к данным и в rust и в C++ одинаково и ровно одно, но памяти на каждый умный указатель тратится чуть меньше в раст.

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

Все-таки чуть соврал тут

не соврал,а вариант реализации считай. врать - это глагол, говорящий о намеренном введении в заблуждение.

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

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