C# Protobuf.net内存使用率

C# Protobuf.net内存使用率,c#,protobuf-net,C#,Protobuf Net,是的。protobuf.net的长期爱好者 不过还是有个简单的问题。我有一个高度多线程的C#应用程序,它每秒反序列化大约100个对象,大约50MB/秒。我看到了非常大的内存使用量,远远超过了我正在进行的反序列化。我已经通过“红门蚂蚁内存档案器”运行了这个应用程序,它向我展示了protobuf带来的大量第2代内存对象(超过应用程序使用量的50%)。大多数对象都是int值,并与以下对象链接: - TypeModel.TryDeserializeList() - ProtoBuf.Meta.Basic

是的。protobuf.net的长期爱好者

不过还是有个简单的问题。我有一个高度多线程的C#应用程序,它每秒反序列化大约100个对象,大约50MB/秒。我看到了非常大的内存使用量,远远超过了我正在进行的反序列化。我已经通过“红门蚂蚁内存档案器”运行了这个应用程序,它向我展示了protobuf带来的大量第2代内存对象(超过应用程序使用量的50%)。大多数对象都是int值,并与以下对象链接:

- TypeModel.TryDeserializeList()
- ProtoBuf.Meta.BasicList
如果您能帮助减少第2代内存的使用,我们将不胜感激


Marc

我觉得这里的根T是数组本身,即

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

int[] values = Serializer.Deserialize<MyDataWrapper>(source).Values;
int[]values=序列化程序.反序列化(源).values;
只要使用的字段号是
1
,它实际上与通过
Serialize
序列化的数据完全兼容。这种方法的另一个好处是,如果需要,可以使用“压缩”子格式(仅适用于基本体的列表/数组,如int);尽管在这种情况下,由于长度太长(序列化时可能需要缓冲),这可能不是一个好主意



附加上下文;这里的“v1”基本上使用MakeGenericType动态切换到类似上面的内容;然而,由于这种方法在“v2”目标的许多其他平台中都不可用,因此它在这里使用了一种不那么优雅的方法。但现在它非常稳定,我可以在运行完整的.NET 2.0或更高版本时重新添加优化版本。

为了详细说明Marcs的答案,我做了一个快速的基准测试

  • 使用包装器的序列化/反序列化与使用数组的序列化/反序列化
  • 启用/不启用服务器GC
基准测试创建了100.000个复杂对象(1个timespan、2个Double、2个ints、2个int?s、包含0到4个短元素(1个字符)的字符串列表),并将序列化/反序列化过程重复了30次,并测量了运行期间所花费的总时间和发生的GC集合数。结果是(在VS之外的版本中运行)

所以我的结论是

  • 包装器方法对序列化和反序列化都有好处(后者的效果更为显著)
  • 在没有服务器GC的情况下运行时,数组方法施加的GC收集开销更为明显。还请注意,如果不在多个线程上运行服务器GC和反序列化(结果不包括在内),则GC性能影响非常糟糕
希望有人觉得这个有用


(不幸的是,基准代码依赖于内部代码,所以我不能在这里发布完整的代码)。

有趣的是,您能给出模型的大致外观吗?它是一个整数数组吗?一个int的列表?还有:这是什么框架?完整的.NET?查阅SL?可能可以很容易地解决这个问题,但需要更多的上下文来确定。还有-根对象是否在列表中?基本上。。。哈兹·科兹会吗?(或者至少,类似的东西)好的。对缺乏细节表示歉意。你所说的“模型是什么样子的”是什么意思。我正在反序列化的最大对象是一个使用.net 4.0的int[33554432]数组。不幸的是,我无法提供导致问题的特定代码片段。内存分析器似乎给了我的所有信息是,Gen2和大对象堆中都有大量的in值与ProtoBuf.Meta.BasicList有某种关联。还值得补充的是,当所有的反序列化都完成时,我称之为手动垃圾收集,应用程序的内存使用率将下降到反序列化时的20%。这就是protobuf在反序列化如此大的int数组时预期的内存使用情况吗?我想知道的主要问题是。。。反序列化中使用的T是什么?是不是反序列化?是的,这就是问题所在。添加包装器和其他任何东西都不能完全消除大内存使用。再次感谢马克。@marcfk;我将看看是否可以重新添加优化,以备将来使用usage@MarcGravell我知道这是一个老问题,但我遇到了同样的GC问题。您能否详细介绍一下解决方法(使用包装器类):这是否适用于所有数组类型(例如,ComplexType[]作为根对象,其中ComplexType可以使用继承进行建模)?包装和数组解决方案是否完全等效(即protobuf在序列化数组时是否只生成一个包装类)?最后,你考虑过如何解决v。2.1.0(预释放)?是的,然后将使用不同的代码路径(尽管数据相同);没有,它还没有被检修过;我记不清这是否会对GC产生重大影响,但值得一试。对于最外层的类型:不,它不是简单地包装它iinternally@MarcGravell谢谢你的快速回复。我在下面的答案中添加了一些基准测试结果。这种效果非常明显,因此在适当的平台上对代码进行特殊处理可能是有意义的。
int[] values = Serializer.Deserialize<MyDataWrapper>(source).Values;
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