Клонирование экземпляров сообщений при доставке потребителям

3 views
Skip to first unread message

Yauheni Akhotnikau

unread,
Feb 12, 2009, 3:23:57 AM2/12/09
to sobje...@googlegroups.com
Доброго дня!

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

Но для SObjectizer5 можно рассмотреть подход, когда сообщение клонируется
на столько копий, сколько подписчиков на него есть. И каждый подписчик
будет получать собственную копию сообщения. Соответственно, процесс
уничтожения сообщения будет очень простой -- отработал event, экземпляр
сообщения уничтожился.

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

<остапа понесло>
А можно... А можно еще и так. Предоставить агентам возможность управлять
своей очередью сообщений и возможность делать копии исходных сообщений для
помещения в свою очередь. Например, если агент A получает три сообщения,
каждое из которых является простой POD структурой в несколько десятков
байт, то такой агент может создать специализированную очередь сообщений, с
предварительно выделенными блоками памяти, и при помещении туда очередного
сообщения вообще не будет операций new/delete для создания клона --
достаточно будет простых memcpy/memmove.
</остапа понесло>

--
Regards,
Yauheni Akhotnikau
Chief Developer
Intervale, http://www.intervale.ru
e-mail:eao...@intervale.ru <mailto:eao...@intervale.ru>

Dmitriy V'jukov

unread,
Feb 17, 2009, 2:06:29 PM2/17/09
to SObjectizer
Я много думал над этим вопросом, но так окончательно для себя не
определился, как же надо передавать сообщения.
Какие тут есть моменты:
1. Направленная посылка или широковещательная рассылка
2. Можем ли мы копировать сообщение или это крайней не желательно
(размер сообщения)
3. Используем ли мы объект, который пользователь передал в send(), или
сразу делаем копию и оставляем пользователю его объект для возможных
последующих модификаций; либо мы вообще "отнимаем" объект у
пользователя, обнуляя его указатель.
4. Разрешено ли пользователю форвардить дальше или запоминать для
последующего использования полученное сообщение
5. Разрешено ли пользователю модифицировать полученное сообщение
6. Выбираем ли мы только какой-то один вариант передачи или
предоставляем несколько; если несколько, то как это должно выглядеть
для пользователя

С чем я для себя определился, так это с тем, что тут надо делать свой
slab аллокатор памяти для агентов/сообщений/эвентов, т.к. в каких-бы
частных случаях мы не пытались избежать new/delete, всё равно будут
такие паттерны использования, которые будут требовать постоянных new/
delete. Во-вторых, требуется собственный механизм управления временем
жизни агентов/сообщений по аналогичной причине; а атомарный подсчёт
ссылок - это анти-паттерн производительности. А если у нас есть эти 2
оптимизированных механизма, то это сводит на нет попытки оптимировать
маленькие сообщения, т.к. практически ничего не выиграем, зато создаём
проблем и пользователю и себе.

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

Так же, имхо, очень важный момент это - поддержка "передачи владения"
сообщением между агентами (или uniquiness types в ФП). При передаче
владения отправитель "отказывается" от сообщения, но зато получатель
получает сообщение в полное распоряжение (т.е. может модифицировать
его inplace и передать дальше). Правда тут вопрос - как это должно
выглядеть для пользователя, и что делать, если идёт широковещательная
рассылка.

По поводу собственного аллокатора памяти. Тут ничего особенного нет,
есть достаточно стандартный дизайн т.н. distributed slab allocator,
который тут идеально подходит. Кратко - у каждого потока есть
собственный набор fixed-size аллокаторов и работа с ними очень
эффективна. Например такой аллокатор есть в TBB - scalable_alloc.

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

