protocol-buffers - 为什么ProtoBuf在第一次调用时这么慢但是在内部循环中却非常快?

标签 protocol-buffers protobuf-net

灵感来自this question。我创建了一个小型基准程序来比较ProtoBuf,BinaryFormatter和Json.NET。基准测试本身是一个位于https://github.com/sidshetye/SerializersCompare的小型控制台。可以随意添加/改进,向混合中添加新的序列化器非常简单。无论如何,我的结果是:

        Binary Formatter         ProtoBuf          Json.NET     ServiceStackJson   ServiceStackJSV
 Loop     Size:512 bytes    Size:99 bytes    Size:205 bytes      Size:205 bytes     Size:181 bytes
    1         16.1242 ms      151.6354 ms       277.2085 ms         129.8321 ms        146.3547 ms
    2          0.0673 ms        0.0349 ms         0.0727 ms           0.0343 ms          0.0370 ms
    4          0.0292 ms        0.0085 ms         0.0303 ms           0.0145 ms          0.0148 ms
    8          0.0255 ms        0.0069 ms         0.0017 ms           0.0216 ms          0.0129 ms
   16          0.0011 ms        0.0064 ms         0.0282 ms           0.0114 ms          0.0120 ms
   32          0.0164 ms        0.0061 ms         0.0334 ms           0.0112 ms          0.0120 ms
   64          0.0347 ms        0.0073 ms         0.0296 ms           0.0121 ms          0.0013 ms
  128          0.0312 ms        0.0058 ms         0.0266 ms           0.0062 ms          0.0117 ms
  256          0.0256 ms        0.0097 ms         0.0448 ms           0.0087 ms          0.0116 ms
  512          0.0261 ms        0.0058 ms         0.0307 ms           0.0127 ms          0.0116 ms
 1024          0.0258 ms        0.0057 ms         0.0309 ms           0.0113 ms          0.0122 ms
 2048          0.0257 ms        0.0059 ms         0.0297 ms           0.0125 ms          0.0121 ms
 4096          0.0247 ms        0.0060 ms         0.0290 ms           0.0119 ms          0.0120 ms
 8192          0.0247 ms        0.0060 ms         0.0286 ms           0.0115 ms          0.0121 ms

免责声明:
  • 上面的结果来自Windows VM中-与裸机OS相比,非常小的时间间隔内的秒表/计时器值可能不是100%准确。因此,请忽略上表中的超低值。
  • 对于ServiceStack,Json和JSV得分来自两次单独的运行。由于它们共享相同的基础ServiceStack库,因此一个接一个运行会影响下一次运行的“冷启动” 1循环得分(快速启动“热启动”)

  • BinaryFormatter的大小最大,但对于单个序列化=>反序列化循环而言,它也是最快的。但是,一旦我们紧紧围绕序列化=>反序列化代码,ProtoBuf就会变得非常快。

    问题#1 :为什么对于单个序列化,ProtoBuf这么慢=>反序列化循环?

    问题#2 :从实际的角度来看,我们该怎么做才能克服“冷启动”的问题?通过它运行至少一个(任何类型的)对象?通过它运行每种(关键)对象类型吗?

    最佳答案

    Question#1: Why is ProtoBuf that much slower for a single serialization => deserialization loop?



    因为它需要大量的工作来分析模型和准备策略;我花了很多时间使生成的策略尽可能快地疯狂,但是可能是我在元编程层中跳过了一些优化。我很高兴将其添加为要查看的项目,以减少第一次通过的时间。当然,另一方面,元编程层仍然是Json.NET等效预处理的两倍; p

    Question#2: From a practical perspective, what can we do to get past that "cold start"? Run at least one object (of any time) through it? Run every (critical) object type through it?



    多种选择:
  • 在构建过程中使用“precompile”工具将编译后的序列化器生成为单​​独的完全静态编译的dll,您可以像正常一样引用和使用该文件:然后完全零元编程就会发生
  • 在启动时明确告诉模型有关“根”类型的信息,并存储Compile()的输出
    static TypeModel serializer;
    ...
    RuntimeTypeModel.Default.Add(typeof(Foo), true);
    RuntimeTypeModel.Default.Add(typeof(Bar), true);
    serializer = RuntimeTypeModel.Default.Compile();
    

    (Compile()方法将根据根类型进行分析,并根据需要添加任何其他类型,并返回已编译的生成实例)
  • 在启动时明确告知模型有关“根”类型的信息,并“多次”调用CompileInPlace()CompileInPlace()不会完全扩展模型-但是几次调用它应该可以覆盖大多数基础,因为编译一层会将其他类型带入模型
    RuntimeTypeModel.Default.Add(typeof(Foo), true);
    RuntimeTypeModel.Default.Add(typeof(Bar), true);
    for(int i = 0 ; i < 5 ; i++) {
        RuntimeTypeModel.Default.CompileInPlace();
    }
    

  • 另外,我可能应该:
  • 添加一种方法来完全扩展CompileInPlace场景
  • 的模型
  • 花一些时间优化元编程层

  • 最后的想法:此处CompileCompileInPlace之间的主要区别是,如果您忘记添加某些类型,将会发生什么; CompileInPlace适用于现有模型,因此您仍然可以在以后(隐式或显式)添加新类型,它将“正常工作”; Compile更严格:一旦通过它生成了一个类型,它就会被修复,并且只能处理,即编译时可以推断出的类型。

    关于protocol-buffers - 为什么ProtoBuf在第一次调用时这么慢但是在内部循环中却非常快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13735248/

    相关文章:

    java - Protocol Buffer : How to define Date type?

    c# - 如何使用 protobuf-net 处理 .proto 文件

    C++ 2010 Express Protobuf 编译错误

    java - 如何序列化protobuf中的反向关系

    android - GRPC OkHttp版本(1.29.0)如何升级com.squareup.okhttp(4.6.0)?

    c# - C++ 中的 Google ProtoBuf 与 C# 中的 Protobuf-net 聊天(UDP)

    c# - 序列化 LinqToSql 生成的实体,保持关系和延迟加载

    c# - 使用 protobuf 为 c# 反序列化 "long"字符串对我来说不能正常工作

    protocol-buffers - 在预构建事件中使用 protogen 从 .proto 文件生成 c# 文件

    c# - 我可以选择检索 protobuf-net 或 NHibernate 中的对象字段吗?