хе хе или пофлеймим в понедельнег :)

Все, что вы хотели знать о программизме, но боялись спросить.
Аватара пользователя
sz
Маньяк
Сообщения: 1266
Зарегистрирован: 17 фев 2003, 19:34

Сообщение sz »

Azazello писал(а):Я, конечно, не специалист, так что больно не бейте ;). По-моему, вариант со swap не самый эффективный из-за overhead плюс лишний вызов memcpy.
Я, откровенно говоря, пример со свапом вообще не понял. Ну, swap. Объявили такую функцию. А что она, собственно, делает?
В чем изященство-то?
Azazello писал(а):А такой вариант не подойдёт (new выбрасывает exception)?
Подойдет, в принципе. Но слегка напрягает память на один лишний блок. Если уж там доходит до нехватки, то это, наверное, критично.

Лучше выключить эксепшены и проверить на нуль. И код проще, и время выполнения быстрее.
Аватара пользователя
aissp
Маньяк
Сообщения: 2710
Зарегистрирован: 07 ноя 2005, 09:51

Сообщение aissp »

swap просто обменивает пойнтеры, и поетому она безопасна в смысле исключений. То есть екзактли код описанный азазелло - ето то что внутри свапа - что она обменивает пойнтеры неявно следует из ее определения как функции не бросаюшей исключеньев. 8)

О да естественно речь идет о строгой гарантии оператора = на отсутствие исключениев. Спецом для оптимизаторов определяем задачу, если оперетор = займеть на один блок больше, то не страшно, а вот если объект не откатит то штраф. 8)
Последний раз редактировалось aissp 19 мар 2007, 17:58, всего редактировалось 1 раз.
Аватара пользователя
ajkj3em
Маньяк
Сообщения: 2063
Зарегистрирован: 12 ноя 2006, 06:53

Сообщение ajkj3em »

sz писал(а):Если уж там доходит до нехватки, то это, наверное, критично.
ну если уж за блохами гоняться, то надо realloc() использовать вместо delete/new
Аватара пользователя
Azazello
Житель
Сообщения: 769
Зарегистрирован: 16 янв 2007, 04:31

Сообщение Azazello »

sz писал(а): Я, откровенно говоря, пример со свапом вообще не понял. Ну, swap. Объявили такую функцию. А что она, собственно, делает?
В чем изященство-то?
В том, что автоматически вызывается деструктор для temp, в котором после swap лежит содержимое *this. Но это неэффективно с точки зрения overhead на вызов copy-constructor, swap (нужно два memcpy, если swap - нормальный swap), destructor.
sz писал(а): Подойдет, в принципе. Но слегка напрягает память на один лишний блок. Если уж там доходит до нехватки, то это, наверное, критично.

Лучше выключить эксепшены и проверить на нуль. И код проще, и время выполнения быстрее.
В варианте со swap точно также выделяется дополнительная память. С точки зрения экономии памяти - оба варианта одинаковы. С точки зрения скорости - swap медленнее, особенно, если class qq - имеет какие-то base classes, скажем виртуальный интерфейс, чьи constructors и destructors будут вызваны.
Аватара пользователя
aissp
Маньяк
Сообщения: 2710
Зарегистрирован: 07 ноя 2005, 09:51

Сообщение aissp »

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

Итого получаем одну аллокацию буффера в конструкторе копии и одну деаллакацию в деструкторе временного объекта - ето все.
Истчо раз - одЫн раз вызываем new одЫн раз delete
Аватара пользователя
Azazello
Житель
Сообщения: 769
Зарегистрирован: 16 янв 2007, 04:31

Сообщение Azazello »

aissp писал(а):откуда там мемкопи то?
свап просто меняет содержание указателей. он больше ничего не делает.
Пардон, assip, виноват. Да, конечно, Вы правы, я почему-то подумал, что имеется ввиду "Standard-like" swap. В любом случае, мой вариант немного быстрее (overhead), но практически незаметно. С другой стороны, почему бы не сделать чуть быстрее ;)?
Кстати, я может чего не уловил - operator=() НЕ должен выбрасывать исключений? В смысле, исключение ВООБЩЕ не должно выбрасываться, или объект просто должен быть живым после исключения? Поскольку, я полагаю, в swap варианте исключение может быть выброшено из copy-constructor.
Последний раз редактировалось Azazello 19 мар 2007, 18:26, всего редактировалось 1 раз.
Аватара пользователя
sz
Маньяк
Сообщения: 1266
Зарегистрирован: 17 фев 2003, 19:34

Сообщение sz »

aissp писал(а):swap просто обменивает пойнтеры, и поетому она безопасна в смысле исключений.
А, ну это тогда то же самое, что Azazello предлагал. Перенести delete после new.
Аватара пользователя
sz
Маньяк
Сообщения: 1266
Зарегистрирован: 17 фев 2003, 19:34

Сообщение sz »

ajkj3em писал(а):ну если уж за блохами гоняться, то надо realloc() использовать вместо delete/new
Логично, но тогда уж и malloc/free.
Не не все аллокаторы умеют realloc.
Поэтому с учетом возможного переопределения new/delete, лучше realloc с ними вместе не трогать.
Аватара пользователя
aissp
Маньяк
Сообщения: 2710
Зарегистрирован: 07 ноя 2005, 09:51

Сообщение aissp »