Если попробовать собрать всё это воедино, то получается примерно
следующая картина.
Отказываемся от оптимизации inplace хранения маленьких объектов (без
new/delete), т.к. эффективный аллокатор это решает. Отказываемся от
оптимизации копирования сообщений, т.к. эффективное управление
временем жизни объектов это решает. Всем получателям идёт
непосредственно указатель на объект переданный в send(), но получатели
получают его уже как указатель на константный объект handle<my_msg
const>, при этом они могут его запоминать и передавать дальше. Хотя
тут есть небольшая засада:
handle<my_msg> m = my_msg::create(...);
m->x += 1;
target->send(m);
m->x += 1; // запрещено! сообщение уже может обрабатываться другим
потоком.
Хорошего варианта как это решить я не вижу. Можно либо вообще
запретить все модификации сообщения после отработки его конструктора
(слишком ограничивает), либо делать динамическую проверку, что если мы
пытаемся у handle<my_msg> вызвать неконстантный operator->(), и
счётчик ссылок сообщения больше 1, то давать ассёрт (плохо, т.к.
проверка только в ран-тайм и может не отлавливать ошибки).
Дальше, при подписке на сообщение агент может указать, что ему нужно
не константное сообщение, а модифицируемое; тогда если агент-
отправитель передаёт владение сообщением, то копии не делается; если
же агент-отправитель сам продолжает держать ссылку на сообщение, либо
делает отправку нескольким агентам, то делаем копию сообщения, дабы
вручить получателю модифицируемое сообщение.
Что я тут подразумеваю под передачей владения. Вариант 1:
target->send(my_msg::create(...)); // можно определить, что в send()
передаётся временный объект со счётчиком ссылок 1 и "украсть" его
Вариант 2:
my_msg::handle m = my_msg::create(...);
...m...
target->send(move(m)); // явно отказываемся от владения
Вобщем пользователю всегда рекомендуется использовать передачу
владения, т.к. это более эффективно.
Вроде всё... Вот так я сейчас это вижу.

Yauheni Akhotnikau

unread,
Feb 18, 2009, 3:35:45 AM2/18/09
to sobje...@googlegroups.com
On Tue, 17 Feb 2009 22:06:29 +0300, Dmitriy V'jukov <dvy...@gmail.com>
wrote:

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

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

> Какие тут есть моменты:
> 1. Направленная посылка или широковещательная рассылка

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

> 2. Можем ли мы копировать сообщение или это крайней не желательно
> (размер сообщения)

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

> 3. Используем ли мы объект, который пользователь передал в send(), или
> сразу делаем копию и оставляем пользователю его объект для возможных
> последующих модификаций; либо мы вообще "отнимаем" объект у
> пользователя, обнуляя его указатель.

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

> 4. Разрешено ли пользователю форвардить дальше или запоминать для
> последующего использования полученное сообщение

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

> 5. Разрешено ли пользователю модифицировать полученное сообщение

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

> 6. Выбираем ли мы только какой-то один вариант передачи или
> предоставляем несколько; если несколько, то как это должно выглядеть
> для пользователя

Здесь же опять я повторюсь: вряд ли имеет смысл давать пользователю
какую-то большую свободу -- SObjectizer работает либо так, либо так. Все.

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

<... технические подробности про аллокаторы после прочтения поскипаны ...>

Yauheni Akhotnikau

unread,
Feb 18, 2009, 3:35:47 AM2/18/09
to sobje...@googlegroups.com
On Tue, 17 Feb 2009 22:06:29 +0300, Dmitriy V'jukov <dvy...@gmail.com>
wrote:

> Отказываемся от оптимизации inplace хранения маленьких объектов (без


> new/delete), т.к. эффективный аллокатор это решает.

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

> Отказываемся от
> оптимизации копирования сообщений, т.к. эффективное управление
> временем жизни объектов это решает.

А вот с этого момента поподробнее, пожалуйста :)
Что такое эффективное управление временем жизни объектов? Сборка мусора?

Dmitriy V'jukov

unread,
Feb 18, 2009, 5:22:03 AM2/18/09
to SObjectizer
On 18 фев, 11:35, "Yauheni Akhotnikau" <eao...@intervale.ru> wrote:
> On Tue, 17 Feb 2009 22:06:29 +0300, Dmitriy V'jukov <dvyu...@gmail.com>

> wrote:
>
> > Я много думал над этим вопросом, но так окончательно для себя не
> > определился, как же надо передавать сообщения.
>
> Ну я как раз поднял эту тему для того, чтобы определиться.
>
> > Какие тут есть моменты:
> > 1. Направленная посылка или широковещательная рассылка
>
> В случае направленной посылки ситуация достаточно проста -- нам не нужно
> ничего клонировать. Есть единственный экземпляр сообщения, который
> уничтожается после обработки.


Т.е. запрещается:

my_msg* m = new my_msg (...);
...
agent1->send(m);
...
agent2->send(m);

?


> При широковещательной рассылке выполняется
> клонирование.

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

Dmitriy V'jukov

