Использование памяти Protobuf.net - PullRequest
6 голосов
/ 09 декабря 2011

Heyup. Давний любитель protobuf.net.

Быстрый вопрос. У меня есть многопоточное приложение C #, которое десериализует, возможно, 100 объектов в секунду, что составляет около 50 МБ / с. Я вижу очень большое использование памяти, намного превышающее то, что я десериализирую. Я запустил приложение через 'Red Gate ANTS Memory Profiler', и он показывает мне огромное количество объектов памяти поколения 2 из-за protobuf (более 50% использования приложения). Большинство объектов имеют значения типа int и связаны с:

- TypeModel.TryDeserializeList()
- ProtoBuf.Meta.BasicList

Буду признателен за любую помощь, уменьшающую использование памяти этого поколения 2.

Марк

Ответы [ 2 ]

5 голосов
/ 11 декабря 2011

Мне кажется, что корнем T здесь является сам массив, т.е.

int[] values = Serializer.Deserialize<int[]>(source);

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

[ProtoContract]
public class MyDataWrapper { // need a new name...
    [ProtoMember(1)]
    public int[] Values { get;set; }
}

и затем:

int[] values = Serializer.Deserialize<MyDataWrapper>(source).Values;

Фактически это полностью совместимо с данными, уже сериализованными через Serialize<int[]>, при условии, что используемый номер поля равен 1. Еще одним преимуществом этого подхода является то, что при желании вы можете использовать «упакованный» подформат (доступен только для списков / массивов примитивов, таких как int); хотя, возможно, это не очень хорошая идея в этом случае из-за большой длины (может потребоваться буферизация при сериализации).


Дополнительный контекст; «v1» здесь в основном использует MakeGenericType, чтобы переключиться на что-то подобное выше на лету; однако, поскольку этот подход недоступен во многих дополнительных платформах, на которые ориентирован v2, здесь используется менее элегантный подход. Но теперь, когда он довольно стабилен, я мог бы повторно добавить оптимизированную версию при работе на полной версии .NET 2.0 или выше.

1 голос
/ 18 декабря 2015

Чтобы уточнить ответ Марка, я сделал быстрый тест

  • Сериализация / десериализация с использованием обертки по сравнению с использованием массива.
  • С / без GC сервера

В тесте производительности было создано 100 000 сложных объектов (1 раз, 2 двойных, 2 целых, 2 целых, список строк с 0-4 короткими элементами (1 символ)) и 30 раз повторен процесс сериализации / десериализации общее время и количество сборов GC, которые произошли во время прогона. Результаты были (запущены в Release вне VS)

GC IsServer: False, GC latency: Interactive, GC LOH compaction: Default
Wrapper serialization
Generation 0: 0 collects
Generation 1: 0 collects
Generation 2: 0 collects
Time: 20.363 s
------------------------
Array serialization
Generation 0: 0 collects
Generation 1: 0 collects
Generation 2: 0 collects
Time: 30.433 s
------------------------
Wrapper deserialization
Generation 0: 109 collects
Generation 1: 47 collects
Generation 2: 16 collects
Time: 71.277 s
------------------------
Array deserialization
Generation 0: 129 collects
Generation 1: 57 collects
Generation 2: 19 collects
Time: 89.145 s


GC IsServer: True, GC latency: Interactive, GC LOH compaction: Default
Wrapper serialization
Generation 0: 0 collects
Generation 1: 0 collects
Generation 2: 0 collects
Time: 20.430 s
------------------------
Array serialization
Generation 0: 0 collects
Generation 1: 0 collects
Generation 2: 0 collects
Time: 30.364 s
------------------------
Wrapper deserialization
Generation 0: 4 collects
Generation 1: 3 collects
Generation 2: 2 collects
Time: 39.452 s
------------------------
Array deserialization
Generation 0: 3 collects
Generation 1: 3 collects
Generation 2: 3 collects
Time: 47.546 s

Итак, мой вывод

  • Подход обертки выгоден как для сериализации, так и для десериализации (последний имеет более выраженный эффект).
  • Издержки сбора GC, наложенные подходом массива, более заметны при работе без GC сервера. Также обратите внимание, что влияние на производительность GC очень плохо, если сервер GC не запущен и не десериализован в нескольких потоках (результаты не включены).

Надеюсь, кто-нибудь найдет это полезным.

(к сожалению, код теста зависит от внутреннего кода, поэтому я не могу опубликовать полный код здесь).

...