А был ли мальчик? (помянем плюсы)

User avatar
AndreyT
Уже с Приветом
Posts: 3000
Joined: 14 Apr 2004 01:11
Location: SFBA (было: Минск, Беларусь)

Re: А был ли мальчик? (помянем плюсы)

Post by AndreyT »

ksi wrote: 25 Jan 2018 05:37 А можно unrelated вопрос? У меня была такая ситуация (я не программист в чистом виде, поэтому сорри, если чего не понимаю): мне нужно создать в памяти копии какого объекта. В большом количестве, сотни тысяч или миллионы потенциально. Поинтеры на новые объекты куда-то положить, ну в массив например. Есть какой-нибудь трюк чтобы это сделать наиболее быстро или ничего кроме цикла из memcpy нельзя придумать?
Если погрузиться глубже уровня С++ платформы, то в системах с виртуальной памятью вы можете достичь такого эффекта заполнив копиями вашего объекта всего одну физическую страницу памяти и замапив эту единственную страницу в требуемый вам диапазон виртуальных адресов адресного пространства процесса. То есть пока вы используете этот диапазон адресов только по чтению, вы будете видеть в нем виртуальные копии одной и той же физической страницы: гигантский массив копий, на построение которого вы затратили минимальные усилия.

При необходимости записи будет работать механизм copy-on-write, т.е. при записи в некую точку этого диапазона виртуальных адресов от исходной физической страницы "на лету" будет отпочковываться новая физическая страница, в которой и будет выполняться модификация. Понятно, что если в конечном итоге вы собираетесь модифицуировать все элементы вашего гигантского массива, то "отпочкуется" в итоге все: финальный эффект не будет отличаться от изначального физического выделения и явного заполнения всего массива...
Best regards,
Андрей
Pantigalt
Уже с Приветом
Posts: 803
Joined: 24 Jan 2007 07:32
Location: Сергели->Новосибирск->SFBA->Новосибирск->Москва->NY->SFBA

Re: А был ли мальчик? (помянем плюсы)

Post by Pantigalt »

ksi wrote: 25 Jan 2018 14:17 А память под вектор выделяется одним куском или она может быть фрагментирована? Это можно как-то эмулировать в С стиле: я имею в виду, что если просто звать malloc (1000000*sizeof (MyObject)) то такого куска памяти подряд может просто не быть. У вектора есть внутренняя поддержка для таких ситуаций?
На этот случай есть std::deque.
Спи быстрее, твоя подушка нужна другому. Copyright Зощенко
Pantigalt
Уже с Приветом
Posts: 803
Joined: 24 Jan 2007 07:32
Location: Сергели->Новосибирск->SFBA->Новосибирск->Москва->NY->SFBA

Re: А был ли мальчик? (помянем плюсы)

Post by Pantigalt »

del
Спи быстрее, твоя подушка нужна другому. Copyright Зощенко
Сабина
Уже с Приветом
Posts: 19041
Joined: 11 Jan 2012 09:25
Location: CA

Re: А был ли мальчик? (помянем плюсы)

Post by Сабина »

Prosche wrote: 25 Jan 2018 16:08 Хорошо, хотите натягивать мув на свап, не смею вам мешать. Я сделал все что мог.
Хотите думать, что мув придуман чтобы сохранять дорогие ресурсы, барабан вам на шею. Будете сидеть в тишине вашего кабинета, попробуйте подумать почему именно мув, такое странное название, в словарик там посмотрите. Может быть осознаете простую истину, что идея мув в мув, взять справа и передвинуть влево, а совсем не сохранить "дорогие ресурсы".
То есть вы не просто "страшный", вы еще и спорить любите :mrgreen: ?
https://www.youtube.com/watch?v=wOwblaKmyVw
User avatar
Alexander Troyansky
Уже с Приветом
Posts: 5753
Joined: 15 Aug 2008 00:52

Re: А был ли мальчик? (помянем плюсы)

