void f(boost::shared_ptr<X> const& p);
От: Alexander G Украина  
Дата: 29.12.09 09:29
Оценка:

Правило 20: Предпочитайте передачу по ссылке на const передачу по значению

(Скотт Мейерс, Эффективное использование C++
Автор(ы): Скотт Мейерс

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

Стоит ли передавать boost::shared_ptr<X> по константной ссылке?

С одной стороны, подсчёт ссылок может дорого стоить.

но недостатков много:

  • Добавляется косвенность (если ссылки реализованы как указатели).
  • Больше букв писать.
  • const ссылка может создавать иллюзию константности.
  • Оно хорошо только если типы совпадают, иначе — преобразование всё равно нужно.
    class Y : X {};
    void f(boost::shared_ptr<X const> const& p);
    
    boost::shared_ptr<Y const> p1;
    boost::shared_ptr<X> p2;
    f(p1); // временый boost::shared_ptr<X const> создаётся
    f(p2); // временый boost::shared_ptr<X const> создаётся


    Как вообще бороться с оверхедом на подсчёт ссылок и стоит ли?

    Кстати, в Delphi для типов, для которых подсчёт ссылок встроен в компилятор, всё просто и без увеличения уровня косвенности:


    procedure P1(S: string; I: IUnknown);
    begin (* неявное увеличение числа сссылок на S и AddRef для I *)
        
      S := F1(); I := F2(); (* можно *)
    
    end; (* неявное уменьшение числа сссылок на S и Release для I *)
    
    
    procedure P2(const S: string; const I: IUnknown); (* передаются значения указателей, а не указатели на указатели *)
    
    begin (* неявных операций с I и S нет *)
        
      S := F1(); I := F2(); (* нельзя - ошибка компиляции *)
    
    end; (* неявных операций с I и S нет *)
  • Русский военный корабль идёт ко дну!
    Re: void f(boost::shared_ptr<X> const& p);
    От: remark Россия http://www.1024cores.net/
    Дата: 29.12.09 09:59
    Оценка: 12 (1)
    Здравствуйте, Alexander G, Вы писали:

    AG>Стоит ли передавать boost::shared_ptr<X> по константной ссылке?


    AG>С одной стороны, подсчёт ссылок может дорого стоить.


    AG>но недостатков много:


    AG>
  • Добавляется косвенность (если ссылки реализованы как указатели).

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


    AG>
  • Больше букв писать.

    Ну с контейнерами же мы терпим На крайняк можно сделать что-то типа:
    typedef std::string const& str_p;
    struct x {...};
    typedef shared_ptr<x> x_ptr;
    typedef shared_ptr<x> const& x_ptr_p;




    AG>
  • const ссылка может создавать иллюзию константности.
    AG>
  • Оно хорошо только если типы совпадают, иначе — преобразование всё равно нужно.
    AG>
    AG>class Y : X {};
    AG>void f(boost::shared_ptr<X const> const& p);
    
    AG>boost::shared_ptr<Y const> p1;
    AG>boost::shared_ptr<X> p2;
    AG>f(p1); // временый boost::shared_ptr<X const> создаётся
    AG>f(p2); // временый boost::shared_ptr<X const> создаётся
    AG>


    AG>Как вообще бороться с оверхедом на подсчёт ссылок и стоит ли?


    *Может* стоить. Стоит ли в конкретном случае — скажет только профайлер.
    Как бороться — тут есть 2 пути: консервативный и оперативный.
    Консервативный — это убрать лишние операции над счётчиком ссылок (которых может быть великое множество). Во-первых, конечно, не передавать по значению. Во-вторых, не возвращать по значению. В-третьих, не делать лишних копий на стеке.
    Вообще, изменение счётчика необходимо только в 2 случаях — кладём в динамический объект, берем из динамического объекта. Всё, что касается стека, вызова функций и т.д. тут необходимости менять счётчик нет.
    В-четвёртых, не использовать подсчёт ссылок там, где управление временем жизни тривиально и без него. Например некоторые любят использовать shared_ptr в таких вещах, как очереди производитель-потребитель и многопоточные хэш-мапы; тут тривиально реализовать без него — производитель создал объект, положил в очередь, потребитель взял объект из очереди, обработал, удалил (пока объект живёт у производителя и потребителя его можно защитить auto_ptr).

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


    AG>Кстати, в Delphi для типов, для которых подсчёт ссылок встроен в компилятор, всё просто и без увеличения уровня косвенности:


    В С++ это тоже тривиально — используй интрузивный умный указатель (boost::intrusive_ptr). Если производительность важна, то неинтрузивный shared_ptr имеет смысл использовать только для прототипирования; а для реального кода — интрузивный (такая же история и с контейнерами — boost::intrusive).
    При использовании интрузивного подсчёта ссылок в функцию можно передавать обычный указатель на объект, а умный указатель всегда можно из него восстановить при необходимости.


    AG>
    AG>procedure P1(S: string; I: IUnknown);
    AG>begin (* неявное увеличение числа сссылок на S и AddRef для I *)
    AG>  S := F1(); I := F2(); (* можно *)
    AG>end; (* неявное уменьшение числа сссылок на S и Release для I *)
    AG>


    Зачем при обычном вызове функции надо менять счётчик 2 раза? Разве вызывающий код может освободить счётчик до возврата из функции?


  • 1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
    Re: void f(boost::shared_ptr<X> const& p);
    От: Тот кто сидит в пруду Россия  
    Дата: 29.12.09 10:41
    Оценка: 6 (1)
    Здравствуйте, Alexander G, Вы писали:

    AG>

    AG>Правило 20: Предпочитайте передачу по ссылке на const передачу по значению


    Хорошая, качественная опечатка

    AG>(Скотт Мейерс, Эффективное использование C++
    Автор(ы): Скотт Мейерс

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


    И по ссылке — тоже


    AG>Стоит ли передавать boost::shared_ptr<X> по константной ссылке?


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

    AG>но недостатков много:


    AG>
  • Добавляется косвенность (если ссылки реализованы как указатели).

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

    AG>
  • Больше букв писать.
    AG>
  • const ссылка может создавать иллюзию константности.
    AG>
  • Оно хорошо только если типы совпадают, иначе — преобразование всё равно нужно.
    AG>
    AG>class Y : X {};
    AG>void f(boost::shared_ptr<X const> const& p);
    
    AG>boost::shared_ptr<Y const> p1;
    AG>boost::shared_ptr<X> p2;
    AG>f(p1); // временый boost::shared_ptr<X const> создаётся
    AG>f(p2); // временый boost::shared_ptr<X const> создаётся
    AG>


    AG>Как вообще бороться с оверхедом на подсчёт ссылок и стоит ли?


    Если семантика позволяет — лучше вообще передавать X const&. IMHO.

    AG>Кстати, в Delphi для типов, для которых подсчёт ссылок встроен в компилятор, всё просто и без увеличения уровня косвенности:


    А вот лишний уровень косвенности я что-то не припоминаю, чтобы компилятор (VC) хоть раз сгородил. Явного указателя на указатель в коде же нет
  • Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
    Re[2]: void f(boost::shared_ptr<X> const& p);
    От: remark Россия http://www.1024cores.net/
    Дата: 29.12.09 10:52
    Оценка:
    Здравствуйте, Тот кто сидит в пруду, Вы писали:

    AG>>Стоит ли передавать boost::shared_ptr<X> по константной ссылке?


    ТКС>У меня были ситуации, когда по результатам профилирования оказывалось оправданным передавать shared_ptr по константной ссылке.


    А какие-нибудь цифры помнишь? Сколько он ел до оптимизации, сколько — после? Просто ради интереса.


    AG>>но недостатков много:


    AG>>
  • Добавляется косвенность (если ссылки реализованы как указатели).

    ТКС>При передаче по значению еще и пляски на тему исключений будут. А что могут быть за затраты на косвенность при обращении с объектом? С ним же один черт работа как через ссылку.


    Если умный указатель передаётся по-значению, то это равнозначно передаче указателя на объект.
    Если же передаётся ссылка на умный указатель, то это — указатель на указатель на объект.

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


  • 1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
    Re[2]: void f(boost::shared_ptr<X> const& p);
    От: Alexander G Украина  
    Дата: 29.12.09 11:10
    Оценка:
    Здравствуйте, remark, Вы писали:

    AG>>Как вообще бороться с оверхедом на подсчёт ссылок и стоит ли?


    R>*Может* стоить. Стоит ли в конкретном случае — скажет только профайлер.


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

    AG>>Кстати, в Delphi для типов, для которых подсчёт ссылок встроен в компилятор, всё просто и без увеличения уровня косвенности:


    R>В С++ это тоже тривиально — используй интрузивный умный указатель (boost::intrusive_ptr). Если производительность важна, то неинтрузивный shared_ptr имеет смысл использовать только для прототипирования; а для реального кода — интрузивный (такая же история и с контейнерами — boost::intrusive).

    R>При использовании интрузивного подсчёта ссылок в функцию можно передавать обычный указатель на объект, а умный указатель всегда можно из него восстановить при необходимости.

    Кстати, похожее возможно с boost::shared_ptr при использовании boost::enable_shared_from_this.


    AG>>
    AG>>procedure P1(S: string; I: IUnknown);
    AG>>begin (* неявное увеличение числа сссылок на S и AddRef для I *)
    AG>>  S := F1(); I := F2(); (* можно *)
    AG>>end; (* неявное уменьшение числа сссылок на S и Release для I *)
    AG>>


    R>Зачем при обычном вызове функции надо менять счётчик 2 раза? Разве вызывающий код может освободить счётчик до возврата из функции?


    т.к. переданы I и S "по значению", в самой вызывающей функции этим переменным могут присваиваться другие значения, при присвоении, как обычно счётчик ссылок правой части увеличивается, затем левой уменьшается. Для балланса необходимо это неявное увеличение/уменьшение счётчика ссылок на входе/выходе.
    при передаче "по константному значению" возможность присвоение переменной нового значения отсутствует.

    наверное, можно было бы в boost::shared_ptr предусмотреть на этот случай класс, который вёл бы себя как shared_ptr без =, reset и swap, который бы не дёргал счётчик ссылок. но у boost::shared_ptr приоритеты — корректность и единообразие, а не оптимальность и настраиваемость.

    Русский военный корабль идёт ко дну!
    Re[2]: void f(boost::shared_ptr<X> const& p);
    От: Alexander G Украина  
    Дата: 29.12.09 11:16
    Оценка:
    Здравствуйте, Тот кто сидит в пруду, Вы писали:

    ТКС>При передаче по значению еще и пляски на тему исключений будут.




    Конструкторы сматрпоинтеров не бросают исключений
    Русский военный корабль идёт ко дну!
    Re[3]: void f(boost::shared_ptr<X> const& p);
    От: Тот кто сидит в пруду Россия  
    Дата: 29.12.09 11:41
    Оценка:
    Здравствуйте, remark, Вы писали:

    AG>>>Стоит ли передавать boost::shared_ptr<X> по константной ссылке?


    ТКС>>У меня были ситуации, когда по результатам профилирования оказывалось оправданным передавать shared_ptr по константной ссылке.


    R>А какие-нибудь цифры помнишь? Сколько он ел до оптимизации, сколько — после? Просто ради интереса.


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

    AG>>>но недостатков много:


    AG>>>
  • Добавляется косвенность (если ссылки реализованы как указатели).

    ТКС>>При передаче по значению еще и пляски на тему исключений будут. А что могут быть за затраты на косвенность при обращении с объектом? С ним же один черт работа как через ссылку.


    R>Если умный указатель передаётся по-значению, то это равнозначно передаче указателя на объект.


    Нет. Это равнозначно созданию нового объекта типа "умный указатель", установке/разрушению фреймов исключений, вызове методов shared_ptr(), разрушению объекта.

    R>Если же передаётся ссылка на умный указатель, то это — указатель на указатель на объект.


    Не-не-не. Вызов методов объекта типа shared_ptr<X> будет осуществляться ровно так же, как и в предыдущем случае. Хотя он скорее всего и проинлайнится, картины это изменить не должно.
  • Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
    Re[3]: void f(boost::shared_ptr<X> const& p);
    От: Тот кто сидит в пруду Россия  
    Дата: 29.12.09 11:51
    Оценка:
    Здравствуйте, Alexander G, Вы писали:

    ТКС>>При передаче по значению еще и пляски на тему исключений будут.


    AG>


    AG>Конструкторы сматрпоинтеров не бросают исключений


    Так а порушить-то его в случае чего все-равно надо.
    Вот иллюстрация:


    __declspec(noinline) int foo(boost::shared_ptr<X> x)
    {
        return x->test();
    }



    __declspec(noinline) int foo(boost::shared_ptr<X> x)
    {
    00EB11E0  push        0FFFFFFFFh 
    00EB11E2  push        offset __ehhandler$?foo@@YAHV?$shared_ptr@VX@@@boost@@@Z (0EB22F8h) 
    00EB11E7  mov         eax,dword ptr fs:[00000000h] 
    00EB11ED  push        eax  
    00EB11EE  push        ecx  
    00EB11EF  push        esi  
    00EB11F0  push        edi  
    00EB11F1  mov         eax,dword ptr [___security_cookie (0EB4018h)] 
    00EB11F6  xor         eax,esp 
    00EB11F8  push        eax  
    00EB11F9  lea         eax,[esp+10h] 
    00EB11FD  mov         dword ptr fs:[00000000h],eax 
    00EB1203  mov         dword ptr [esp+18h],0 
        return x->test();
    00EB120B  mov         eax,dword ptr [esp+20h] 
    00EB120F  push        eax  
    00EB1210  call        X::test (0EB1150h) 
    00EB1215  mov         dword ptr [esp+18h],0FFFFFFFFh 
    00EB121D  mov         esi,dword ptr [esp+24h] 
    00EB1221  mov         edi,eax 
    00EB1223  test        esi,esi 
    00EB1225  je          foo+73h (0EB1253h) 
    00EB1227  lea         ecx,[esi+4] 
    00EB122A  or          edx,0FFFFFFFFh 
    00EB122D  lock xadd   dword ptr [ecx],edx 
    00EB1231  jne         foo+73h (0EB1253h) 
    00EB1233  mov         eax,dword ptr [esi] 
    00EB1235  mov         edx,dword ptr [eax+4] 
    00EB1238  mov         ecx,esi 
    00EB123A  call        edx  
    00EB123C  lea         eax,[esi+8] 
    00EB123F  or          ecx,0FFFFFFFFh 
    00EB1242  lock xadd   dword ptr [eax],ecx 
    00EB1246  jne         foo+71h (0EB1251h) 
    00EB1248  mov         edx,dword ptr [esi] 
    00EB124A  mov         eax,dword ptr [edx+8] 
    00EB124D  mov         ecx,esi 
    00EB124F  call        eax  
    00EB1251  mov         eax,edi 
    }
    00EB1253  mov         ecx,dword ptr [esp+10h] 
    00EB1257  mov         dword ptr fs:[0],ecx 
    00EB125E  pop         ecx  
    00EB125F  pop         edi  
    00EB1260  pop         esi  
    00EB1261  add         esp,10h 
    00EB1264  ret


    И

    __declspec(noinline) int bar(boost::shared_ptr<X> const & x)
    {
        return x->test();
    }


    с кодами
    __declspec(noinline) int bar(boost::shared_ptr<X> const & x)
    {
    00EB12B0 push ecx
    return x->test();
    00EB12B1 mov eax,dword ptr [eax]
    00EB12B3 push eax
    00EB12B4 call X::test (0EB1150h)
    00EB12B9 pop ecx
    }
    00EB12BA ret
    Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
    Re[3]: void f(boost::shared_ptr<X> const& p);
    От: remark Россия http://www.1024cores.net/
    Дата: 29.12.09 12:45
    Оценка:
    Здравствуйте, Alexander G, Вы писали:

    AG>>>Как вообще бороться с оверхедом на подсчёт ссылок и стоит ли?


    R>>*Может* стоить. Стоит ли в конкретном случае — скажет только профайлер.


    AG>Профайлер скажет. Вопрос в том, что писать до тех пор, пока профайлер не потребовался, чтобы не пессимизировать преждевременно.


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


    AG>>>Кстати, в Delphi для типов, для которых подсчёт ссылок встроен в компилятор, всё просто и без увеличения уровня косвенности:


    R>>Зачем при обычном вызове функции надо менять счётчик 2 раза? Разве вызывающий код может освободить счётчик до возврата из функции?


    AG>т.к. переданы I и S "по значению", в самой вызывающей функции этим переменным могут присваиваться другие значения, при присвоении, как обычно счётчик ссылок правой части увеличивается, затем левой уменьшается. Для балланса необходимо это неявное увеличение/уменьшение счётчика ссылок на входе/выходе.

    AG>при передаче "по константному значению" возможность присвоение переменной нового значения отсутствует.

    Сделали поддержку в компляторе, и так облажались


    AG>наверное, можно было бы в boost::shared_ptr предусмотреть на этот случай класс, который вёл бы себя как shared_ptr без =, reset и swap, который бы не дёргал счётчик ссылок. но у boost::shared_ptr приоритеты — корректность и единообразие, а не оптимальность и настраиваемость.


    По-хорошему нужен указатель, который поддерживает одновременные обращения из разных потоков (то, что сейчас вынесено в отдельные atomic_* функции); указатель, который не поддерживает обращения из разных потоков, но поддерживает разделение объекта между потоками (текущий shared_ptr); однопоточный указатель (нет в boost), и умный указатель для передачи как параметр в функции и возвращения из функций (по сути — просто обёртка над указателем).
    Тогда можно было бы точно подбирать требуемый вариант, не компромитируя единообразие синтаксиса.


    AG>


    1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
    Re[4]: void f(boost::shared_ptr<X> const& p);
    От: remark Россия http://www.1024cores.net/
    Дата: 29.12.09 12:58
    Оценка:
    Здравствуйте, Тот кто сидит в пруду, Вы писали:

    ТКС>>>У меня были ситуации, когда по результатам профилирования оказывалось оправданным передавать shared_ptr по константной ссылке.


    R>>А какие-нибудь цифры помнишь? Сколько он ел до оптимизации, сколько — после? Просто ради интереса.


    ТКС>Пока было копирование — в выводе профайлера оно присутствовало на уровне единиц процентов. После замены на ссылки оно, естественно, ушло совсем. Ну и число собранных сэмплов интересующего фрагмента кода тоже на несколько процентов ушло вниз, что подтверждает влияние именно копирования/разрушения shared_ptr. Но это ж не бенчмарка, которая для подтверждения какой-то гипотезы писалась, просто в конкретном куске кода так совпало, да и профилировалось оно по другому поводу.


    Спасибо, понятно, т.е. не особо большие потери были на лишние операции со счётчиком.


    1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
    Re[4]: void f(boost::shared_ptr<X> const& p);
    От: remark Россия http://www.1024cores.net/
    Дата: 29.12.09 13:00
    Оценка:
    Здравствуйте, Тот кто сидит в пруду, Вы писали:

    AG>>>>
  • Добавляется косвенность (если ссылки реализованы как указатели).

    ТКС>>>При передаче по значению еще и пляски на тему исключений будут. А что могут быть за затраты на косвенность при обращении с объектом? С ним же один черт работа как через ссылку.


    R>>Если умный указатель передаётся по-значению, то это равнозначно передаче указателя на объект.


    ТКС>Нет. Это равнозначно созданию нового объекта типа "умный указатель", установке/разрушению фреймов исключений, вызове методов shared_ptr(), разрушению объекта.


    Это понятно, но не имеет отношения к косвенности.


    R>>Если же передаётся ссылка на умный указатель, то это — указатель на указатель на объект.


    ТКС>Не-не-не. Вызов методов объекта типа shared_ptr<X> будет осуществляться ровно так же, как и в предыдущем случае. Хотя он скорее всего и проинлайнится, картины это изменить не должно.


    Это каким-таким образом передача по значению и по ссылке будет одинаковой???

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

    void baz(boost::shared_ptr<X> const& p)
    {
        bar(p.get());
    001210A0  mov         eax,dword ptr [esp+4] 
    001210A4  mov         ecx,dword ptr [eax] 
    001210A6  mov         dword ptr [esp+4],ecx 
    001210AA  jmp         bar (121040h) 
    }


    void foo(boost::shared_ptr<X> p)
    {
        bar(p.get());
    00121150  mov         eax,dword ptr [esp+4] 
    00121154  push        esi  
    00121155  push        eax  
    00121156  call        bar (121040h) 
    }




  • 1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
    Re[2]: void f(boost::shared_ptr<X> const& p);
    От: remark Россия http://www.1024cores.net/
    Дата: 29.12.09 13:08
    Оценка: 6 (1)
    Здравствуйте, remark, Вы писали:

    AG>>
  • Добавляется косвенность (если ссылки реализованы как указатели).

    R>Если приложение многопоточное (для однопоточного там вроде был какой-то макрос для shared_ptr, который убирал атомарные операции из shared_ptr), то стоит. Это покроет дополнительный уровень косвенности.



    Тут ещё есть тонкий момент. Производительность будет зависеть от того, разделяется ли данный умный указатель между потоками или нет (ещё/уже/вообще). Если не разделяется, то стоимость операций со счётчиком относительно велика, но не катастрофична. Если же он разделяется, то это можнет положить систему.
    Предельно плохой сценарий будет такой. В программе есть некий глобальный объект (типа настроек, или таблицы какой-то), множество рабочих потоков в начале обработки очередного запроса "захватювают" этот объект и дальше начинают передавать в разные функции как shared_ptr по значению. В такой ситуации все эти операции со счётчиком (не считая первой и последней) абсолютно бессмысленны, но могут полностью уложить систему, т.к. множество потоков при каждой операции будут конкурировать друг с другом.



  • 1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
    Re[5]: void f(boost::shared_ptr<X> const& p);
    От: Тот кто сидит в пруду Россия  
    Дата: 29.12.09 13:45
    Оценка:
    Здравствуйте, remark, Вы писали:

    R>>>Если же передаётся ссылка на умный указатель, то это — указатель на указатель на объект.


    ТКС>>Не-не-не. Вызов методов объекта типа shared_ptr<X> будет осуществляться ровно так же, как и в предыдущем случае. Хотя он скорее всего и проинлайнится, картины это изменить не должно.


    R>Это каким-таким образом передача по значению и по ссылке будет одинаковой???


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

    R>Вот тест, скомпилированной студией в релиз режиме. При передаче по ссылке — лишний уровень косвенности:


    R>
    R>void baz(boost::shared_ptr<X> const& p)
    R>{
    R>    bar(p.get());
    R>001210A0  mov         eax,dword ptr [esp+4] 
    R>001210A4  mov         ecx,dword ptr [eax] 
    R>001210A6  mov         dword ptr [esp+4],ecx 
    R>001210AA  jmp         bar (121040h) 
    R>}
    R>


    R>
    R>void foo(boost::shared_ptr<X> p)
    R>{
    R>    bar(p.get());
    R>00121150  mov         eax,dword ptr [esp+4] 
    R>00121154  push        esi  
    R>00121155  push        eax  
    R>00121156  call        bar (121040h) 
    R>}
    R>


    Зато в первом случае компилятор догадался поменять call на jmp
    Вообще как фишка ляжет. Я рядом кусочек кода постил — там компилятор в случае с константной ссылкой догадался fastcall применить, обошлось без лишней косвенности. Щас попробую усложнить, интересно че нагенерит.
    Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
    Re[6]: лирическое отступление
    От: Тот кто сидит в пруду Россия  
    Дата: 29.12.09 14:02
    Оценка:
    ТКС>Вообще как фишка ляжет. Я рядом кусочек кода постил — там компилятор в случае с константной ссылкой догадался fastcall применить, обошлось без лишней косвенности. Щас попробую усложнить, интересно че нагенерит.

    Оффтопик: оптимизатор вообще отжигает не по детски:

    __declspec(noinline) int __stdcall bar(char const *name, int q, boost::shared_ptr<X> const & x)
    {
        std::cout << name << q;
        if (x->test2() > 0.5)
            return x->test();
        return 0;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        boost::shared_ptr<X> a(new X);
        foo("value", 10, a);
        bar("reference", 10, a);
        return 0;
    }


    Тело функции bar:


    __declspec(noinline) int __stdcall bar(char const *name, int q, boost::shared_ptr<X> const & x)
    {
    01351370  push        ecx  
        std::cout << name << q;
    01351371  mov         eax,dword ptr [__imp_std::cout (1353064h)] 
    01351376  push        0Ah  
    01351378  push        ecx  
    01351379  push        offset string "reference" (1353184h) 
    0135137E  push        eax  
    0135137F  call        std::operator<<<std::char_traits<char> > (1351A40h) 
    01351384  add         esp,0Ch 
    01351387  mov         ecx,eax 
    01351389  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (1353048h)] 
        if (x->test2() > 0.5)
    0135138F  mov         eax,dword ptr [esi] 
    01351391  call        X::test2 (1351200h) 
    01351396  fcomp       qword ptr [__real@3fe0000000000000 (13531F0h)] 
    0135139C  fnstsw      ax   
    0135139E  test        ah,41h 
    013513A1  jne         bar+3Dh (13513ADh) 
            return x->test();
    013513A3  mov         eax,dword ptr [esi] 
    013513A5  push        eax  
    013513A6  call        X::test (1351170h) 
    013513AB  pop         ecx  
    };


    Что называется — не мытьем, так катаньем. Встраивать функцию ему запретили, так он константные параметры внутрь затащил
    Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
    Re[6]: void f(boost::shared_ptr<X> const& p);
    От: remark Россия http://www.1024cores.net/
    Дата: 29.12.09 15:17
    Оценка:
    Здравствуйте, Тот кто сидит в пруду, Вы писали:

    ТКС>Здравствуйте, remark, Вы писали:


    R>>>>Если же передаётся ссылка на умный указатель, то это — указатель на указатель на объект.


    ТКС>>>Не-не-не. Вызов методов объекта типа shared_ptr<X> будет осуществляться ровно так же, как и в предыдущем случае. Хотя он скорее всего и проинлайнится, картины это изменить не должно.


    R>>Это каким-таким образом передача по значению и по ссылке будет одинаковой???


    ТКС>Передача — по разному, вызов — как сложится, оптимизатор всякое вытворить может. Обычно, IMHO, вызов одинаково выполняется.


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



    R>>Вот тест, скомпилированной студией в релиз режиме. При передаче по ссылке — лишний уровень косвенности:


    R>>
    R>>void baz(boost::shared_ptr<X> const& p)
    R>>{
    R>>    bar(p.get());
    R>>001210A0  mov         eax,dword ptr [esp+4] 
    R>>001210A4  mov         ecx,dword ptr [eax] 
    R>>001210A6  mov         dword ptr [esp+4],ecx 
    R>>001210AA  jmp         bar (121040h) 
    R>>}
    R>>


    R>>
    R>>void foo(boost::shared_ptr<X> p)
    R>>{
    R>>    bar(p.get());
    R>>00121150  mov         eax,dword ptr [esp+4] 
    R>>00121154  push        esi  
    R>>00121155  push        eax  
    R>>00121156  call        bar (121040h) 
    R>>}
    R>>


    ТКС>Зато в первом случае компилятор догадался поменять call на jmp

    ТКС>Вообще как фишка ляжет. Я рядом кусочек кода постил — там компилятор в случае с константной ссылкой догадался fastcall применить, обошлось без лишней косвенности. Щас попробую усложнить, интересно че нагенерит.

    LTCG — это отдельная история.



    1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
    Re[7]: лирическое отступление
    От: remark Россия http://www.1024cores.net/
    Дата: 29.12.09 15:20
    Оценка:
    Здравствуйте, Тот кто сидит в пруду, Вы писали:

    ТКС>>Вообще как фишка ляжет. Я рядом кусочек кода постил — там компилятор в случае с константной ссылкой догадался fastcall применить, обошлось без лишней косвенности. Щас попробую усложнить, интересно че нагенерит.


    ТКС>Оффтопик: оптимизатор вообще отжигает не по детски:


    ТКС>Что называется — не мытьем, так катаньем. Встраивать функцию ему запретили, так он константные параметры внутрь затащил


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


    1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
    Re[8]: лирическое отступление
    От: Тот кто сидит в пруду Россия  
    Дата: 29.12.09 16:44
    Оценка:
    Здравствуйте, remark, Вы писали:

    R>Здравствуйте, Тот кто сидит в пруду, Вы писали:


    ТКС>>>Вообще как фишка ляжет. Я рядом кусочек кода постил — там компилятор в случае с константной ссылкой догадался fastcall применить, обошлось без лишней косвенности. Щас попробую усложнить, интересно че нагенерит.


    ТКС>>Оффтопик: оптимизатор вообще отжигает не по детски:


    ТКС>>Что называется — не мытьем, так катаньем. Встраивать функцию ему запретили, так он константные параметры внутрь затащил


    R>Если отключить LTCG, то всё встанет на свои места.

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

    Да, действительно, без LTCG убрать лишнюю косвенность оптимизатору не удается. Хотя все равно — для того, чтобы передача shared_ptr по ссылке отстала от передачи по значению, функция должна быть очень уж своеобразная. Потому что на то, чтобы сложить при необходимости результат первого разыменования в регистр у оптимизатора "ума" хватает, а фора (конструктор+деструктор+(опционально — расходы на поддержку исключений)) у передачи по ссылке большая.
    Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
    Re[4]: void f(boost::shared_ptr<X> const& p);
    От: Alexander G Украина  
    Дата: 29.12.09 17:09
    Оценка:
    Здравствуйте, remark, Вы писали:

    R>>>Зачем при обычном вызове функции надо менять счётчик 2 раза? Разве вызывающий код может освободить счётчик до возврата из функции?


    AG>>т.к. переданы I и S "по значению", в самой вызывающей функции этим переменным могут присваиваться другие значения, при присвоении, как обычно счётчик ссылок правой части увеличивается, затем левой уменьшается. Для балланса необходимо это неявное увеличение/уменьшение счётчика ссылок на входе/выходе.

    AG>>при передаче "по константному значению" возможность присвоение переменной нового значения отсутствует.

    R>Сделали поддержку в компляторе, и так облажались


    Я так и не понял, к чему претензии.
    Если передаём по значению, значит может потребоваться присвоить переменной новое значение. Как внутри функции, так и передав куда-то как var параметр (параметр по ссылке). т.е. на C++ было бы как-то так
    void f(boost::shared_array<char> s); // здесь увеличение числа ссылок
    {
       s = f1(); // может поменяться
    }// здесь уменьшение числа ссылок


    Конечно, можно было бы обнаруживать, когда переменная не меняется и устранять лишний подсчёт ссылок для этого случая. Возможно, более новая версия Delphi так и делает.
    Русский военный корабль идёт ко дну!
     
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.