LINUX.ORG.RU

Стоимость std::unique_ptr

 


0

6

Вот что если мне не лень печатать, и везде где по факту наблюдается семантика передачи собственности на указатель я буду лепить std::unique_ptr<X> вместо X*?

Какая стоимость такого подхода по скорости выполнения например в clang++ на x86_64 если гонять это миллионы раз? Там же еще deleter торчит, говорят, но вроде если он дефолтный, то компилятор его может выкосить и останется обычный указатель

Assembly вроде полистал, раньше хорошо понимал что генерировал g++, но вроде мои времена прошли на этом поприще.

★★★★★

Думал поставить тег rust, но передумал

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

Думаю такая же, как и просто указателя, если ты не собираешься его удалять сам. Вот с delete все сложнее. Я не знаю, как там он обрабатывается, неужели через просто delete?

namezys ★★★★
()

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

Думал поставить тег rust, но передумал

Да, в Ржавчине же оптимизирует :)

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

Как я понял, может ошибаюсь, он таскает за собой deleter, который в дефолтном поведении дергает delete. Но можна воткнуть какой-то close(fd) при желании. Так вот, есть инфа из StackOverflow что по идее он при дефолтном deleter имеет специализацию, которая компилятором вытирается в чистый указатель совсем

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

Ржавчина - язык программирования unique_ptr :) Я хотел о плюсах узнать. И не хотел чтобы набежало фанатов ржавчины

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

std::unique_ptr <X> в недрах всё равно указатель держит. Ну и делетер по сути тот же std::function не? Так что будет у тебя в аргументах вместо одного указателя - объект размером в один указатель или два если с делетером. Что-то то в этом роде. Т.е. вопрос из разряда какой вызов (без собственно выполнения) быстрее. Этот

negate(int);
или этот
add(int, int);

Можно перефразировать. Какая операция быстрее - прибавить с sp один или прибавить к sp два.

Хотя наверное с move() могут быть ньюансы.

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

Deleter — это аргумент шаблона. Если разработчики хоть немного адекватны, экземпляр делитера будет создаваться в виде локальной переменной деструктора unique_ptr, а не таскаться вместе с каждым смарт-поинтером. Хотя не, фигню сказал. Делитер действительно хранится как поле. Но мне кажется, стандартный делитер не имеет какого-либо состояния, так что оверхеда создавать не должен.

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

Да вот вроде спецификация шаблона и не должен таскаться с указателем... Но тред на StackOverflow меня запутал.

http://stackoverflow.com/questions/17777287/could-an-optimizing-compiler-remo...

Тут как будто обсуждают что на него есть ссылка

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

Ну вот и ответ. Если у тебя deleter статический и его методы inline (что верно для шаблонов), то он будет подставлен везде. Далее все деструктуры будут развернуты до delete.

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

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

В SO треде пишут

#include <memory>

template <typename T>
struct deleter {
    char filler;
    void operator()(T* ptr) {}
};

int main() {
    static_assert(sizeof(int*) != sizeof(std::unique_ptr<int, deleter<int>>), "");
    return 0;
}