Post by Alexander Troyansky »

который год уже с читаю АндреяТ на тему c++: понимаю, как мало я в этом деле понимаю :)
I would hope that a wise white man with the richness of his experiences would more often than not reach a better conclusion than a latina female who hasn't lived that life
User avatar
Мальчик-Одуванчик
Уже с Приветом
Posts: 15526
Joined: 27 Sep 2007 22:53

Re: А был ли мальчик? (помянем плюсы)

Post by Мальчик-Одуванчик »

Alexander Troyansky wrote: 26 Jan 2018 06:24 который год уже с читаю АндреяТ на тему c++: понимаю, как мало я в этом деле понимаю :)
Страуструп тоже об этом писал.
User avatar
Prosche
Уже с Приветом
Posts: 8090
Joined: 08 Nov 2004 12:24
Location: GA

Re: А был ли мальчик? (помянем плюсы)

Post by Prosche »

Сабина wrote: 26 Jan 2018 05:45
Prosche wrote: 25 Jan 2018 16:08 Хорошо, хотите натягивать мув на свап, не смею вам мешать. Я сделал все что мог.
Хотите думать, что мув придуман чтобы сохранять дорогие ресурсы, барабан вам на шею. Будете сидеть в тишине вашего кабинета, попробуйте подумать почему именно мув, такое странное название, в словарик там посмотрите. Может быть осознаете простую истину, что идея мув в мув, взять справа и передвинуть влево, а совсем не сохранить "дорогие ресурсы".
То есть вы не просто "страшный", вы еще и спорить любите :mrgreen: ?
Только если вижу как с большим апломбом и умным видом пишут глупости. :-)
Pantigalt
Уже с Приветом
Posts: 803
Joined: 24 Jan 2007 07:32
Location: Сергели->Новосибирск->SFBA->Новосибирск->Москва->NY->SFBA

Re: А был ли мальчик? (помянем плюсы)

Post by Pantigalt »

Prosche wrote: 26 Jan 2018 15:18
Сабина wrote: 26 Jan 2018 05:45
Prosche wrote: 25 Jan 2018 16:08 Хорошо, хотите натягивать мув на свап, не смею вам мешать. Я сделал все что мог.
Хотите думать, что мув придуман чтобы сохранять дорогие ресурсы, барабан вам на шею. Будете сидеть в тишине вашего кабинета, попробуйте подумать почему именно мув, такое странное название, в словарик там посмотрите. Может быть осознаете простую истину, что идея мув в мув, взять справа и передвинуть влево, а совсем не сохранить "дорогие ресурсы".
То есть вы не просто "страшный", вы еще и спорить любите :mrgreen: ?
Только если вижу как с большим апломбом и умным видом пишут глупости. :-)
Вы почитайте книжки пожалуйста. Например Майерса - "Effective Modern C++"
http://doc.imzlp.me/viewer.html?file=do ... ernCPP.pdf

Item 29 "Assume that move operations are not present,
not cheap, and not used." Page 203
Let’s begin with the observation that many types fail to support move semantics. The
entire C++98 Standard Library was overhauled for C++11 to add move operations
for types where moving could be implemented faster than copying,
Even types with explicit move support may not benefit as much as you’d hope. All
containers in the standard C++11 library support moving, for example, but it would
be a mistake to assume that moving all containers is cheap. For some containers, this
is because there’s no truly cheap way to move their contents. For others, it’s because
the truly cheap move operations the containers offer come with caveats the container
elements can’t satisfy.
Assuming that Widget is a type
where moving is faster than copying, moving a std::array of Widget will be faster
than copying the same std::array.
On the other hand, std::string offers constant-time moves and linear-time copies.
That makes it sound like moving is faster than copying,
There are thus several scenarios in which C++11’s move semantics do you no good:
• No move operations: The object to be moved from fails to offer move opera‐
tions. The move request therefore becomes a copy request.
• Move not faster: The object to be moved from has move operations that are no
faster than its copy operations
.
• Move not usable: The context in which the moving would take place requires a
move operation that emits no exceptions, but that operation isn’t declared noex
cept.
Есть прямая связь между дорогими ресурсами и move семантикой.
Спи быстрее, твоя подушка нужна другому. Copyright Зощенко
User avatar
Prosche
Уже с Приветом
Posts: 8090
Joined: 08 Nov 2004 12:24
Location: GA

