Java 中不同二进制序列化方法的性能

发布于 2021-03-22 20:36:44 字数 4250 浏览 1174 评论 0

我们将去了解java中二进制序列化的性能,下面的类将参与比较:

  • DataInputStream(ByteArrayInputStream) 和对应的 DataOutputStream(ByteArrayOutputStream)。
  • 查看同步是如何影响 ByteArrayInput/OutputStream 的,检查 BAInputStream-复制 ByteArrayInputStream w/o 同步的.
  • ByteBuffer 的 4 种风味-基于堆/内存 (heap/direct),大/小端 (big/little endian)。
  • sun.misc.Unsafe 基于堆的字节数组的内存操作。

以我的经验,所有这些序列化方法的性能既依赖于数据节点的数量,又依赖于缓冲区或流的类型。因此我写了两组测试。第一个测试工作在只有单一字段 byte[500] 的对象,然而第二个测试使用另一个具其他单一字段-long[500] 的对象。在 ByteBuffer 和 Unsafe 测试中,我们会测试大量的操作,在每次单独的调用中都会序化每个数组元素。

首先,下面是第二联盟 (sencond league) 的测试结果:流。出于比较目的,我们将记录下使用小端堆(heap little-endian)的 ByteBuffer 迭代次数相同的结果。请注意,我们在第二测试联盟中使用的迭代次数比较小。

两次测试中,每个单元格显示的是 Java6/Java7 运行一次需要的秒数。

第二联盟

Structure:DataOutputStream (ByteArrayOutputStream)DataInputStream (ByteArrayInputStream)DataInputStream (BAInputStream – not synchronized)ByteBuffer
TypeHeap
EndianLittle
Write 1M byte buffers (500 bytes)16.178/15.74--0.362/0.379
Read 1M byte buffers (500 bytes)-16.106/16.4242.432/2.3610.471/0.508
Write 1M long buffers (500 longs)11.735/11.632--3.771/3.691
Read 1M long buffers (500 longs)-11.747/11.6229.966/9.5326.062/4.069

需要注意的是:在ByteArray流上同步对小数据类型的影响要大于大数据类型。字符操作中,同步和不同步的版本性能相差8倍,这确实是个问题!

第一阵营

在第一阵营中,我们将比较各种通过 sun.misc.Unsafe 进行内存访问的 ByteBuffers。不幸的是,Unsafe 类只工作在本地字节顺序下(大端或小端)。通过使用 Short/Integer/Long.reverseBytes 方法(覆盖2/4/8字节数据类型)可以解决该问题,但没有临时缓冲区的话,大量的非本地字节顺序的操作是不可能的,即使有临时缓冲区也没多大意义-这意味着2数据拷贝而不是一个。

Structure:ByteBufferByteBufferByteBufferByteBufferUnsafe
TypeHeapHeapDirectDirect
EndianLittleBigLittleBigLittle (native)
Write 80M byte buffers (as bytes)26.21/26.2627.457/27.365101.967/43.338102.912/43.39631.578/31.846
Write 80M byte buffers (as 1 byte[])2.966/2.8883.489/3.3214.036/4.0164.27/4.0382.456/2.035
Read 80M byte buffers (as bytes)56.163/36.08956.322/36.08440.1/41.41140.519/41.19536.68/41.711
Read 80M byte buffers (as 1 byte[])6.909/6.8426.926/7.0366.795/6.8087.377/7.1766.307/6.317
Write 10M long buffers (as longs)36.179/37.31651.063/51.22114.702/7.36664.305/10.1277.301/6.699
Write 10M long buffers (as 1 long[])32.301/29.65153.477/54.1152.014/1.91227.265/28.3191.703/1.701
Read 10M long buffers (as longs)59.625/39.92353.978/52.09726.715/10.94167.355/15.9328.492/10.026
Read 10M long buffers (as 1 long[])47.19/36.37360.107/35.1866.668/6.75432.925/35.0716.075/6.143

总结

我们发现了什么?

  1. 将字节一个一个地写入直接字节缓冲区是极慢地。你应该避免使用直接字节缓冲区写几乎都是单一字节字段的记录。
  2. 如果你有原始数组字段-总是使用批操作方法处理它们。ByteBuffer大量操作方法的性能几乎接近Unsafe方法(不过,ByteBuffer总是慢点)。如果你需要存储或加载除了byte的任何原始数组-在更新完字节缓冲区位置后,调用ByteBuffer.as[yourType]Buffer.put(array)方法。
  3. 平均字段长度越长-堆缓存区越慢,直接字节缓冲区越快。Unsafe即使访问分开的字段也是挺快的。
  4. 和Java6相比,Java7中许多类型的ByteBuffer访问都经过明显的优化。
  5. 总是尝试使用直接字节缓冲区以平台自然的字节顺序序列化原生数组-它的性能和Unsafe相当,不过更灵活。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84960 人气
更多

推荐作者

沧笙踏歌

文章 0 评论 0

山田美奈子

文章 0 评论 0

佚名

文章 0 评论 0

岁月无声

文章 0 评论 0

暗藏城府

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文