:(

It's true that smart implementations can detect if D is empty and has a copy/move constructor that doesn't do anything (this is the case of default_delete<T>) and, in such case, avoid the overhead of copying a D. In addition, it can save memory by not adding any extra byte for D.

Но

unique_ptr's destructor must check whether the T* is null or not before calling the deleter. For defalt_delete<T> I believe, the optimizer might eliminate this test since it's OK to delete a null pointer.

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

например в clang++ на x86_64 если гонять это миллионы раз?

Проще проверить. Вангую, что при всём дефолтном оверхэд нулевой.

no-such-file ★★★★★
()
Ответ на: комментарий от vertexua

Я бы для начала обратил внимание на то, каким методом является operator(...) для удаления. Если он статический, то абсолютно точно не надо иметь сам объект D для его выполнения. Если же нет, то уже зависит от самого метода.

Если unique_ptr содержит в себе ссылку или копию объекта D, то без дополнительных затрат памяти тут не обойтись, а так же без дополнительных расходов. Если же включает его композицией, то тогда можно обойтись, ибо компилятор одназначно понимает, сколько ему памяти надо затратить. И operator тоже может сильно оптимизировать.

В общем, судя по всему, зависит от реализации, но можно сделать ее ОЧЕНЬ дешевой.

Проверку на null_ptr убрать нельзя вообще. Ибо она будет или внутри delete, или снаружи (надеюсь повторную проверку внутри delete он пропустит). Кроме того, я не знаю, как срабатывает вызов деструктора объекта. При раскрутке стека он явно должен делать такую проверку. А вот при штатных выходах вполен может местами обойтись.

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

Там проверка на null внутри, по стандарту. Вроде можно дергать delete null сколько угодно.

Ладно, спасибо, наверное не буду париться, по идее можно надеяться на zero cost при дефолтном deleter

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

Я думаю, что если компилятор добавить расходы на проверку для выполнения delete, то оно там будет к место, так как он не сможет статически вычислить, что в этом месте ptr не владеет ни чем.

namezys ★★★★
()

Зависит от реализации. Если повезет, то будет стоить столько же, сколько и обычный указатель (собственно умные указатели и разрабатывались с этой идеей в т.ч.). Кастомный deleter прибавит размер еще одного указателя (тут тоже зависит от реализации std::function).

Все тут уже сказано. В любом случае, спорю, что в проекте (уже) есть более узкие места, чем повсеместное использование unique_ptr :)

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

В любом случае, спорю, что в проекте (уже) есть более узкие места, чем повсеместное использование unique_ptr :)

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

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

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

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

Я бы для начала обратил внимание на то, каким методом является operator(...) для удаления. Если он статический

Операторы уже могут быть статическими?

Если unique_ptr содержит в себе ссылку или копию объекта D, то без дополнительных затрат памяти тут не обойтись

Обойтись, если у default_deleter нет полей и unique_ptr унаследован от него.

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

Кастомный deleter прибавит размер еще одного указателя (тут тоже зависит от реализации std::function).

Это для shared_ptr, а у unique_ptr тип deleter'а не стирается и дополнительной косвенности не требуется.

Begemoth ★★★★★
()

Оно у тебя _уже_ тормозит?

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

Операторы уже могут быть статическими?

Ступил. Признаю. Тогда да, выход один - или создавтаь объект, или наследоваться от него.

Обойтись, если у default_deleter нет полей и unique_ptr унаследован от него.

То есть фактически D не становится членом класса. Может быть я криво выразился. Вообще ничего, кроме как наследвоания для такого поведения я придумать не могу

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

Там используется std::tuple<T*, Deleter>. Внутри tuple как раз устроен через наследование ровно для этого.

vzzo ★★★
()

Я тут конечно типа не шарю в ваших новомодных плюсах, но я не вижу смысла в этих unique_ptr"ах, кроме как эмуляции «бесконечного стека».

Что такое «семантика передачи» я не знаю. Опишите нормально.

Штука в том, что когда время жизни объекта блок и объект не может жить «вечно»/выше блока, то плюсовые аллокаторы тупо заменяются на самопильный стек.

В примитивных случаях с хеадеронли конпелятор unique_ptr свернёт в new/delete.

Но тормаза всяких автоптров не в том, что они тормазнее обычных указателей, а в том, что тормазит плюсовой аллокатор как говно.

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

unique_ptr тормазит своей основой - стандартным аллокатором.

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

Я тут конечно типа не шарю в ваших новомодных плюсах

В этом и причина твоего непонимания.

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