Re: А был ли мальчик? (помянем плюсы)

Post by Prosche »

Pantigalt wrote: 26 Jan 2018 18:17 Есть прямая связь между дорогими ресурсами и move семантикой.
Капитан очевидность? Мув быстрее копирования? Неожиданно :) Мув применим там где надо перекинуть ценный ресурс справа налево. Снова брейкин ньюз. Только я то тут причем? Я веселился только по поводу имплементации мув через свап. Последняя цитата убийственная, Майерс там курит чтоли? Раньше не замечал в его кнгах такого: "Вам не поможет мув, если в классе он не заимплеменчен и объект будет скопирован". Нежданчик. :D То что вы выделили тоже внушает. Если копирование заимплеменчено так же быстро как мув, то мув не быстрее копирования. Господи Иисусе! :-)
User avatar
Medium-rare
Уже с Приветом
Posts: 9195
Joined: 04 Mar 2011 03:04
Location: SFBA

Re: А был ли мальчик? (помянем плюсы)

Post by Medium-rare »

Medium-rare wrote: 25 Jan 2018 18:10 static_assert почаще применять для устранения недопониманий... Как в свой код поставить, понятно. А если хочется внятно сообщить о неправильном случае использования стандартного контейнера для конкретного типа?
Ну, как заставить компилятор сообщить о специфической ошибке невозможности использования стандартного контейнера? Как из того примера с vector<unique_ptr<T>>, где нет копирования и др. Если этого не сделать, gcc вываливает очень немалую кучку сообщений, пережевать которых ещё та работа.
... and even then it's rare that you'll be going there...
Pantigalt
Уже с Приветом
Posts: 803
Joined: 24 Jan 2007 07:32
Location: Сергели->Новосибирск->SFBA->Новосибирск->Москва->NY->SFBA

Re: А был ли мальчик? (помянем плюсы)

Post by Pantigalt »

Prosche wrote: 26 Jan 2018 18:35 мув в мув, взять справа и передвинуть влево, а совсем не сохранить "дорогие ресурсы"
Вы написали вот это спорное утверждение на что я Вам и отвечал в предыдущем посте. Видимо мне это надо было подчеркнуть.
Сохранить "дорогие ресурсы" - в этом и есть основной смысл move семантики, там где это быстрее копирования.
Prosche wrote: 26 Jan 2018 18:35 Я веселился только по поводу имплементации мув через свап.
Вы обсуждали это не со мной а с AndreyT.
Спи быстрее, твоя подушка нужна другому. Copyright Зощенко
User avatar
Prosche
Уже с Приветом
Posts: 8090
Joined: 08 Nov 2004 12:24
Location: GA

Re: А был ли мальчик? (помянем плюсы)

Post by Prosche »

Pantigalt wrote: 26 Jan 2018 19:08 Вы написали вот это спорное утверждение на что я Вам и отвечал в предыдущем посте. Видимо мне это надо было подчеркнуть.
Сохранить "дорогие ресурсы" - в этом и есть основной смысл move семантики, там где это быстрее копирования.
Понятно. Но в нем речь шла о том, чтобы сохранять дорогие ресурсы, внимание :umnik1: , находящиеся слева, путем перемещения вправо! т.е. по сути делать свап вместо мува. Возможно я не где-то это выразил неясно. Так что я совершенно согласен, что мув отлично подходит, чтобы перенести дорогой ресурс справа налево и избежать создания временных дефолтных объектов, копирования и пр.
Pantigalt
Уже с Приветом
Posts: 803
Joined: 24 Jan 2007 07:32
Location: Сергели->Новосибирск->SFBA->Новосибирск->Москва->NY->SFBA

