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

Если производительность шаред мать его птра вдруг становится проблемой, эту проблему надо искать в другом месте

эти золотые слова можно сказать о чем угодно.

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

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

Или проблема в том что добавляется еще один класс? Ну это понятно, но кода он генерирует за собой много.

Учитывая все это, непонятно в чем суть комментария.

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

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

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

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

тут сразу вопрос.

  1. а чем «код без классов», отличается от кода с классами???

И еще: там есть класс - Data в обоих случаях. если вам так нужны «классы».

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

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

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

Метод класса может вообще содержать минимум кода, и инлайниться в одну асмовую команду.

Вы книжки-то читайте там уже

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

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

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

из этого делаются некие выводы. а вы рассказываете про какие-то «классы».

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

А во что превращяется шаблон после подстановки типа? :)

shared_ptr - это шаблон. во что бы он ни превращался при актуализации.

Ну вообще то shared_ptr это обычный класс. :)

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

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

Какая глупость. :)

Шаблон:

template<typename T>
class Boo
{
  T value;
}

после подстановки типа

Boo<int>

превращяется в обычный класс:

class Boo_int
{
  int value;
}

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

Вот для shared_ptr компилятор сгененрировал все его методы для типа Data

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

у вас смешались в голове классы и шаблоны.

читайте определение.

https://en.cppreference.com/w/cpp/language/templates

A template is a C++ entity that defines one of the following:

    a family of classes (class template), which may be nested classes 

Разницу между классом и «семьей классов» ощущаем? Ощущаем разницу между петей ивановым и родом ивановых?

alysnix ★★★
() автор топика
struct Data(usize);
impl Data{
 pub fn print(&self){
    println!("val is: {}",self.0);
 }
}
fn test(key: &u8){
    if key ==&1{
    let xx = Box::new(Data(777));
    xx.print();
}else{
    let xx =Data(888);
    xx.print();
}
}
pub fn main(){
    test(&1);
    test(&0);

}

прошло через оптимизатор

example::main:
        sub     rsp, 24
        mov     edi, 1
        call    example::test
        xor     edi, edi
        call    example::test
        add     rsp, 24
        ret

example::test:
        sub     rsp, 24
        cmp     dil, 1
        jne     .LBB1_2
        mov     edi, 777
        call    <example::Data as example::print>::print
        jmp     .LBB1_3
.LBB1_2:
        mov     edi, 888
        call    <example::Data as example::print>::print
.LBB1_3:
        add     rsp, 24
        ret

<example::Data as example::print>::print:
        sub     rsp, 24
        mov     esi, edi
        lea     rdi, [rip + .L__unnamed_1]
        mov     edx, 1
        call    qword ptr [rip + std::io::stdio::_print@GOTPCREL]
        add     rsp, 24
        ret

.L__unnamed_1:
        .asciz  "\nval is: "

без

section .text

example::main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     byte ptr [rbp - 1], 1
        lea     rdi, [rbp - 1]
        call    example::test
        mov     byte ptr [rbp - 2], 0
        lea     rdi, [rbp - 2]
        call    example::test
        add     rsp, 16
        pop     rbp
        ret

example::test:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 48
        mov     qword ptr [rbp - 8], rdi
        mov     rax, qword ptr [rbp - 8]
        mov     al, byte ptr [rax]
        cmp     al, 1
        jne     .LBB1_2
        mov     edi, 4
        call    __rust_alloc
        mov     rcx, rax
        mov     qword ptr [rbp - 24], rcx    ; Box allocation
        mov     dword ptr [rcx], 777          ; Store Data(777)
        mov     rax, qword ptr [rbp - 24]
        mov     qword ptr [rbp - 16], rax     ; xx = Box
        mov     rdi, qword ptr [rbp - 16]
        call    example::Data::print
        mov     rdi, qword ptr [rbp - 16]
        call    <alloc::boxed::Box<example::Data> as core::ops::drop::Drop>::drop
        jmp     .LBB1_3
.LBB1_2:
        mov     dword ptr [rbp - 28], 888     ; Data(888) on stack
        lea     rdi, [rbp - 28]
        call    example::Data::print
.LBB1_3:
        add     rsp, 48
        pop     rbp
        ret

example::Data::print:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     qword ptr [rbp - 8], rdi
        mov     rax, qword ptr [rbp - 8]
        mov     eax, dword ptr [rax]
        mov     esi, eax
        lea     rdi, [rip + .L__unnamed_1]
        mov     al, 0
        call    printf
        add     rsp, 16
        pop     rbp
        ret

<alloc::boxed::Box<example::Data> as core::ops::drop::Drop>::drop:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     qword ptr [rbp - 8], rdi
        mov     rax, qword ptr [rbp - 8]
        mov     rdi, rax
        mov     esi, 4
        mov     edx, 4
        call    __rust_dealloc
        add     rsp, 16
        pop     rbp
        ret

.L__unnamed_1:
        .asciz  "val is: %d\n"
Silerus ★★★★
()
Последнее исправление: Silerus (всего исправлений: 1)

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

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

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

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

все видят, але, что там лишний класс, но это вообще не говорит ничего о вносимом оверхеде.

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

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

По-моему, ты вообще не разобрался в своей же асм портянке, все эти асм команды, которые видишь, это библиотечные функции, которые были вставлены, часть из них вообще похожи на какие-то заглушки. Давай поглядим на main():

