Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Непонятно чем это лучше проактора для данной задачи.
тем что не надо заранее аллоцировать буфер
EP>По сути точно такая же неблокирующая операция, которая выполнится в будущем — нам точно также нужно сохранять текущий callstack и отдавать управление.
нет, это реактор, и операция записи/чтения блокирующая, только она выполняется моментально т.к. в сокет уже готов для чтения/записи ( коллбек зовётся при появлении возможности писать/читать )
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Собственно у меня два вопроса: EP>1. Какие есть ещё альтернативы? Возможно есть какие-то стандартные/распространённые решения?
JSON
EP>2. Встречалась ли вам такая задача? В каком контексте? И как она была решена?
Я использовал state machime aproach, передавал не сами древовидные структуры, а операции их редактирования. Правда там задача была сложнее — нужно было организовать репликацию большой, древовидной структуры данных на множество машин.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>А как отдать управление io_service'у после первой отсылки?
эм..
EP>Но сериализация в большинстве случаев будет на порядки быстрее отправки/получения чанков.
ох и не факт =)
обновил информацию о сравнительном тесте.
EP>Отправив первый чанк — мы заполним второй быстрее чем сообщение будет отправлено, и теперь у нас уже два неотправленных буфера. То есть мы вернулись к тому, с чего начали.
нужен итерационный алгоритм, с возможностью сохранения значения итератора. но ты говоришь, что тебе нужна и возможность итерировать члены класса...
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>А как отдать управление io_service'у после первой отсылки?
эм..
EP>Но сериализация в большинстве случаев будет на порядки быстрее отправки/получения чанков.
ох и не факт =)
обновил информацию о сравнительном тесте.
EP>Отправив первый чанк — мы заполним второй быстрее чем сообщение будет отправлено, и теперь у нас уже два неотправленных буфера. То есть мы вернулись к тому, с чего начали.
нужен итерационный алгоритм, с возможностью сохранения значения итератора. но ты говоришь, что тебе нужна и возможность итерировать члены класса...
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, Lazin, Вы писали:
L>JSON
хм..
вот у меня есть архив бинарного представления, и архив JSON представления — оба содержат абсолютно одни и те же данные. чем json лучше?
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, antropolog, Вы писали:
EP>>Непонятно чем это лучше проактора для данной задачи. A>тем что не надо заранее аллоцировать буфер
Это понятно.
EP>>По сути точно такая же неблокирующая операция, которая выполнится в будущем — нам точно также нужно сохранять текущий callstack и отдавать управление. A>нет, это реактор, и операция записи/чтения блокирующая, только она выполняется моментально т.к. в сокет уже готов для чтения/записи ( коллбек зовётся при появлении возможности писать/читать )
Да, но готов-то он будет потом, а не сейчас. А пока он не готов — нужно отдать управление другим, при этом не потеряв свой стэк.
Здравствуйте, Lazin, Вы писали:
EP>>Собственно у меня два вопроса: EP>>1. Какие есть ещё альтернативы? Возможно есть какие-то стандартные/распространённые решения? L>JSON
Ты про готовые XML/JSON SAX?
Да, я вспоминал это — но в данном конкретном случае спецификация формата уже есть, и он бинарный.
EP>>2. Встречалась ли вам такая задача? В каком контексте? И как она была решена? L>Я использовал state machime aproach, передавал не сами древовидные структуры, а операции их редактирования. Правда там задача была сложнее — нужно было организовать репликацию большой, древовидной структуры данных на множество машин.
А конечный автомат выписывался руками или была какая-то автоматизация?
Здравствуйте, niXman, Вы писали:
EP>>Но сериализация в большинстве случаев будет на порядки быстрее отправки/получения чанков. X>ох и не факт =) X>обновил информацию о сравнительном тесте.
Но всё же — у памяти пропускная способность десятки гигабайт в секунду. У сети же на порядок, а то и на два меньше.
Это надо как-то сильно постараться чтобы бинарная сериализация была медленней I/O.
EP>>Отправив первый чанк — мы заполним второй быстрее чем сообщение будет отправлено, и теперь у нас уже два неотправленных буфера. То есть мы вернулись к тому, с чего начали. X>нужен итерационный алгоритм, с возможностью сохранения значения итератора. но ты говоришь, что тебе нужна и возможность итерировать члены класса...
Да, именно так.
Один из вариантов это Boost.Fusion — он как раз позволяет итерировать члены класса (он и сейчас у меня итерирует, но в контексте синхронной сериализации).
Основное неудобство (использования Fusion для асинхронной сериализации, вместо корутин) в том, что для базовых типов, или каких-то особенных пользовательских (например массивы), придётся выписывать конечный автомат вручную.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Собственно у меня два вопроса: EP>1. Какие есть ещё альтернативы? Возможно есть какие-то стандартные/распространённые решения? EP>2. Встречалась ли вам такая задача? В каком контексте? И как она была решена?
browser парсит HTML в DOM асинхронно. Т.е. там происходит примерно то что ты описал.
В одном из вариантов имплементации используется push parser ( a.k.a. SAX parser ) c опять же push tokenizer'ом.
Как раз этот вот свой generator я написал для развертки pull parser в push вариант.
(это то для чего ты собираешься coroutines использовать как я понимаю).
Здравствуйте, c-smile, Вы писали:
CS>browser парсит HTML в DOM асинхронно. Т.е. там происходит примерно то что ты описал. CS>В одном из вариантов имплементации используется push parser ( a.k.a. SAX parser ) c опять же push tokenizer'ом.
Да, SAX это как раз пример подобного. Но у меня бинарная структура, со специфицированном форматом.
Apache Thrift / Protocol Buffers могли бы тоже генерировать необходимые конечные автоматы (но вроде ещё не умеют?).
CS>Как раз этот вот свой generator я написал для развертки pull parser в push вариант. CS>(это то для чего ты собираешься coroutines использовать как я понимаю).
Да — у тебя фактически stackless coroutine (кстати, подобное есть в Boost). Из-за того что она stackless — их понадобится несколько выстроенных в цепочку (по корутине на каждый узел дерева).
Причём каждый уровень должен будет знать об использовании корутин. То есть сделать полностью прозрачно для промежуточных уровней, как при использовании stackful coroutine, не получится.
Здравствуйте, niXman, Вы писали:
X>Здравствуйте, Lazin, Вы писали:
L>>JSON X>хм.. X>вот у меня есть архив бинарного представления, и архив JSON представления — оба содержат абсолютно одни и те же данные. чем json лучше?
На самом деле, JSON почти всем лучше Проще перечислить недостатки: не самое компактное представление, особенно для бинарных данных и не самая оптимальная скорость кодирования/декодирования. В остальном — одни достоинства, недаром весь интернет использует JSON и ему подобные представления, вместо бинарных архивов.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>А конечный автомат выписывался руками или была какая-то автоматизация?
Я имел ввиду подход https://en.wikipedia.org/wiki/State_machine_replication
У нас в архитектуре был большой лог событий, через который выполнялись все модификации данных (можно назвать это event sourcing). Если нужно передавать не разные версии одних и тех же структур данных, то это не очень подходит. Зависит от задачи. Часто это работает.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Во-первых, порядок двух асинхронных операций не определён. Во-вторых, async_write не атомарен, и сам состоит из нескольких операций типа async_write_some, которые могут перемешиваться с async_write_some от другого async_write.
ну это явно недостатки сетевой библиотеки, которую ты используешь. неатомарность async_write — это идеологический глюк.
EP>В момент вызова каждого async_write у нас уже должен быть готов соответствующий буфер, чего мы изначально и пытались избежать.
мы пытались избежать больших буферов, чтобы сэкономить на памяти. если у нас все чанками передается, то достаточно иметь некий пул небольших буферов
EP>Или для наглядности рассмотрим чтение — async_read. Сразу после запуска первого чтения мы не можем запустить второе, так как мы ещё первый кусок данных не распрасили.
можем, я не вижу архитектурных ограничений. опять же я не знаю, является ли десериализация потоковой. к примеру, парсинг FIX сообщений вполне себе потоковый и мы этим пользуемся, когда парсим сообщение внутри метода on_data (см. ниже)
EP>Поток в смысле отдельный thread для сериализации, который блокируется на записи/чтении?
нет, я плохо выразился. хотел подчеркнуть, что мы не делаем сериализацию одного сообщения то в одном, то во втором потоке. это вполне можно сделать в одном потоке (для разных сообщений это могут быт разные потоки)
основная проблема в том, что у тебя в голове ограничение в виде буста, а я с ним не знаком, поэтому плохо понимаем друг друга. я работаю с сетевой библиотекой, которая имеет внутри нужный пул буферов и которая не имеет read_async, зато имеет on_data(void*, size), то есть модель доставки данных другая. при этом on_data вызывается из IO completion потоков с гарантией последовательности вызова on_data
для разнообразия все же рекомендую отвлечься от буста и посмотреть как устроены асинхронные API (файлы, сеть) в винде и линуксе, быть может, там найдется более удобная модель для данной задачи
Здравствуйте, uzhas, Вы писали:
EP>>В момент вызова каждого async_write у нас уже должен быть готов соответствующий буфер, чего мы изначально и пытались избежать. U>мы пытались избежать больших буферов, чтобы сэкономить на памяти. если у нас все чанками передается, то достаточно иметь некий пул небольших буферов
Сериализация быстрее I/O — что делать когда все буферы заполнены, но ещё не отправлены? (суммарный размер буферов меньше самой структуры — мы же экономим)
EP>>Или для наглядности рассмотрим чтение — async_read. Сразу после запуска первого чтения мы не можем запустить второе, так как мы ещё первый кусок данных не распрасили. U>можем, я не вижу архитектурных ограничений. опять же я не знаю, является ли десериализация потоковой. к примеру, парсинг FIX сообщений вполне себе потоковый и мы этим пользуемся, когда парсим сообщение внутри метода on_data (см. ниже)
В FIX'е какой максимальный/типичный размер сообщения?
U>основная проблема в том, что у тебя в голове ограничение в виде буста, а я с ним не знаком, поэтому плохо понимаем друг друга. я работаю с сетевой библиотекой, которая имеет внутри нужный пул буферов и которая не имеет read_async, зато имеет on_data(void*, size), то есть модель доставки данных другая. при этом on_data вызывается из IO completion потоков с гарантией последовательности вызова on_data U>для разнообразия все же рекомендую отвлечься от буста и посмотреть как устроены асинхронные API (файлы, сеть) в винде и линуксе, быть может, там найдется более удобная модель для данной задачи
Мне кажется другое асинхронное API никак не поможет, в принципе.
Вот даже в твоём случае — с on_data. Для десериализации структуры должно быть вызвано несколько on_data, так как буфер фиксированный, а структура в него не помещается.
Как процедура сериализации уйдёт в сон пока ожидает следующую on_data? Как продолжит десереализацию в правильном состоянии когда придут данные?
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>1. Какие есть ещё альтернативы? Возможно есть какие-то стандартные/распространённые решения?
В принципе есть решение, аозволяющее вообще отказаться от буфера. Ведь данные уже и без того есть (дерево), зачем еще один буфер ? Передать бы дерево, да и дело с концом. Понятно, что просто так это невозможно, там указатели, да и лежит оно в памяти бог знает как.
А вот если это дерево и все его внутренности расположены в некотором непрерывном блоке памяти, а вместо указателей используются смещения в этом блоке памяти, то можно перебросить весь этот блок памяти целиком, не копируя ничего и никуда.
Я в свое время нечто подобное делал, для дерева с весьма нетривиальными листьями, содержащими списки и массивы переменной длины. Все это хранилось в буфере, выделенном по механизму memory-mapped files. Для сохранения этого буфера надо было просто его целиком записать в файл , а поскольку буфер был на основе mmf, то и писать ничего не требовалось, просто закрыть mmf. В следующий раз mmf вновь открывался и был сразу готов к употреблению.
Здравствуйте, Pavel Dvorkin, Вы писали:
EP>>1. Какие есть ещё альтернативы? Возможно есть какие-то стандартные/распространённые решения? PD>В принципе есть решение, аозволяющее вообще отказаться от буфера. Ведь данные уже и без того есть (дерево), зачем еще один буфер ? Передать бы дерево, да и дело с концом. Понятно, что просто так это невозможно, там указатели, да и лежит оно в памяти бог знает как. PD>А вот если это дерево и все его внутренности расположены в некотором непрерывном блоке памяти, а вместо указателей используются смещения в этом блоке памяти, то можно перебросить весь этот блок памяти целиком, не копируя ничего и никуда.
Как я уже говорил эти структуры не trivially copyable. А так да — было бы проще всего (и эффективней) сделать zero-copy.
EP>Как я уже говорил эти структуры не trivially copyable. А так да — было бы проще всего (и эффективней) сделать zero-copy.
Ты писал
>Эти структуры не trivially copyable (как минимум потому, что содержат массивы переменной длины) — следовательно zero-copy никак не получится.
Наличие массивов переменной длины помешать не может. Если есть другие, более серьезные причины — тогда да.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Наличие массивов переменной длины помешать не может. Если есть другие, более серьезные причины — тогда да.
В принципе да, но также есть и целые числа переменной длины — чтобы знать где что находится, нужно как минимум распарсить такие числа и т.п.
Меня больше интересует общий механизм, со сложной сериализацией. Часто встречаются компактные форматы в виде потока бит.
EP>В принципе да, но также есть и целые числа переменной длины — чтобы знать где что находится, нужно как минимум распарсить такие числа и т.п.
Тот факт, что их нужно распарсить, не отменяет возможности рассматривать их как массив байтов переменной длины, поэтому сводится к предыдущему.
Теоретически я вообще не вижу ситуации, когда я не могу хранить все данные некоторого объекьа в мной выделенном блоке памяти. Не могу по той простой причине, что не все ли равно где байты эти хранятся, а раз все равно, то пусть они в моем блоке и хранятся. Разумеется, такой подход требует полного контроля над аллокацией памяти.
Практически — это может быть совсем не всегда легко сделать, и не всегда применимо.
EP>Меня больше интересует общий механизм, со сложной сериализацией. Часто встречаются компактные форматы в виде потока бит.
Любой набор последовательно расположенных данных всегда можно рассматривать как массив байтов переменной длины