Re: А был ли мальчик? (помянем плюсы)

Post by Pantigalt »

Кстати если кому интересно вот решил заглянуть как имплементирован std::swap и std::move в Visual Studio 2017

std::swap
template<class _Ty,
class> inline
void swap(_Ty& _Left, _Ty& _Right)
_NOEXCEPT_COND(is_nothrow_move_constructible<_Ty>::value
&& is_nothrow_move_assignable<_Ty>::value)
{ // exchange values stored at _Left and _Right
_Ty _Tmp = _STD move(_Left);
_Left = _STD move(_Right);
_Right = _STD move(_Tmp);
}
std::move
// TEMPLATE FUNCTION move
template<class _Ty>
constexpr remove_reference_t<_Ty>&&
move(_Ty&& _Arg) _NOEXCEPT
{ // forward _Arg as movable
return (static_cast<remove_reference_t<_Ty>&&>(_Arg));
}
Спи быстрее, твоя подушка нужна другому. Copyright Зощенко
User avatar
AndreyT
Уже с Приветом
Posts: 3000
Joined: 14 Apr 2004 01:11
Location: SFBA (было: Минск, Беларусь)

Re: А был ли мальчик? (помянем плюсы)

Post by AndreyT »

Pantigalt wrote: 26 Jan 2018 19:45 Кстати если кому интересно вот решил заглянуть как имплементирован std::swap и std::move в Visual Studio 2017
Не совсем понятно, что именно вы хотели увидеть. Это довольно странная комбинация, ибо `std::swap` и `std::move` - совершенно разные по своей природе сущности. Как пара из яблока и апел^H^H^H логарифмической линейки.

`std::swap` - это алгоритм физически реализующий операцию обмена значений.

`std::move` же физически не делает ничего вообще. Он не реализует никакой операции move. `std::move` - это просто другая форма записи явного каста к типу rvalue reference. А собственно физическое перемещение данных вы должны реализовывать сами, руками в той функции, которая вызовется для аргумента типа rvalue reference, полученного из `std::move`.

Строго говоря `std::move` "не нужен" - вы можете им вообще не пользоваться, а просто везде явно руками выполнять `static_cast` к типу rvalue reference (т.е. имено то, что вы увидели внутри реализации `std::move`). Но `std::move` ввели в качестве альтернативной формы записи для этого `static_cast` - просто чтобы повысить удобочитаемость и компактность кода.

---

Может быть вы и не имели это в виду, но это довольно распространненая ошибка: полагать что, мол, "`std::swap` реализует swap, а `std::move` реализует move". На самом же деле `std::swap` действительно реализует swap, а вот `std::move` не реализует ничего вообще.
Best regards,
Андрей
Pantigalt
Уже с Приветом
Posts: 803
Joined: 24 Jan 2007 07:32
Location: Сергели->Новосибирск->SFBA->Новосибирск->Москва->NY->SFBA

Re: А был ли мальчик? (помянем плюсы)

Post by Pantigalt »

AndreyT wrote: 26 Jan 2018 20:11
Pantigalt wrote: 26 Jan 2018 19:45 Кстати если кому интересно вот решил заглянуть как имплементирован std::swap и std::move в Visual Studio 2017
Не совсем понятно, что именно вы хотели увидеть. Это довольно странная комбинация, ибо `std::swap` и `std::move` - совершенно разные по своей природе сущности. Как пара из яблока и апел^H^H^H логарифмической линейки.

`std::swap` - это алгоритм физически реализующий операцию обмена значений.

`std::move` же физически не делает ничего вообще. Он не реализует никакой операции move. `std::move` - это просто другая форма записи явного каста к типу rvalue reference. А собственно физическое перемещение данных вы должны реализовывать сами, руками в той функции, которая вызовется для аргумента типа rvalue reference, полученного из `std::move`.

