5

我想知道 MicroStream 的序列化是如何详细工作的。既然它被描述为“超快”,它就必须依赖代码生成,对吧?还是基于反射?
与 Protobuf-Serialization 相比,它将如何执行,Protobuf-Serialization 依赖于直接读取 java 字段并将它们写入字节缓冲区的代码生成,反之亦然。
在大规模序列化​​对象时,使用反射会大大降低性能,不是吗?

我正在寻找一种快速的方法来传输和持久化多人游戏的对象,并且每一毫秒都很重要。:)

提前致谢!

PS:由于我没有足够的声誉,我无法创建“微流”标签。https://microstream.one/

4

1 回答 1

14

我是 MicroStream 的首席开发人员。 (这不是别名帐户。我真的只是创建了它。我在 StackOverflow 上阅读了 10 年左右,但从未有理由创建帐户。直到现在。)

在每次初始化时,MicroStream 都会分析所有必需实体和值类型类的当前运行时版本,并从中派生优化的元数据。在运行时遇到迄今为止未知的类时也会这样做。分析是针对每个反射进行的,但由于每个处理的类只进行一次,因此反射性能成本可以忽略不计。实际的存储和加载或序列化和反序列化是通过基于创建的元数据的优化框架代码完成的。

如果类布局更改,类型分析会创建从存储类实例的字段布局到当前类的映射。如果可能,自动(明确更改或通过一些可配置的启发式),否则通过用户提供的映射。性能保持不变,因为 JVM 不关心它(简而言之)是否将加载的值 #3 复制到位置 #3 或位置 #5。这一切都在元数据中。

使用 ByteBuffers,更准确地说是直接 ByteBuffers,但仅作为通过直接“不安全”低级操作处理的堆外内存的锚。如果您不熟悉“不安全”操作,一个简短的概念是:“它与 C++ 代码一样直接和快速。”。您可以非常快速且接近记忆地做任何您想做的事情,但您也要对所有事情负责。有关更多详细信息,请搜索“sun.misc.Unsafe”。

不生成代码。不使用字节码黑客、通过代理默认替换实例或类似的猴子业务。在技​​术层面上,它只是一个 Java 库(包括“不安全”用法),但有很多设计合理的逻辑。

附带说明:反射并不像通常认为的那么慢。不再。确实如此,但在过去的一些 Java 版本中已经进行了相当多的优化。如果每个操作都必须重新进行所有的类分析、字段查找等,这只会很慢(很多框架似乎都这样做,因为它们写得不好)。如果字段被收集(设置可访问等)一次然后缓存,反射实际上快得惊人。

关于与 Protobuf-Serialization 的比较:

由于我没有使用协议缓冲区并且我不知道它在内部是如何工作的,所以我不能说任何具体的内容。与复杂技术一样,真正有意义的比较可能很难进行,因为不同的技术具有不同的优化优先级和限制。

大多数序列化方法放弃了引用一致性,但只存储“数据”(即,如果两个对象引用第三个,反序列化将创建第三个对象的两个实例。像这样:A->C<-B ==serialization==> A- >C1 B->C2。这基本上会破坏/破坏/破坏对象图并使循环图的序列化成为不可能,因为它会创建并无休止地级联复制。例如,参见 JSON 序列化。有趣的东西。)甚至 Brian Goetz 的草稿Java“序列化 2.0”包括该限制(参见http://cr.openjdk.java.net/~briangoetz/amber/serialization.html上的“限制” )(以及另一个打破关注点分离的限制)。

MicroStream 没有这个限制。它可以正确处理任意对象图,而不会破坏它们的引用。正如他所写,保持引用一致性的完整性到目前为止并不是“试图做太多”。它是“正确地做”。一个人只需要知道如何正确地做到这一点。如果做得正确,它甚至是相当微不足道的。因此,根据 Protobuf-Serialization 有多少限制(“与魔鬼的契约”),它可能几乎或什至根本无法与 MicroStream 相比。

当然,您始终可以针对您的特定要求创建一些性能比较测试,看看哪种技术最适合您。只需确保您了解某种技术对您施加的限制(破坏的引用一致性、禁止的类型、所需的注释、所需的默认构造函数/getter/setter 等)。MicroStream 没有*。

(*) 在合理范围内:序列化/存储系统内部(如线程)或非实体(如 lambda 或代理实例)虽然在技术上是可行的,但有意排除在外。

于 2019-11-13T11:18:48.293 回答