main:
.LFB2644:
	.cfi_startproc
	pushq	%rbx
	.cfi_def_cfa_offset 16
	.cfi_offset 3, -16
	movl	$24, %edi
	call	_Znwm@PLT
	movl	$777, %edx
	leaq	.LC1(%rip), %rsi
	leaq	.LC2(%rip), %rdi
	movq	%rax, %rbx
	movq	.LC0(%rip), %rax
	movl	$777, 16(%rbx)
	movq	%rax, 8(%rbx)
	leaq	16+_ZTVSt23_Sp_counted_ptr_inplaceI4DataSaIvELN9__gnu_cxx12_Lock_policyE2EE(%rip), %rax
	movq	%rax, (%rbx)
	xorl	%eax, %eax
	call	printf@PLT
	movq	%rbx, %rdi
	call	_ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE10_M_releaseEv
	xorl	%eax, %eax
	popq	%rbx
	.cfi_def_cfa_offset 8
	ret

Код раздулся всего на несколько инструкций

код растет пропорционально количеству использований этого дела

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

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

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

да понял я все. он просто инстанцировал шаблон и загнал его в мой код.

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

если шаблон инстанцировать где нить вовне, такого ужаса не будет

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

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

Это потроха shared_ptr, которые сгенерировал компилятор для

 shared_ptr<Data>

Но это все бессмысленно объяснять, тема прямо пробивающий дно тупняк. :)

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

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

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

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

Ну вроде дошел уже до дна, но нет… :) Ты включил -O2 и удивляешься что компилятор провел оптимизации? Включи -O0 и ты увидишь, что было до них, скорее всего ты удивишься еще больше. :)

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

Включи -O0 и ты увидишь, что было до них, скорее всего ты удивишься еще больше. :)

ты еще и про ключи оптимизции знаешь? ты настолько знаешь, что даже удивлен, откуда это знают другие?

отлистай назад, я это уж писал много раз.

alysnix ★★★
() автор топика
Ответ на: комментарий от kvpfs_2
$ c++filt _Znwm
operator new(unsigned long)

$ c++filt _ZTVSt23_Sp_counted_ptr_inplaceI4DataSaIvELN9__gnu_cxx12_Lock_policyE2EE
vtable for std::_Sp_counted_ptr_inplace<Data, std::allocator<void>, (__gnu_cxx::_Lock_policy)2>

$ c++filt _ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE10_M_releaseEv
std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release()
anonymous
()
Ответ на: комментарий от alysnix

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

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

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

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

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

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

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

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

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

Замени make_shared на make_unique и получишь абсолютно тот же результат, что и с удалением вручную. Если ты не в состоянии понять что такое shared_ptr и для чего он нужен, то тогда тебе действительно не стоит пользоваться shared_ptr’ом. Видимо слишком сложная технология.

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

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

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

а оверхед там от того, что компилятор инстанцировал темплейт shared_ptr для Data, и вставил этот код в обьектник.

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

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

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

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

отому что смотрел оверхед от единственного использвания shared_ptr

И что ты там насмотрел? Что оверхед есть? Что атомарные переменные это дорого?
Хочешь увидеть цену создания/удаления шареда - так и напиши создание пустого шареда: https://godbolt.org/z/GM361srTq

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

и мне пришлось на ответ тратить свое время

Сделал абсолютно идиотское сравнение и получил невероятное открытие, до которого никто не мог догадаться - теперь терпи.

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

И что ты там насмотрел? Что оверхед есть? Что атомарные переменные это дорого?

атомарные переменные скалярных типов - это дешево(это одна команда). относительно дорого - атомарные переменные длинных структурных типов. но вряд ли они есть в шаред_ptr.

так и напиши создание пустого шареда: https://godbolt.org/z/GM361srTq

кого интересуют твои «пустые» shared. там вопрос вообще не в классе, на который shared указывает. а в инстанцированнии темплейтов как shared_ptr, так и make_shared. без разницы на какой класс.

по коду ты практически ничего не поменял со своими пустыми shared.

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

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

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

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

https://en.cppreference.com/w/cpp/memory/shared_ptr#Implementation_notes

In a typical implementation, shared_ptr holds only two pointers:

  • the stored pointer (one returned by get());
  • a pointer to control block.
utf8nowhere ★★★★
()
Ответ на: комментарий от alysnix

атомарные переменные скалярных типов - это дешево(это одна команда).

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

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

атомарные переменные скалярных типов - это дешево(это одна команда).

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

Одна инструкция будет ОЧЕНЬ дорогой, и это как раз случай атомарных переменных, потому что при этом лочится шина. Ты видимо никогда не видел в perf’е одну красную строчку lock cmpxchg в неправильно выбранного аллокаторе из-за которой новая 256 ядерная тачка работает как третий пень (literally, если что). Кроме того, часть стоимости атомарных переменных находится ВНЕ них, потому что их использование (в зависимости от выбранных барьеров, конечно) приводит к переупорядочиванию инструкций вокруг них с потерей возможностей для оптимизации. И ещё есть cache aliasing, потому что лочится не переменная, а линейка кэша, и если ты в неё засунешь несколько переменных, они будут тормозить друг друга.

А в другом случае несколько страниц инструкций будут бесплатными, потому что на практике это замечательно предиктящийся бранч и jump через все эти страницы, нужные на самом деле для всяких corner case встречающихся один раз за время жизни указателя или вообще никогда.

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

Стоят, ещё как стоят. Не говори о чём не знаешь.

anonymous
()