С .net я только начинаю знакомиться, но с сериализацией я знаком давно
по MFC. И там еще у меня возник вопрос, на который я ответа не нашел.
Предполагаю, что и здесь его нет, но все же, может, кто-то скажет
интересное.
Для понятности задачу слегка упростил. В действительности все было
намного сложнее.
Есть класс — двумерная матрица. Матрица сильно разрежена. Ее надо
сохранить, но нули сохранять не надо.
Для сохранения предлагется простейший формат
Целое N3 — число троек.
Первая тройка — i,j, value
Вторая тройка — i,j, value
И т.д.
Насколько я понимаю, здесь надо сериализоваться с написанием собственной
ISerializable.GetObjectData
и специального конструктора. Хорошо.
Проблема же вот в чем. Я не знаю N3. Я его узнаю, только когда
просканирую матрицу и подсчитаю число ненулевых элементов. А вывести его
надо сначала, иначе потом я не при десериализации не буду знать, сколько
троек читать.
(Конечно, есть вариант с записью в конец ограничителя — чего-то вроде
(0,0,0). Здесь он проходит, но, напоминаю, исходная задача была намного
сложнее. Так что этот вариант не рассматривается).
Получается, что алгоритм двухпроходной. На первом проходе находим N3, на
втором — записываем тройки.
Если бы вместо сериализации я просто в файл писал — алгоритм
однопроходной. Пропускаем в файле 4 байта для N3, записываем тройки
(одновременно считаем N3) , позиционируемся назад к позиции N3,
записываем ее.
Можно ли сделать алгоритм однопроходным при сериализации ?
Holmes wrote: > > Pavel, > > А как насчет варианта заранее иметь количество заполненных ячеек в матрице?
Согласен, в данном случае это возможно, но , напоминаю, исходная задача
была намного сложнее. Если хочешь, немного деталей. Матрица была
трехмерной, и вертикали в ней состояли из ненулевых кусков, вот эти
куски надо было и выводить, причем при выводе анализировалось, не был ли
такой же кусок (т.е того же размера и с такими же значениями) выведен
раньше в другой вертикали, и если да — он не выводился, а делалась
ссылка на его позицию в файле. Это все несколько минут занимало . Что
поделать, размер матрицы был огромный (порядка 5000*5000*200) и в ОП она
никогда целиком не хранилась.
PD>Если бы вместо сериализации я просто в файл писал — алгоритм PD>однопроходной. Пропускаем в файле 4 байта для N3, записываем тройки PD>(одновременно считаем N3) , позиционируемся назад к позиции N3, PD>записываем ее.
Как насчет во время сериализации записать все что нужно в ArrayList и сериализовать его?
Apollo13 wrote: > > Здравствуйте, Pavel Dvorkin, Вы писали: > > PD>Если бы вместо сериализации я просто в файл писал — алгоритм > PD>однопроходной. Пропускаем в файле 4 байта для N3, записываем тройки > PD>(одновременно считаем N3) , позиционируемся назад к позиции N3, > PD>записываем ее. > > Как насчет во время сериализации записать все что нужно в ArrayList и сериализовать его?
Ох нет, только не это . Там десятки, если не сотни Мб. См. пояснение
в моем ответе Holmes
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Матрица была PD>трехмерной, и вертикали в ней состояли из ненулевых кусков, вот эти PD>куски надо было и выводить, причем при выводе анализировалось, не был ли PD>такой же кусок (т.е того же размера и с такими же значениями) выведен PD>раньше в другой вертикали, и если да — он не выводился, а делалась PD>ссылка на его позицию в файле.
Мне кажется при таком расскладе самым разумным было бы сделать хэш-таблицу хранящую ссылки на элементы матрицы (индекс или еще что), так как иначе время поиска дубликата будет слишком велико. Ну, и потом серализовать эту хэш-таблицу.
PD> Это все несколько минут занимало . Что PD>поделать, размер матрицы был огромный (порядка 5000*5000*200) и в ОП она PD>никогда целиком не хранилась.
А насколько она разрежена? Может быть есть смысл эмулировать матрицу на хэш-таблице? Например, в экселе делается именно так. Правда с трехмерной прийдется повозиться, но все же.
... << RSDN@Home 1.1.4 beta 2 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VladD2 wrote: > > Здравствуйте, Pavel Dvorkin, Вы писали:
<skipped>
> А насколько она разрежена? Может быть есть смысл эмулировать матрицу на хэш-таблице? Например, в экселе делается именно так. Правда с трехмерной прийдется повозиться, но все же.
Похоже, дело свелось к обсуждению того, как лучше эту матрицу хранить и
выводить. Не это меня интересует, а другое.
Можно ли при сериализации с собственной реализацией GetObjectData
передвигаться в потоке ? Т.е. оставить место , а потом к нему вернуться
и записать значение. Например
Оставить место для A
Вывести B
Вывести C
Вывести D
Вернуться и записать A
Вернуться в позицию после D
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Похоже, дело свелось к обсуждению того, как лучше эту матрицу хранить и PD>выводить. Не это меня интересует, а другое.
Дык именно неверное представление в первую очередь бросается в глаза.
PD>Можно ли при сериализации с собственной реализацией GetObjectData PD>передвигаться в потоке ? Т.е. оставить место , а потом к нему вернуться PD>и записать значение.
Ты волен создать свой поток и делать с ним что душа пожелает. Бинари- и Соап-форматер принимают именованные значения через SerializationInfo.AddValue(). Подсунь туда свой массив байтов или стрим.
... << RSDN@Home 1.1.4 beta 2 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD> ...[skipped]...
PD>Есть класс — двумерная матрица. Матрица сильно разрежена. Ее надо PD>сохранить, но нули сохранять не надо.
PD>Насколько я понимаю, здесь надо сериализоваться с написанием собственной PD>ISerializable.GetObjectData PD> и специального конструктора. Хорошо.
При сериализации в Net в поток пишешь не ты. Собственно... порядок параметров не важен.
Сделай так:
ArrayList rowsList = new ArrayList(/*~N*/);
.... // добавление троек.
int[][] rows = rowsList.ToArray(typeof(int[])) as int[][];
или
int[,] rows = ....
info.AddValue("rows", rows);
ну а N, вообще говоря, теперь и не нужно никуда писать.
VladD2 wrote: > > Здравствуйте, Pavel Dvorkin, Вы писали: > > PD>Похоже, дело свелось к обсуждению того, как лучше эту матрицу хранить и > PD>выводить. Не это меня интересует, а другое. > > Дык именно неверное представление в первую очередь бросается в глаза.
Ну и бог с ним . Я ведь не об этом спрашиваю, а о сериализации.
> > PD>Можно ли при сериализации с собственной реализацией GetObjectData > PD>передвигаться в потоке ? Т.е. оставить место , а потом к нему вернуться > PD>и записать значение. > > Ты волен создать свой поток и делать с ним что душа пожелает. Бинари- и Соап-форматер принимают именованные значения через SerializationInfo.AddValue(). Подсунь туда свой массив байтов или стрим.
А вот здесь, если можно, поподробнее. Мне самому вчера эта мысль в
голову пришла. Идея следующая
Открываем свой поток
Класс имплементирует ISerializable
В GetObjectData делаю следующее
AddValue(произвольное целое)
Position1 = Stream.Position // запомнили позицию
Теперь в цикле прохожу матрицу и заношу по AddValue все тройки и считаю
их количество — N3
Position2 = Stream.Position // запомнили позицию
Stream.Position = Position1
AddValue(N3) // записали вместо произвольного целого N3
Stream.Position = Position2 // и вернулись в конец
Lloyd wrote: > > Здравствуйте, Pavel Dvorkin, Вы писали: > > А почему бы просто не писать в MemoryStream. А после просто вызвать метод GetBuffer и получить все что ты туда записал в виде массива байт.
Не совсем понял. MemoryStream, как я понимаю, находится в ОП, так ? Т.е.
ты предлагаешь сначала туда записать, а потом взять байты и их
сериализовать ? Это решение не пройдет — объем данных ОЧЕНЬ большой и
НИКАКОЕ копирование их по ходу сериализации просто недопустимо.
Я понимаю (даже с моим знанием .net , что можно придумать еще много
других решений. Вообще, если этот вопрос рассматривать чисто
практически, то разумное решение находится за 1 минуту — выкинуть это N3
вообще, а в конце записать тройку (0,0,0) . Мой вопрос в другом —
можно ли перемещаться по потоку при сериализации ? Иными словами ,
сериализация — строго последовательный алгоритм или все же можно в нем
позиционироваться ?
См. также мой ответ VladD2
Poudy wrote: > > Здравствуйте, Pavel Dvorkin, Вы писали: > > PD> ...[skipped]... > > PD>Есть класс — двумерная матрица. Матрица сильно разрежена. Ее надо > PD>сохранить, но нули сохранять не надо. > > PD>Насколько я понимаю, здесь надо сериализоваться с написанием собственной > PD>ISerializable.GetObjectData > PD> и специального конструктора. Хорошо. > При сериализации в Net в поток пишешь не ты. Собственно... порядок параметров не важен. > > Сделай так: > > ArrayList rowsList = new ArrayList(/*~N*/); > .... // добавление троек.
Никакое копирование при сериализации недопустимо по причине ограничений
по памяти. См. также мои другие сегодняшние ответы.
Здравствуйте, Pavel Dvorkin, Вы писали:
>> А насколько она разрежена? Может быть есть смысл эмулировать матрицу на хэш-таблице? Например, в экселе делается именно так. Правда с трехмерной прийдется повозиться, но все же.
PD>Похоже, дело свелось к обсуждению того, как лучше эту матрицу хранить и PD>выводить.
Потому что проблема именно в этом.
PD>Можно ли при сериализации с собственной реализацией GetObjectData PD>передвигаться в потоке ?
Нет, потому что не любой поток допускает позиционирование.
AndrewVK wrote: > > Здравствуйте, Pavel Dvorkin, Вы писали: > > >> А насколько она разрежена? Может быть есть смысл эмулировать матрицу на хэш-таблице? Например, в экселе делается именно так. Правда с трехмерной прийдется повозиться, но все же. > > PD>Похоже, дело свелось к обсуждению того, как лучше эту матрицу хранить и > PD>выводить. > > Потому что проблема именно в этом. > > PD>Можно ли при сериализации с собственной реализацией GetObjectData > PD>передвигаться в потоке ? > > Нет, потому что не любой поток допускает позиционирование.
А если в GetObjectData проверять контекст и разрешать, только если это
File ?
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>А если в GetObjectData проверять контекст и разрешать, только если это PD>File ?
Боюсь механика кастомной сериализации универсальная и на подобные извращения не рассчитана. Вобщем тебе правильно тут советуют — либо храни свою матрицу ввиде хешей (а для разреженной матрицы именно этот способ хранения является оптимальным), либо формируй список вершин со значениями в памяти целиком.
AndrewVK wrote: > > Здравствуйте, Pavel Dvorkin, Вы писали:
> Боюсь механика кастомной сериализации универсальная и на подобные извращения не рассчитана. Вобщем тебе правильно тут советуют — либо храни свою матрицу ввиде хешей (а для разреженной матрицы именно этот способ хранения является оптимальным), либо формируй список вершин со значениями в памяти целиком.
Понятно. Все это не проходит, так как матрица была взята только для
пояснения сути проблемы, реальную проблему я описал в одном из ответов.
Ну что же, раз нельзя, значит, нельзя.
--
With best regards,
Pavel Dvorkin
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Это корректно ?
Просто сохрани нужное тебе число с отдельным именем. А с другим именем сохрани ссылку на стрим. Когда будешь читать, то читай и то и другое, а потом уже читай свой стрим.
... << RSDN@Home 1.1.4 beta 2 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Не совсем понял. MemoryStream, как я понимаю, находится в ОП, так ? Т.е. PD>ты предлагаешь сначала туда записать, а потом взять байты и их PD>сериализовать ? Это решение не пройдет — объем данных ОЧЕНЬ большой и PD>НИКАКОЕ копирование их по ходу сериализации просто недопустимо.
Думаю ты преувеличивашь проблему. Объем данных будет значительно меньше твоей матрицы. К тму же есть своп. Если что ОС займет место в нем.
В конце концов сегда вместоа стрима можно использовать дисковый файл и стрин на его основе.
Создать простую реализацию тебе ничего не стоит. Попробуй, а потом будеш делать выводы. Поменять реализацию никогда не поздно.
И помни, сериализация в дотнете очень не эффективная. Если ты создашь свой стрим, то резко повысишь производительность и понизишь расход памяти. Так что...
Кстати, зачем тебе сериализация? Для каки целей?
PD>Я понимаю (даже с моим знанием .net , что можно придумать еще много PD>других решений. Вообще, если этот вопрос рассматривать чисто PD>практически, то разумное решение находится за 1 минуту — выкинуть это N3 PD>вообще, а в конце записать тройку (0,0,0) .
Во-во. И тормоза с перерасходм памяти выростят на порядок, так как стандартная сериализация дико не эффективна.
PD> Мой вопрос в другом - PD>можно ли перемещаться по потоку при сериализации ?
Зависит от потока. Стандартная сериализация вообще не предоставляет тебе никаких потоков. Она предлагает создать тебе граф обхектов или заполнить словарь (имя->значение). Сериализация же при этом делается дотнетом (системными библиотеками). Нужно это для двух вещей. 1. Это позволяет решить проблему чтения сериализованной информации другой версии. 2. Это позволяет абстрагироваться от формата сериализации и, например, сохраняь данные в ХМЛ. Но это довольно не эффективно, плюс ко всему, орлы из МС реализовали этот механизм из рук вон плохо. Так что лучше сериализовать вручную, в тот самый мемори-стрим (или файл). А потом или просто использовать этот стрим, или подсовывать его стандартному сериализатору.
PD> Иными словами , PD>сериализация — строго последовательный алгоритм или все же можно в нем PD>позиционироваться ?
Ни то, ни то. Ответ выше.
... << RSDN@Home 1.1.4 beta 2 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
AVK>>Нет, потому что не любой поток допускает позиционирование.
VD>Да нет поблем сохранить нужную величину под отдельным именем. И по стримам прыгать не прицдется.
Ты не понял — он хочет в стрим сначала записать количество, потом данные, притом количество он заранее не знает, а держать все данные в памяти или делать два прохода не хочет.
Здравствуйте, AndrewVK, Вы писали:
AVK>Ты не понял — он хочет в стрим сначала записать количество, потом данные, притом количество он заранее не знает, а держать все данные в памяти или делать два прохода не хочет.
Я все понял. Но как ты понимашь стримов там просто нет. Так что его один фиг вручную создавать. А если так, то количество можно хранить и под отдельным именем.
... << RSDN@Home 1.1.4 beta 2 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
PD>Ох нет, только не это . Там десятки, если не сотни Мб. См. пояснение PD>в моем ответе Holmes
1. Так ведь десятки разреженных мегабайт?
2. Сериализация в дотнете работает очень небыстро. При сериализации нет доступа к бинарному виду, все виртуализировано до уровня именованых свойств.
Если ты хочешь в бинарном виде сохранять, делай так.
* Создай MemoryStream, в конструкторе задай примерную вместимость (прикинь, какой процент ячеек обычно заполнен, добавь чуток сверху).
* Беги по матрице, сбрасывай понемногу данные.
* Получи MemoryStream.ToArray и скинь его сериализационному механизму в один именованый слот.
* Скинь размер в другой именованый слот.
PD>Если хочешь, немного деталей. Матрица была PD>трехмерной, и вертикали в ней состояли
Тогда действительно лучшим решением будет создать правильное хранилище для этой матрицы (которое используется в Run-Time), и потом сериализовать его стандартным образом, вообще без custom ISerializable implementation.
Теоретически, нужно те столбцы или колонки, которые ты там используешь, реализовать отдельным внутренним классом, и учитывать по нему некий хеш.
И реализовывать механизм Copy-On-Write. При попытке изменения данных в объекте-столбце, смотреть на то, используетя ли он "в нескольких позициях". Если да, то делать копию, и уменьшать счетчик. То есть при возникновении изменений совместно используемый столбец будет "расслаиваться", дивергенция.
Хеширование нужно для того, чтобы сделать обратный механизм, "конвергенцию". Когда в результате изменений два разных столбца начинают хранить одинаковые данные, их можно объединить и одну копию данных выбросить.
Чтобы производительность была высокой, хеширование должно быть простым (можно XOR или сумму всех элементов). Тогда при изменении одной ячейки в столбце не нужно будет пересчитывать весь хеш, а только произвести две обратные операции -- отнять старое значение, прибавить новое. Когда при изменении ячейки хеш вдруг начинает совпадать с другим столбцом, нужно удостовериться что столбцы действительно полностью совпадают, и тогда их совмещать и удалять дубль.
Такое бережливое отношение к памяти ускорит и runtime-обработку, так как скорость перезагрузки процессорных кешей обычно очень критична для обсчетов.