Строго говоря `std::move` "не нужен" - вы можете им вообще не пользоваться, а просто везде явно руками выполнять `static_cast` к типу rvalue reference (т.е. имено то, что вы увидели внутри реализации `std::move`). Но `std::move` ввели в качестве альтернативной формы записи для этого `static_cast` - просто чтобы повысить удобочитаемость и компактность кода.

---

Может быть вы и не имели это в виду, но это довольно распространненая ошибка: полагать что, мол, "`std::swap` реализует swap, а `std::move` реализует move". На самом же деле `std::swap` действительно реализует swap, а вот `std::move` не реализует ничего вообще.
Про то что std::move физически ничего не перемещает мне известно.
Я обратил внимание на другое - на move assignment.
Я как то об этом не думал что в операции swap логичнее использовать move assignment вместо copy assignment.
Хотя вроде это и очевидно.

На самом деле у меня мало практического опыта на С++ так что не удивляйтесь что какие то вещи очевидные вам не сразу очевидны мне.
Моя основная специализация C# хотя я читал много книг по C++ в свое время.

PS: К слову об очевидных вещах. В вышеприведенной реализации с 3-мя move. В первом случае для аргумента будет вызван move конструктор a для остальных move assignment.
Спи быстрее, твоя подушка нужна другому. Copyright Зощенко
Pantigalt
Уже с Приветом
Posts: 803
Joined: 24 Jan 2007 07:32
Location: Сергели->Новосибирск->SFBA->Новосибирск->Москва->NY->SFBA

Re: А был ли мальчик? (помянем плюсы)

Post by Pantigalt »

AndreyT wrote: 26 Jan 2018 20:40 Строго говоря `std::move` "не нужен" - вы можете им вообще не пользоваться, а просто везде явно руками выполнять `static_cast` к типу rvalue reference (т.е. имено то, что вы увидели внутри реализации `std::move`). Но `std::move` ввели в качестве альтернативной формы записи для этого `static_cast` - просто чтобы повысить удобочитаемость и компактность кода.
Я с Вами немного подискутирую тут.

Вы все верно сказали про то что

1. Можно явно руками выполнять `static_cast` к типу rvalue reference
2. `std::move` ввели в качестве альтернативной формы записи для этого `static_cast` - просто чтобы повысить удобочитаемость и компактность кода

Как я вижу move там оказался еще и потому что передает смысл намерений (хотя при std::move физически ничего не происходит кроме каста).
"Внимание!!! Сейчас обьект будет перемещен (будет вызван move constructor или move assignments и обьект будет передан как аргумент)."

1. Создали новый временный обьект в который "переместили" содержимое первого параметра.
2. В первый (уже пустой) обьект "переместили" содержимое второго параметра.
3. Во второй (уже пустой) обьект "переместили" содержимое временного обьекта.

Сравним это со "старым стилем".
1. Создали новый временный обьект в который "скопировали" содержимое первого параметра.
2. В первый (не пустой) обьект "скопировали" содержимое второго параметра.
3. Во второй (не пустой) обьект "скопировали" содержимое временного обьекта.

В общем виде не было возможности реализовать эффективный swap.
Спи быстрее, твоя подушка нужна другому. Copyright Зощенко
User avatar
Мальчик-Одуванчик
Уже с Приветом
Posts: 15526
Joined: 27 Sep 2007 22:53

Re: А был ли мальчик? (помянем плюсы)

Post by Мальчик-Одуванчик »

Prosche wrote: 26 Jan 2018 18:35 Я веселился только по поводу имплементации мув через свап.
Вполне себе нормальная практика имплементировать конструктор копирования && и оператор присваивания && через swap.
User avatar
Prosche
Уже с Приветом
Posts: 8090
Joined: 08 Nov 2004 12:24
Location: GA