угу объект должен остаться жив, то есть оператор= должен работать как транзакция в независимости от выброса исключеньев.
Аватара пользователя
Azazello
Житель
Сообщения: 769
Зарегистрирован: 16 янв 2007, 04:31

Сообщение Azazello »

sz писал(а):
ajkj3em писал(а):ну если уж за блохами гоняться, то надо realloc() использовать вместо delete/new
Логично, но тогда уж и malloc/free.
Не не все аллокаторы умеют realloc.
Поэтому с учетом возможного переопределения new/delete, лучше realloc с ними вместе не трогать.
В принципе, если new/delete используют некие Alloc/Free, то ReAlloc будет работать, но только на типах, которые built-in-like - с bitwise semantics и default destructors, что, в общем, понятно... Хотя, это и не гарантируется Standard, нужно проверять на конкретном компиляторе. Лучше всего написать свои new/delete, а в placement new (для красоты ;)) использовать ReAlloc и проверить. В принципе, можно получить очень большой выигрыш по скорости - с ReAlloc часто получается избежать копирования.
Аватара пользователя
ajkj3em
Маньяк
Сообщения: 2063
Зарегистрирован: 12 ноя 2006, 06:53

Сообщение ajkj3em »

sz писал(а):
ajkj3em писал(а):ну если уж за блохами гоняться, то надо realloc() использовать вместо delete/new
Логично, но тогда уж и malloc/free.
Не не все аллокаторы умеют realloc.
Поэтому с учетом возможного переопределения new/delete, лучше realloc с ними вместе не трогать.

ну я имел в виду хитрые варианты realloc'a, которые по возможности
избегают memcpy с помошю хитрож*пых page mappings, small block
memory pools и всего такого ..

то есть поинт в том, что malloc/free, new/delete - это кул, но при этом
используеця один лишний блок; realloc жe - marginally better, но при
втом тривиальные реализации используют memcpy, что тоже есть не
фонтан.

то есть все как обычно - надо смотреть на конкретную задачу и
пользовать чего подходит :)
Аватара пользователя
sz
Маньяк
Сообщения: 1266
Зарегистрирован: 17 фев 2003, 19:34

Сообщение sz »

Ну я к этому добавлю, что нынешние процессоры так интересно устроены, что для меня, например, совсем неочевидно, что realloc будет всегда быстрее комбинации free, malloc, memcpy.
Аватара пользователя
Azazello
Житель
Сообщения: 769
Зарегистрирован: 16 янв 2007, 04:31

Сообщение Azazello »

sz писал(а):Ну я к этому добавлю, что нынешние процессоры так интересно устроены, что для меня, например, совсем неочевидно, что realloc будет всегда быстрее комбинации free, malloc, memcpy.
sz, Вы не поясните? Насколько я понимаю (но может быть я и ошибаюсь), скорость realloc >= скорости malloc+memcpy+free (не считая overhead) и зависит от реализации RTL. Реально, realloc дополнительную память - это в худшем случае = malloc+memcpy+free, a в лучшем просто, грубо говоря, malloc в конец уже allocated блока памяти, что часто и происходит (зависит от степени фрагментации кучи и размера запрашиваемой памяти). А если мы уменьшаем размер блока, то мы просто возвращаем "лишнюю" память в pool - вообще никогда ни malloc, ни memcpy не нужны. Или я чего-то не понимаю здесь?
Аватара пользователя
sz
Маньяк
Сообщения: 1266
Зарегистрирован: 17 фев 2003, 19:34

Сообщение sz »

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

void copy( void* ptr1, const void* ptr2, unsigned size )
{
if( ptr1 != ptr2 ) memcpy( ptr1, ptr2, size );
}

или:

void copy( void* ptr1, const void* ptr2, unsigned size )
{
memcpy( ptr1, ptr2, size );
}

Казалось бы первый? Ведь он не делает копирования в случае совпадения. Ан нет. Потому что он делает кое что похуже копирования - сравнение. Если бранч предскажется неправильно, то процессору придется очищать конвейр и нагружать заново.

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

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

У микрософта есть, кстати, очень правильный tool к xbox. Визуально показывает, как команды бегут через конвейры. Ему скармливаешь ассемблер, а он показывает, как это отработает. Очень помогает в оптимизации. А главное, зрелище совершенно завораживающее ;)
Аватара пользователя
Azazello
Житель
Сообщения: 769
Зарегистрирован: 16 янв 2007, 04:31

Сообщение Azazello »

Хмм... На машинах, с которыми я работал, среднее время realloc значительно меньше, чем malloc+memcpy+free. Но, каюсь, с убер новыми процессорами, которые оптимизируют это всё на hardware уровне, дела я не имел. sz, а Вы не могли бы ссылочку дать, где бы это было описано? Не хотелось бы Ваше время занимать, а почитать хочется (я так понимаю, это больше по hardware) ;). Особенно меня интересует параллельная с дальнейшим выполнением программы запись в память. Serialized heaps, хотя и чуть медленнее, но, тем не менее, не могут (в теории) стать corrupted в случае, когда несколько threads пытаются alloc или free память в одной и той же куче. Non-serialized heaps оставляют эту проблему программисту, и при параллельном доступе могут стать (и скорее всего, станут) corrupted. Насколько я понимаю, стандартная Cишная куча - serialized (точно для multi-thread application). В общем, буду благодарен ссылке.
Ответить