Protobuf.net 内存使用情况

本文关键字:用情 情况 内存 net Protobuf | 更新日期: 2023-09-27 18:17:27

Heyup.protobuf.net 的长期情人。

不过很快的问题。我有一个高度多线程的 C# 应用程序,它每秒反序列化大约 100 个对象,总计大约 50MB/秒。我看到非常大的内存使用量,远远超过我正在反序列化的内存使用量。我通过"Red Gate ANTS Memory Profiler"运行了该应用程序,它向我展示了由于 protobuf(超过 50% 的应用程序使用量(而导致的大量第 2 代内存对象。大多数对象都是 int 值,并与以下对象链接:

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

任何帮助减少此第 2 代内存使用量将不胜感激。

马克

Protobuf.net 内存使用情况

在我看来,这里的根 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 或更高版本上运行时重新添加优化版本。

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

    使用
  • 包装器的序列化/反序列化与使用数组。
  • 启用
  • /未启用服务器 GC

基准测试创建了 100.000 个复杂对象(1 个时间跨度、2 个双精度、2 个整数、2 个整数、包含 0 到 4 个短元素(1 个字符(的字符串列表(,并重复序列化/反序列化过程 30 次,并测量运行期间花费的总时间和 GC 收集的数量。结果是(在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 性能影响非常严重(不包括结果(。

希望有人觉得这有用。

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