Это чистый шаблон, который при дефолтном делетере должен полностью в генерируемом коде ничем не отличаться от обычного указателя. С другой стороны 1) Ссылка на указатель остается в одном месте. Стоит его передать в другую функцию, так в изначальной функции он пропадает, становится невалидным, символизируя передачу ownership на указатель. 2) Сам unique_ptr передается по значению без проблем, так как инкапсулирует указатель. Как только выполнится последняя функция, которая не передала указатель дальше - он будет удален автоматически. Вызов delete проставит статически в нужном месте компилятор.

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

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

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

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

Свёртывается он не благодаря шаблонам, а благодаря скиллу конпелятора.

Если что-то инлайнится, а твой unique_ptr должен инлайнится, то шаблоны ниначто не влияют. А инлайнится оно благодаря «header-only».

Дефолтный/не дефолтный делитер ниначто не влияет, влияет отсутствие твоей функции в «единице трансляции», когда конпелятор не заинлайнит её, ибо не имеет доступа к её телу.

Стоит его передать в другую функцию, так в изначальной функции он пропадает, становится невалидным, символизируя передачу ownership на указатель. 2) Сам unique_ptr передается по значению без проблем, так как инкапсулирует указатель. Как только выполнится последняя функция, которая не передала указатель дальше - он будет удален автоматически. Вызов delete проставит статически в нужном месте компилятор.

Это семантика стека, и если ты напишешь свой unique_ptr, который будет юзать твой самопальный стек, а не дефолтные операторы new/delete, то твоя байда в дожопы раз быстрее.

Про передачу - это плюсопроблемы, нужные для того, чтобы деструктор не вызывался 2+раза. Вернее он будет, но нужен кастыль, чтобы хотябы он не дёргал delete 2раза.

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

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

Этого не было задумано - это фича конпелятора. Да, гцц свернёт этот unique_ptr с любым делитером.

Я, конечно, не умею писать на ваших плюсах, но вот я наваял говнопортянку: https://gist.github.com/superhackkiller1997/6c25bdf96e914c12be84

Мой unique_ptr с делитером заинлайнился в ноль.

test_stack<string>(MB*100): 2.946521sec//стек - тут 0
test_true<string>(MB*100): 2.995480sec//0,048959 
test_newdel<string>(MB*100): 4.721278sec//1,774757 в 36раз медленней 2-го
test_unique<string>(MB*100): 4.787779sec//1,841258 в 37.5раз медленней 2-го

Заодно научитесь нормально считать, а то я уверен, что плюсовик скажет, что 4-й в 1.6раз медленней 2-го, хотя на самом деле в 36. 2.946521 - это время на вызов конструктора std::string.

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

Стек работает быстрее new/delete - моему удивлению нет предела :D

Я тебе простой пример привел. Реально же часто этими unique_ptr жонглируют между потоками. И всегда остается лишь одна ссылка

Все-же ценность в коде кое-какая есть, видно что unique_ptr чуть-чуть просел по сравнению с new/delete. Даже такая адовая синтетика, как дергать по очереди new/delete в данном случае подходит и показывает что есть какой-то оверхед.

Чем ты его компилял и с какими опциями?

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

Стек работает быстрее new/delete - моему удивлению нет предела :D

Любое говно работает быстрее new/delete.

Я тебе простой пример привел. Реально же часто этими unique_ptr жонглируют между потоками. И всегда остается лишь одна ссылка

Ну оторви руки этому дауну. Нулёвые плюсовики думают, что «жонглирование» этим кусков говна быстрее копирования?

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

Все-же ценность в коде кое-какая есть, видно что unique_ptr чуть-чуть просел по сравнению с new/delete. Даже такая адовая синтетика, как дергать по очереди new/delete в данном случае подходит и показывает что есть какой-то оверхед.

Это не синтетика, как же одалело меня ваше нулёвое балабольство.

Цифры не поменяются в любых юзкейсах, вернее чем сложнее будет юзкейс, тем больше будет тормазить стоковый new. 10нитей, ещё пару аллокаций в цикле и будет уже не 35раз разница, а 200-300-1000раз.