unread,
Feb 18, 2009, 5:31:29 AM2/18/09
to SObjectizer
On 18 фев, 11:35, "Yauheni Akhotnikau" <eao...@intervale.ru> wrote:

> > 2. Можем ли мы копировать сообщение или это крайней не желательно
> > (размер сообщения)
>
> При использовании клонирования это решается тем, что в сообщении не
> передаются "тяжелые" данные, а только какой-то их дескриптор. Например,
> shared_ptr.


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


> > 3. Используем ли мы объект, который пользователь передал в send(), или
> > сразу делаем копию и оставляем пользователю его объект для возможных
> > последующих модификаций; либо мы вообще "отнимаем" объект у
> > пользователя, обнуляя его указатель.
>
> Имхо, здесь не нужно давать пользователю какого-то большого выбора. В
> случае, если мы идем на клонирование сообщений, мы можем выбрать один из
> вариантов:
> - пользователь передает в send указатель на динамически созданный объект.
> После обращения к send этим указателем пользователь пользоваться уже не
> может. В данном случае мы не делаем копии сообщения при целенаправленной
> посылке -- достаточно использовать оригинальный объект;
> - пользователь передает в send константную ссылку на объект
> (предполагается, что этот объект временный объект на стеке). В данном
> случае внутри send мы делаем копию объекта.


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


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

Dmitriy V'jukov

unread,
Feb 18, 2009, 5:35:55 AM2/18/09
to SObjectizer
On 18 фев, 11:35, "Yauheni Akhotnikau" <eao...@intervale.ru> wrote:

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


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

Dmitriy V'jukov

unread,
Feb 18, 2009, 5:38:40 AM2/18/09
to SObjectizer
On 18 фев, 11:35, "Yauheni Akhotnikau" <eao...@intervale.ru> wrote:

> > Отказываемся от оптимизации inplace хранения маленьких объектов (без
> > new/delete), т.к. эффективный аллокатор это решает.
>
> Аллокатор решает только проблему памяти, но не оптимизированной под  
> какой-то тип объектов очереди сообщений.


А в чём оптимизация очереди заключается кроме желания съэкономить на
new/delete?


> > Отказываемся от
> > оптимизации копирования сообщений, т.к. эффективное управление
> > временем жизни объектов это решает.
>
> А вот с этого момента поподробнее, пожалуйста :)
> Что такое эффективное управление временем жизни объектов? Сборка мусора?


Да, что-то типа сборки мусора.

Yauheni Akhotnikau

unread,
Feb 18, 2009, 7:12:42 AM2/18/09
to sobje...@googlegroups.com
On Wed, 18 Feb 2009 13:22:03 +0300, Dmitriy V'jukov <dvy...@gmail.com>
wrote:

>> В случае направленной посылки ситуация достаточно проста -- нам не нужно


>> ничего клонировать. Есть единственный экземпляр сообщения, который
>> уничтожается после обработки.
>
>
> Т.е. запрещается:
>
> my_msg* m = new my_msg (...);
> ...
> agent1->send(m);
> ...
> agent2->send(m);
>
> ?

Да. Это именно так сейчас.

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

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

В языке D 2.0 это бы замечательно решилось с помощью инвариантных данных
-- есть там такой спецификатор invariant. Он означает, что никто не может
изменить данные. В C++ такого спецификатора нет, зато можно делать копии.

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

Yauheni Akhotnikau

unread,
Feb 18, 2009, 7:20:17 AM2/18/09
to sobje...@googlegroups.com
On Wed, 18 Feb 2009 13:31:29 +0300, Dmitriy V'jukov <dvy...@gmail.com>
wrote:

>> При использовании клонирования это решается тем, что в сообщении не


>> передаются "тяжелые" данные, а только какой-то их дескриптор. Например,
>> shared_ptr.
>
> Так это получается шыло на мыло. Если уж мы не решили проблему
> эффективного разделения данных, то на пользователя надежды совсем
> мало... Был у нас неэффективный подсчёт ссылок, теперь он будет у
> пользователя...

Ну раньше неэффективный подсчет ссылок был всегда. А теперь -- только для
тяжелых сообщений.

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

Да, невозможность форварда того же самого сообщения -- это временами в
SObjectizer-4 напрягает.

Yauheni Akhotnikau

unread,
Feb 18, 2009, 7:24:42 AM2/18/09
to sobje...@googlegroups.com
On Wed, 18 Feb 2009 13:35:55 +0300, Dmitriy V'jukov <dvy...@gmail.com>
wrote:

> On 18 фев, 11:35, "Yauheni Akhotnikau" <eao...@intervale.ru> wrote:

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

На счет форварда -- было бы хорошо эту возможность иметь. Но вопрос в том,
нужно ли это делать через умные указатели. У меня для SObjectizer-4 была
мысль сделать форвардинг так:

void my_agent::evt_some_event(
const so_4::rt::event_data_t & event_data,
const msg_some_data & cmd ) {
...
// Решаем переслать cmd дальше в виде другого сообщения.
event_data.forward( "owner_name", "message_name" );
}

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

Yauheni Akhotnikau

unread,
Feb 18, 2009, 7:28:46 AM2/18/09
to sobje...@googlegroups.com
On Wed, 18 Feb 2009 13:38:40 +0300, Dmitriy V'jukov <dvy...@gmail.com>
wrote:

>> > Отказываемся от оптимизации inplace хранения маленьких объектов (без
>> > new/delete), т.к. эффективный аллокатор это решает.
>>
>> Аллокатор решает только проблему памяти, но не оптимизированной под  
>> какой-то тип объектов очереди сообщений.
>
> А в чём оптимизация очереди заключается кроме желания съэкономить на
> new/delete?

Ну изначально про собственные очереди сообщений для агентов я заговорил с
прицелом на soft-real-time задачи. Там важна не столько эффективность,
сколько предсказуемость. И операции с динамической памятью оказываются
"вне закона", поскольку они не дают строгих гарантий на время своей
работы. Поэтому в real-time часто работают с предварительно
преаллоцированной памятью. А SObjectizer-4 пролетал мимо real-time задач
как фанера над Прижем.

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

Dmitriy V'jukov

unread,
Feb 18, 2009, 2:06:15 PM2/18/09
to SObjectizer
On 18 фев, 15:12, "Yauheni Akhotnikau" <eao...@intervale.ru> wrote:

> >> В случае направленной посылки ситуация достаточно проста -- нам не нужно
> >> ничего клонировать. Есть единственный экземпляр сообщения, который
> >> уничтожается после обработки.
>
> > Т.е. запрещается:
>
> > my_msg* m = new my_msg (...);
> > ...
> > agent1->send(m);
> > ...
> > agent2->send(m);
>
> > ?
>
> Да. Это именно так сейчас.


Ты хочешь это оставить?


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


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


> Имхо, важнее  
> вопрос удобства разработчика: если мы гарантируем, что экземпляр сообщения  
> уникален для получателя и не может измениться, то это очень здорово.
>
> В языке D 2.0 это бы замечательно решилось с помощью инвариантных данных  
> -- есть там такой спецификатор invariant. Он означает, что никто не может  
> изменить данные. В C++ такого спецификатора нет, зато можно делать копии.
>
> Хотя да, с точки зрения производительности -- клонирование является очень  

> сильным ударом...

... а такой принцип будет заставлять библиотеку и пользователя делать
копии направо и налево. Хочешь отправить одно сообщение нескольким
получателям - копируй, делаешь широковещательную рассылку - библиотека
за тебя будет копировать, хочешь отфорвардить сообщение без изменений
- копируй. При этом все эти копии совершенно бесполезные, т.е. это
лишь дань такому построению библиотеки, никакой полезной работы для
пользователя не совершается.
Моё личное мнение, что тут не стоит равнять на Эрланг. А в С++ всё
равно 1000 и 1 способ отстрелить себе ногу, поэтому я считаю, что надо
хотя бы выгоду с этого получать.
По поводу константности, мне кажется, будет достаточно заставлять
пользователя иметь сигнатуру:
void my_agent::on_msg(my_msg::ptr m);
когда мы не собираемся менять сообщение, и:
void my_agent::on_msg(my_msg::mutable_ptr m);
в противном случае.

Dmitriy V'jukov

unread,
Feb 18, 2009, 2:10:10 PM2/18/09
to SObjectizer
On 18 фев, 15:20, "Yauheni Akhotnikau" <eao...@intervale.ru> wrote:
>
> >> При использовании клонирования это решается тем, что в сообщении не
> >> передаются "тяжелые" данные, а только какой-то их дескриптор. Например,
> >> shared_ptr.
>
> > Так это получается шыло на мыло. Если уж мы не решили проблему
> > эффективного разделения данных, то на пользователя надежды совсем
> > мало... Был у нас неэффективный подсчёт ссылок, теперь он будет у
> > пользователя...
>
> Ну раньше неэффективный подсчет ссылок был всегда. А теперь -- только для  
> тяжелых сообщений.