Re: А был ли мальчик? (помянем плюсы)

Post by Prosche »

Мальчик-Одуванчик wrote: 26 Jan 2018 23:19 конструктор копирования &&
Ничего не могу ответить, на моменте представленном выше у меня мозг ушел в холодный рестарт.
User avatar
Мальчик-Одуванчик
Уже с Приветом
Posts: 15526
Joined: 27 Sep 2007 22:53

Re: А был ли мальчик? (помянем плюсы)

Post by Мальчик-Одуванчик »

Prosche wrote: 27 Jan 2018 01:01 Ничего не могу ответить, на моменте представленном выше у меня мозг ушел в холодный рестарт.
Конструктор переноса и оператор перемещающего присваивания. Лоханулся

Вот в этом примере с помощью свопа реализовано одновременно и копирующее и перемещающее присваивание.
Разумеется при наличии обеих форм конструктора копирования.
Class A {
A& operator=(A source) {
swap(*this, source);
return *this;
}
....
}
User avatar
AndreyT
Уже с Приветом
Posts: 3000
Joined: 14 Apr 2004 01:11
Location: SFBA (было: Минск, Беларусь)

Re: А был ли мальчик? (помянем плюсы)

Post by AndreyT »

Мальчик-Одуванчик wrote: 27 Jan 2018 02:22 Вот в этом примере с помощью свопа реализовано одновременно и копирующее и перемещающее присваивание.
Ну, во-первых, формальная семантика перемещения является расслабленным вариантом семантики копирования, т.е. с формальной точки зрения любое "классическое" копирование одновременно является перемещением. Поэтому ваше утверждение "реализовано одновременно и копирующее и перемещающее присваивание" является в этом смысле тавтологией.

Это отражено и в дизайне языка: тип `T &&` приводим к типу `const T &`, то если если в классе не объявлена перемещающая функция (напр. конструктор или оператор присваивания с параметром `T &&`), то тут же на помощь придет и будет вызвана аналогичная копирующая функция (напр. конструктор или оператор присваивания с параметром `const T &`). В этом нет никакого нарушения семантики перемещения.

Всякий раз, когда вы реализуете полноценное копирование для некоторого класса, вы тем самым "покрываете" и семантику перемещения для этого класса. Покрываете неэффективно, но формально корректно.

Однако, как вы сами понимаете, семантика перемещения была введена в язык не для этого, чтобы лениво использовать копирование там, где можно сделать перемещение и "радоваться, что все работает". Более расслабленные требования семантики перемещения позволяют вам реализовывать перемещение существенно более эффективно. То есть вас никто не заставляет реализовывать эффективное перемещение, вам лишь предоставляется такая возможность. Для копируемых типов семантика перемещения - это лишь опциональная optimization opportunity. А уж найти места, где более эффективная семантика перемещения заслуживает аккуратной отдельной реализации - ваша задача.
Мальчик-Одуванчик wrote: 27 Jan 2018 02:22Разумеется при наличии обеих форм конструктора копирования.
Class A {
A& operator=(A source) {
swap(*this, source);
return *this;
}
....
}
Это верно. В этом и заключается смысл уже упоминавшегося мною выше совета - если вы знаете, что внутри функции вам понадобится копия аргумента, то лучше дать компилятору сделать за вас эту копию (предав аргумент по значению), а не выполнять копирование самостоятельно внутри функции. Таким способом вы сможете одной-единственной реализацией корректно покрыть и семантику копирования, и семантику перемещения. В данном случае реализация получается весьма и весьма эффективной (отдельные реализации для копирования и для перемещения можно сделать эффективнее "на пару тактов", но в большинстве случаев это не нужно.)

(Однако эта реализация не реализует swap semantics, т.е. не занимается сохранением ресурсов левой части в правой, о котором я говорил выше. Swap здесь служит совсем другой цели.)
Best regards,
Андрей

Return to “Вопросы и новости IT”