Это почти самый выгодный юзкейс для new, ты можешь заюзать пустой конструктор для std::string() - разница будет ниже, это будет самый дешевый юзкейс.

Чем ты его компилял и с какими опциями?

Ах да, тыж там про шланг говорил, поидее он должен собирать(там вроде нет omp), я юзаю гцц:

g++ -O2 -march=native -std=gnu++11 -lgomp

g++ -O2 -march=native -std=gnu++11 -lgomp -fwhole-program -static -flto - для экстрималов.

А почему медленней:

lock xaddl	%edx, -8(%rax)

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

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

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

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

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

Давай, поясни мне - зачем нужен шланг, какие у него плюсы и прочее. Возможно я чего-то не знаю. А то я оцениваю конпеляторы по фичам/удобству/выхлопу/мощи, а как там вы оцениваете - я не знаю.

Запилю тебе и шланг-версию.

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

Давай, поясни мне - зачем нужен шланг, какие у него плюсы и прочее. Возможно я чего-то не знаю.

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

Расскажи лучше, как флоаты быстро умножать. Ну там, на ассембелере распараллелить, SSE, MMX, все дела.

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

Давай, поясни мне - зачем нужен шланг, какие у него плюсы и прочее. Возможно я чего-то не знаю.

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

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

RAII

Стек изначально RAII, а автоуказатели - кастыль.

безопасной многопоточности.

В чем заключается «безопасность»? Передача указателя? «треадсейв» рефкаунт? Это балавство для калек. Не вижу тут безопасности.

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

Вывод ошибок лучше

В мечтах, чем лучше? Удоскими подчёркиваниями? Я только от них проблювался. Остальное уже 100500 лет есть в гцц.

анализатор можно хоть к IDE прикручивать.

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

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

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

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

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

Остальное уже 100500 лет есть в гцц.

Да вроде как совсем недавно научился. Я за с++ не слежу, если что.

Какой жопой это плюс конпелятора? Это никакого отношения не имеет к конпелятору.

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

quantum-troll ★★★★★
()

Не использовать умные указатели в 21 веке - это нонсенс. За X* надо ломать руки и ноги.

alpha4
()
Ответ на: комментарий от quantum-troll

Да вроде как совсем недавно научился. Я за с++ не слежу, если что.

Я тоже не слежу за с++. Они там появились ещё тогда, когда шлангом пользовались полтора маргинала.

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

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

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

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

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

Вот ты постарайся ответить - нахрен нужен шланг?

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

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

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

Вот ты постарайся ответить - нахрен нужен шланг?

Я так понимаю, это первый из компиляторов в бекендом LLVM, и по мере допиливания clang'а пилят и llvm? LLVM очевидно нужен, он предоставляет годный бекенд для компиляторов и многие языки используют его в этих целях.

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

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

Сам дурак!

alpha4
()
Ответ на: комментарий от quantum-troll

Я так понимаю, это первый из компиляторов в бекендом LLVM, и по мере допиливания clang'а пилят и llvm? LLVM очевидно нужен, он предоставляет годный бекенд для компиляторов и многие языки используют его в этих целях.

Т.е. из себя шланг ничего не представляет - это чисто академическая поделка для развития llvm? Согласен. Но внимание вопрос, какого хрена все кукарекают, - «убица гцц», «полноценный копенлятор» и прочее?

Пилят больше llvm, нежели шланг. Суть не в этом.

Для меня llvm ничего из себя не представляет - это бездарная поделка, абсолютно не нужная. Она даёт возможность всяких рукоизжопым бездарям ваять всякие русты и мильён принципиально новых псевдоязыков, которые будут всегда в говне и нахрен никому не нужны.

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

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

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

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

Сейчас же пацан впадёт в коллективную иллюзию и будет доконца дней своих верить, что он создал убицу сишки, а говно она потому, что llvm ещё молодой и его не допилили.

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

Хотя llvm пилять уже лет 10 и ссылаться на молодость глупо.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.