А если у нас 90% сообщений тяжелые? Вполне вероятный сценарий.
Допустим пайплайн обработка потока сообщений, в каждом сообщении лежит
std::vector<char> значительного размера. Интенсивный атомарный подсчёт
ссылок убьёт всю систему наповал.

Dmitriy V'jukov

unread,
Feb 18, 2009, 2:18:14 PM2/18/09
to SObjectizer


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

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

Dmitriy V'jukov

unread,
Feb 18, 2009, 2:23:49 PM2/18/09
to SObjectizer
On 18 фев, 15:24, "Yauheni Akhotnikau" <eao...@intervale.ru> wrote:
>
> >> > 4. Разрешено ли пользователю форвардить дальше или запоминать для
> >> > последующего использования полученное сообщение
>
> >> Если SObjectizer отвечает за время жизни экземпляра сообщения, то при
> >> любом раскладе у пользователя очень небольшой выбор -- он не может
> >> сохранять у себя указатель на сообщение, полученный от SObjectizer.
>
> > Отнюдь. Если мы ему даём умный указатель на сообщение, то он может его
> > запоминать и форвардить дальше.
>
> Ну это при условии, что у нас будет очень эффективный умный указатель на  
> экземпляр сообщения. С защитой от многопоточности.


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


> На счет форварда -- было бы хорошо эту возможность иметь. Но вопрос в том,  
> нужно ли это делать через умные указатели. У меня для SObjectizer-4 была  
> мысль сделать форвардинг так:
>
> void my_agent::evt_some_event(
>    const so_4::rt::event_data_t & event_data,
>    const msg_some_data & cmd ) {
>    ...
>    // Решаем переслать cmd дальше в виде другого сообщения.
>    event_data.forward( "owner_name", "message_name" );
> }


Главное, что бы 2 подфункции не решили отфорвардить одновременно :)


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


А почему ты не уверен?

Dmitriy V'jukov

unread,
Feb 18, 2009, 2:45:39 PM2/18/09
to SObjectizer
On 18 фев, 15:28, "Yauheni Akhotnikau" <eao...@intervale.ru> wrote:
>
> >> > Отказываемся от оптимизации inplace хранения маленьких объектов (без
> >> > new/delete), т.к. эффективный аллокатор это решает.
>
> >> Аллокатор решает только проблему памяти, но не оптимизированной под  
> >> какой-то тип объектов очереди сообщений.
>
> > А в чём оптимизация очереди заключается кроме желания съэкономить на
> > new/delete?
>
> Ну изначально про собственные очереди сообщений для агентов я заговорил с  
> прицелом на soft-real-time задачи. Там важна не столько эффективность,  
> сколько предсказуемость. И операции с динамической памятью оказываются  
> "вне закона", поскольку они не дают строгих гарантий на время своей  
> работы. Поэтому в real-time часто работают с предварительно  
> преаллоцированной памятью. А SObjectizer-4 пролетал мимо real-time задач  
> как фанера над Прижем.
>
> А вот если дать агентам возможность иметь собственные очереди, в которых  
> все находится под контролем разработчика, то у SObjectizer появляется шанс  
> быть использованным в этой области.


С рил-тайм системами не работал, не могу сказать стоит ли на них
целиться и какой там "рынок". Однако, я думаю, что (1) не имеет смысл
делать какую-то половинчатую поддержку, тогда идея рил-тайм должна
быть первазивной - т.е. обязательно делать много приоритетов (удар по
производительности для всех), вводить дед-лайны для сообщений,
планировать на основе этих дед-лайнов и т.д. (2) во многих ситуациях
даже обычное обращение к памяти может вылиться в page fault и задержку
в 10 мс, ну или просто череда промахов в кэше (проход по связанному
списку, особенно если его формировали несколько потоков) может занять
значительно больше, чем динамическое выделение памяти.

Yauheni Akhotnikau

unread,
Feb 19, 2009, 2:27:23 AM2/19/09
to sobje...@googlegroups.com
On Wed, 18 Feb 2009 22:06:15 +0300, Dmitriy V'jukov <dvy...@gmail.com>
wrote:

> On 18 фев, 15:12, "Yauheni Akhotnikau" <eao...@intervale.ru> wrote:
>
>> > Т.е. запрещается:
>>
>> > my_msg* m = new my_msg (...);
>> > ...
>> > agent1->send(m);
>> > ...
>> > agent2->send(m);
>>
>> > ?
>>
>> Да. Это именно так сейчас.
>
> Ты хочешь это оставить?

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

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

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

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

Yauheni Akhotnikau

unread,
Feb 19, 2009, 2:27:24 AM2/19/09
to sobje...@googlegroups.com
On Wed, 18 Feb 2009 22:18:14 +0300, Dmitriy V'jukov <dvy...@gmail.com>
wrote:

> Да, вот, кстати, ты сам говоришь, что для тяжёлых сообщений


> предлагаемый способ - это оборачивать тяжёлые данные в shared_ptr. И
> что получается в итоге? Каждому пришла бессмысленная копия "обёртки"
> сообщения, а сами данные так и остались разделяемыми. Даже хуже, чем
> было - у пользователя создаётся иллюзия и привыкание, что типа у меня
> копия, значит могу делать с ней, что хочу.

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

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

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

Yauheni Akhotnikau

unread,
Feb 19, 2009, 2:27:25 AM2/19/09
to sobje...@googlegroups.com
On Wed, 18 Feb 2009 22:45:39 +0300, Dmitriy V'jukov <dvy...@gmail.com>
wrote:

> С рил-тайм системами не работал, не могу сказать стоит ли на них


> целиться и какой там "рынок". Однако, я думаю, что (1) не имеет смысл
> делать какую-то половинчатую поддержку, тогда идея рил-тайм должна
> быть первазивной - т.е. обязательно делать много приоритетов (удар по
> производительности для всех), вводить дед-лайны для сообщений,
> планировать на основе этих дед-лайнов и т.д. (2) во многих ситуациях
> даже обычное обращение к памяти может вылиться в page fault и задержку
> в 10 мс, ну или просто череда промахов в кэше (проход по связанному
> списку, особенно если его формировали несколько потоков) может занять
> значительно больше, чем динамическое выделение памяти.

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

А поддержка приоритетов -- это задача диспетчера. Задача ядра заключается
в поддержке словаря агентов/сообщений/событий и обработка операций send (с
контролем перегрузки и пр.). Т.е., грубо говоря, ядро всего лишь
предоставляет операцию send, внутри которой оно находит агента-получателя
и диспетчера, который обслуживает получателя, и отдает экземпляр сообщения
им на растерзание. А остальное -- это проблемы пользователя: он может
написать диспетчер, который будет поддерживать 128 приоритетов, и агентов,
которые имею свои реализации очередей сообщений на преаллоцированной
памяти.

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

Yauheni Akhotnikau

unread,
Feb 19, 2009, 2:27:25 AM2/19/09
to sobje...@googlegroups.com
On Wed, 18 Feb 2009 22:23:49 +0300, Dmitriy V'jukov <dvy...@gmail.com>
wrote:

>> На счет форварда -- было бы хорошо эту возможность иметь. Но вопрос в

>> том,  
>> нужно ли это делать через умные указатели. У меня для SObjectizer-4
>> была  
>> мысль сделать форвардинг так:
>>
>> void my_agent::evt_some_event(
>>    const so_4::rt::event_data_t & event_data,
>>    const msg_some_data & cmd ) {
>>    ...
>>    // Решаем переслать cmd дальше в виде другого сообщения.
>>    event_data.forward( "owner_name", "message_name" );
>> }
>
> Главное, что бы 2 подфункции не решили отфорвардить одновременно :)

А какие в этом проблемы? Даже этот evt_some_event мог бы вызывать forward
несколько раз -- для экземпляра сообщения просто бы увеличился счетчик
ссылок и все. Поэтому повторные forward-ы у получателей этого сообщения
так же были бы легальны.

>> На счет того, чтобы пользователь мог у себя сохранять ссылку на
>> экземпляр  
>> сообщения... Не уверен. По мне проще заставить пользователя скопировать
>> нужные ему данные.
>
> А почему ты не уверен?

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

Так что, если и позволять сохранять ссылки на сообщения, то только в виде
константных ссылок. Но, с учетом того, что в C++ любая константность
снимается const_cast-ом на раз, то и здесь пользователи могут поиметь
проблемы.

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

Reply all
Reply to author
Forward
0 new messages