Java对象序列化的本机C / C ++类似性能

您是否曾经希望过像使用C ++这样的本地语言将Java对象转换成字节流一样快的速度? 如果您使用标准的Java序列化,您可能会对性能感到失望。 Java序列化的目的是与尽可能快而紧凑地序列化对象的目的截然不同。

为什么我们需要快速紧凑的序列化? 我们的许多系统都是分布式的,我们需要通过在流程之间高效地传递状态进行通信。 这种状态存在于我们的物体内部。 我已经介绍了许多系统,通常大部分成本是该状态与字节缓冲区之间的串行化。 我已经看到用于实现此目的的大量协议和机制。 一方面是易于使用但效率低下的协议,例如Java 序列化 , XML和JSON 。 另一方面,二进制协议可以非常快速和高效,但是需要更深入的理解和技能。

在本文中,我将说明使用简单的二进制协议时可能实现的性能提升,并介绍一种Java中可用的鲜为人知的技术,以实现与C或C ++之类的本地语言类似的性能。

要比较的三种方法是:

  1. Java序列化 :Java中有一个对象实现Serializable的标准方法。
  2. 通过ByteBuffer进行二进制 :使用ByteBuffer API的简单协议,以二进制格式写入对象的字段。 这是我们认为是好的二进制编码方法的基准。
  3. 二进制通过不安全 :介绍不安全和其允许直接内存操作方法的集合。 在这里,我将展示如何获得与C / C ++类似的性能。

编码

import sun.misc.Unsafe;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.Arrays;public final class TestSerialisationPerf
{public static final int REPETITIONS = 1 * 1000 * 1000;private static ObjectToBeSerialised ITEM =new ObjectToBeSerialised(1010L, true, 777, 99,new double[]{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0},new long[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10});public static void main(final String[] arg) throws Exception{for (final PerformanceTestCase testCase : testCases){for (int i = 0; i < 5; i++){testCase.performTest();System.out.format('%d %s\twrite=%,dns read=%,dns total=%,dns\n',i,testCase.getName(),testCase.getWriteTimeNanos(),testCase.getReadTimeNanos(),testCase.getWriteTimeNanos() + testCase.getReadTimeNanos());if (!ITEM.equals(testCase.getTestOutput())){throw new IllegalStateException('Objects do not match');}System.gc();Thread.sleep(3000);}}}private static final PerformanceTestCase[] testCases ={new PerformanceTestCase('Serialisation', REPETITIONS, ITEM){ByteArrayOutputStream baos = new ByteArrayOutputStream();public void testWrite(ObjectToBeSerialised item) throws Exception{for (int i = 0; i < REPETITIONS; i++){baos.reset();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(item);oos.close();}}public ObjectToBeSerialised testRead() throws Exception{ObjectToBeSerialised object = null;for (int i = 0; i < REPETITIONS; i++){ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais);object = (ObjectToBeSerialised)ois.readObject();}return object;}},new PerformanceTestCase('ByteBuffer', REPETITIONS, ITEM){ByteBuffer byteBuffer = ByteBuffer.allocate(1024);public void testWrite(ObjectToBeSerialised item) throws Exception{for (int i = 0; i < REPETITIONS; i++){byteBuffer.clear();item.write(byteBuffer);}}public ObjectToBeSerialised testRead() throws Exception{ObjectToBeSerialised object = null;for (int i = 0; i < REPETITIONS; i++){byteBuffer.flip();object = ObjectToBeSerialised.read(byteBuffer);}return object;}},new PerformanceTestCase('UnsafeMemory', REPETITIONS, ITEM){UnsafeMemory buffer = new UnsafeMemory(new byte[1024]);public void testWrite(ObjectToBeSerialised item) throws Exception{for (int i = 0; i < REPETITIONS; i++){buffer.reset();item.write(buffer);}}public ObjectToBeSerialised testRead() throws Exception{ObjectToBeSerialised object = null;for (int i = 0; i < REPETITIONS; i++){buffer.reset();object = ObjectToBeSerialised.read(buffer);}return object;}},};
}abstract class PerformanceTestCase
{private final String name;private final int repetitions;private final ObjectToBeSerialised testInput;private ObjectToBeSerialised testOutput;private long writeTimeNanos;private long readTimeNanos;public PerformanceTestCase(final String name, final int repetitions,final ObjectToBeSerialised testInput){this.name = name;this.repetitions = repetitions;this.testInput = testInput;}public String getName(){return name;}public ObjectToBeSerialised getTestOutput(){return testOutput;}public long getWriteTimeNanos(){return writeTimeNanos;}public long getReadTimeNanos(){return readTimeNanos;}public void performTest() throws Exception{final long startWriteNanos = System.nanoTime();testWrite(testInput);writeTimeNanos = (System.nanoTime() - startWriteNanos) / repetitions;final long startReadNanos = System.nanoTime();testOutput = testRead();readTimeNanos = (System.nanoTime() - startReadNanos) / repetitions;}public abstract void testWrite(ObjectToBeSerialised item) throws Exception;public abstract ObjectToBeSerialised testRead() throws Exception;
}class ObjectToBeSerialised implements Serializable
{private static final long serialVersionUID = 10275539472837495L;private final long sourceId;private final boolean special;private final int orderCode;private final int priority;private final double[] prices;private final long[] quantities;public ObjectToBeSerialised(final long sourceId, final boolean special,final int orderCode, final int priority,final double[] prices, final long[] quantities){this.sourceId = sourceId;this.special = special;this.orderCode = orderCode;this.priority = priority;this.prices = prices;this.quantities = quantities;}public void write(final ByteBuffer byteBuffer){byteBuffer.putLong(sourceId);byteBuffer.put((byte)(special ? 1 : 0));byteBuffer.putInt(orderCode);byteBuffer.putInt(priority);byteBuffer.putInt(prices.length);for (final double price : prices){byteBuffer.putDouble(price);}byteBuffer.putInt(quantities.length);for (final long quantity : quantities){byteBuffer.putLong(quantity);}}public static ObjectToBeSerialised read(final ByteBuffer byteBuffer){final long sourceId = byteBuffer.getLong();final boolean special = 0 != byteBuffer.get();final int orderCode = byteBuffer.getInt();final int priority = byteBuffer.getInt();final int pricesSize = byteBuffer.getInt();final double[] prices = new double[pricesSize];for (int i = 0; i < pricesSize; i++){prices[i] = byteBuffer.getDouble();}final int quantitiesSize = byteBuffer.getInt();final long[] quantities = new long[quantitiesSize];for (int i = 0; i < quantitiesSize; i++){quantities[i] = byteBuffer.getLong();}return new ObjectToBeSerialised(sourceId, special, orderCode, priority, prices, quantities);}public void write(final UnsafeMemory buffer){buffer.putLong(sourceId);buffer.putBoolean(special);buffer.putInt(orderCode);buffer.putInt(priority);buffer.putDoubleArray(prices);buffer.putLongArray(quantities);}public static ObjectToBeSerialised read(final UnsafeMemory buffer){final long sourceId = buffer.getLong();final boolean special = buffer.getBoolean();final int orderCode = buffer.getInt();final int priority = buffer.getInt();final double[] prices = buffer.getDoubleArray();final long[] quantities = buffer.getLongArray();return new ObjectToBeSerialised(sourceId, special, orderCode, priority, prices, quantities);}@Overridepublic boolean equals(final Object o){if (this == o){return true;}if (o == null || getClass() != o.getClass()){return false;}final ObjectToBeSerialised that = (ObjectToBeSerialised)o;if (orderCode != that.orderCode){return false;}if (priority != that.priority){return false;}if (sourceId != that.sourceId){return false;}if (special != that.special){return false;}if (!Arrays.equals(prices, that.prices)){return false;}if (!Arrays.equals(quantities, that.quantities)){return false;}return true;}
}class UnsafeMemory
{private static final Unsafe unsafe;static{try{Field field = Unsafe.class.getDeclaredField('theUnsafe');field.setAccessible(true);unsafe = (Unsafe)field.get(null);}catch (Exception e){throw new RuntimeException(e);}}private static final long byteArrayOffset = unsafe.arrayBaseOffset(byte[].class);private static final long longArrayOffset = unsafe.arrayBaseOffset(long[].class);private static final long doubleArrayOffset = unsafe.arrayBaseOffset(double[].class);private static final int SIZE_OF_BOOLEAN = 1;private static final int SIZE_OF_INT = 4;private static final int SIZE_OF_LONG = 8;private int pos = 0;private final byte[] buffer;public UnsafeMemory(final byte[] buffer){if (null == buffer){throw new NullPointerException('buffer cannot be null');}this.buffer = buffer;}public void reset(){this.pos = 0;}public void putBoolean(final boolean value){unsafe.putBoolean(buffer, byteArrayOffset + pos, value);pos += SIZE_OF_BOOLEAN;}public boolean getBoolean(){boolean value = unsafe.getBoolean(buffer, byteArrayOffset + pos);pos += SIZE_OF_BOOLEAN;return value;}public void putInt(final int value){unsafe.putInt(buffer, byteArrayOffset + pos, value);pos += SIZE_OF_INT;}public int getInt(){int value = unsafe.getInt(buffer, byteArrayOffset + pos);pos += SIZE_OF_INT;return value;}public void putLong(final long value){unsafe.putLong(buffer, byteArrayOffset + pos, value);pos += SIZE_OF_LONG;}public long getLong(){long value = unsafe.getLong(buffer, byteArrayOffset + pos);pos += SIZE_OF_LONG;return value;}public void putLongArray(final long[] values){putInt(values.length);long bytesToCopy = values.length << 3;unsafe.copyMemory(values, longArrayOffset,buffer, byteArrayOffset + pos,bytesToCopy);pos += bytesToCopy;}public long[] getLongArray(){int arraySize = getInt();long[] values = new long[arraySize];long bytesToCopy = values.length << 3;unsafe.copyMemory(buffer, byteArrayOffset + pos,values, longArrayOffset,bytesToCopy);pos += bytesToCopy;return values;}public void putDoubleArray(final double[] values){putInt(values.length);long bytesToCopy = values.length << 3;unsafe.copyMemory(values, doubleArrayOffset,buffer, byteArrayOffset + pos,bytesToCopy);pos += bytesToCopy;}public double[] getDoubleArray(){int arraySize = getInt();double[] values = new double[arraySize];long bytesToCopy = values.length << 3;unsafe.copyMemory(buffer, byteArrayOffset + pos,values, doubleArrayOffset,bytesToCopy);pos += bytesToCopy;return values;}
}

结果

2.8GHz Nehalem - Java 1.7.0_04
==============================
0 Serialisation  write=2,517ns read=11,570ns total=14,087ns
1 Serialisation  write=2,198ns read=11,122ns total=13,320ns
2 Serialisation  write=2,190ns read=11,011ns total=13,201ns
3 Serialisation  write=2,221ns read=10,972ns total=13,193ns
4 Serialisation  write=2,187ns read=10,817ns total=13,004ns
0 ByteBuffer     write=264ns   read=273ns    total=537ns
1 ByteBuffer     write=248ns   read=243ns    total=491ns
2 ByteBuffer     write=262ns   read=243ns    total=505ns
3 ByteBuffer     write=300ns   read=240ns    total=540ns
4 ByteBuffer     write=247ns   read=243ns    total=490ns
0 UnsafeMemory   write=99ns    read=84ns     total=183ns
1 UnsafeMemory   write=53ns    read=82ns     total=135ns
2 UnsafeMemory   write=63ns    read=66ns     total=129ns
3 UnsafeMemory   write=46ns    read=63ns     total=109ns
4 UnsafeMemory   write=48ns    read=58ns     total=106ns2.4GHz Sandy Bridge - Java 1.7.0_04
===================================
0 Serialisation  write=1,940ns read=9,006ns total=10,946ns
1 Serialisation  write=1,674ns read=8,567ns total=10,241ns
2 Serialisation  write=1,666ns read=8,680ns total=10,346ns
3 Serialisation  write=1,666ns read=8,623ns total=10,289ns
4 Serialisation  write=1,715ns read=8,586ns total=10,301ns
0 ByteBuffer     write=199ns   read=198ns   total=397ns
1 ByteBuffer     write=176ns   read=178ns   total=354ns
2 ByteBuffer     write=174ns   read=174ns   total=348ns
3 ByteBuffer     write=172ns   read=183ns   total=355ns
4 ByteBuffer     write=174ns   read=180ns   total=354ns
0 UnsafeMemory   write=38ns    read=75ns    total=113ns
1 UnsafeMemory   write=26ns    read=52ns    total=78ns
2 UnsafeMemory   write=26ns    read=51ns    total=77ns
3 UnsafeMemory   write=25ns    read=51ns    total=76ns
4 UnsafeMemory   write=27ns    read=50ns    total=77ns

分析

使用Java序列化在我的快速2.4 GHz Sandy Bridge笔记本电脑上写和读一个相对较小的对象可能需要10,000ns,而使用Unsafe时,即使考虑到测试代码本身,也可以减少到不到100ns。 为了说明这一点,在使用Java序列化时,成本与网络跃点相当! 如果您的传输是同一系统上的快速IPC机制,那么这将是非常昂贵的。

Java序列化如此昂贵的原因有很多。 例如,它为每个对象写出完全限定的类和字段名称以及版本信息。 同样, ObjectOutputStream保留所有书面对象的集合,以便在调用close()时可以将它们合并。 对于此示例对象,Java序列化需要340字节,但是对于二进制版本,我们仅需要185字节。 Java序列化格式的详细信息可以在这里找到。 如果我没有使用数组存储大多数数据,那么由于字段名的原因,使用Java序列化,序列化的对象会大很多。 以我的经验,诸如XML和JSON之类的基于文本的协议甚至可能比Java序列化的效率更低。 还应注意Java序列化是RMI所采用的标准机制。

真正的问题是要执行的指令数量。 Unsafe方法有很大优势,因为在Hotspot和许多其他JVM中,优化器将这些操作视为内部操作,并用汇编指令替换了调用以执行内存操作。 对于基本类型,这将导致单个x86 MOV指令,该指令通常可以在单个周期内发生。 如我在上一篇文章中所述,可以通过让Hotspot输出优化的代码来看到详细信息。

现在必须说,“ 功能强大,责任重大 ”,如果您使用Unsafe,它实际上与C语言编程相同,并且当偏移量错误时,也会发生内存访问冲突。

添加一些上下文

“ Google协议缓冲区之类的怎么样?”,听说您大声疾呼。 这些是非常有用的库,通常可以比Java序列化提供更好的性能和更大的灵活性。 但是,它们并不像我在此处所示的那样接近使用Unsafe的性能。 协议缓冲区解决了一个不同的问题,并提供了很好的自描述消息,这些消息在各种语言之间都可以正常工作。 请使用不同的协议和序列化技术进行测试以比较结果。

另外你之间的精明会问,“什么字节顺序的整数(字节顺序)写的?” 使用不安全时,字节以本机顺序写入。 这对于IPC以及相同类型的系统之间非常有用。 如果系统使用不同的格式,则必须进行转换。

我们如何处理一个类的多个版本或如何确定对象所属的类? 我想让本文重点关注,但让我们说一个简单的整数来表示实现类是标题所需的全部。 该整数可用于查找反序列化操作的适当实现。

我经常听到反对二进制协议和文本协议的争论,那么人类可读和调试该怎么办? 有一个简单的解决方案。 开发用于读取二进制格式的工具!

结论

总之,可以通过有效使用相同的技术,在Java中实现相同的本机C / C ++性能级别,以将对象与字节流进行串行化。 我已为其提供了基本实现的UnsafeMemory类,可以轻松扩展以封装此行为,从而在使用这种敏锐的工具时可以保护自己免受许多潜在问题的影响。

现在是急需解决的问题。 如果Java通过本地提供我对Unsafe所做的有效工作来为Serializable提供替代的Marshallable接口,会不会更好呢???

参考: Mechanical Sympathy博客上的JCG合作伙伴 Martin Thompson提供的Java对象序列化的本机C / C ++类性能,用于Java对象序列化 。


翻译自: https://www.javacodegeeks.com/2012/07/native-cc-like-performance-for-java.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/371942.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WebStrom Sass 编译配置 windows

第一步&#xff1a; 先安装Ruby下载 一路next 安装完成后打开开始菜单 打开后输入 gem install sass sass -v 出现版本号说明成功 第二部配置webstorm 在webstorm中settings中搜索file watchers工具&#xff0c;在此工具中添加一个scss的watcher 确定&#xff0c;打开一个scss…

非本地跳转之setjmp与longjmp

非本地跳转(unlocal jump)是与本地跳转相对应的一个概念。 本地跳转主要指的是类似于goto语句的一系列应用&#xff0c;当设置了标志之后&#xff0c;可以跳到所在函数内部的标号上。然而&#xff0c;本地跳转不能将控制权转移到所在程序的任意地点&#xff0c;不能跨越函数&am…

清华计算机自主招生试题,2017年清华大学自主招生笔试题

2017年清华大学自主招生笔试题2017高考结束后&#xff0c;全国各大高校自主招生面试开始了&#xff0c;以下是百分网小编搜索整理的关于2017年清华大学自主招生笔试题&#xff0c;供各位参考&#xff0c;希望对大家有所帮助!想了解更多相关信息请持续关注我们应届毕业生考试网!…

扩展剂:模式还是反模式?

扩展器模式在最近几年变得很流行&#xff0c;甚至已经在OSGi标准&#xff08;例如&#xff0c;蓝图服务和Web应用程序规范&#xff09;中使用。 在处女座&#xff0c;我们从一开始就与扩展程序一起工作&#xff0c;但是尽管它们具有优势&#xff0c;但它们仍有一些明显的缺点。…

python html格式编码

web应用如用到ace等网络编辑器的时候&#xff0c;如要支持html,xml等格式的文件编辑&#xff0c;输入ace 的文本内容必须先进行html格式编码&#xff1a; def html_escape(content): import cgi return cgi.escape(content)转载于:https://www.cnblogs.com/zhouxiaoming/p/703…

字符串替换

题目: 给定一个英文的字符串, 要求你将其中的元音删除掉, 返回新的字符串. 例如:"This website is for losers LOL!" --> "Ths wbst s fr lsrs LL!" 当看到这个题目的时候, 第一个想起的就是re模块的正则表达式. 不过由于最近使用过字符串的replace方…

小学计算机技术指导纲要,《中小学信息技术课程指导纲要(试行)》

《中小学信息技术课程指导纲要(试行)》2000年11月教育部颁发的《中小学信息技术课程指导纲要(试行)》教学目标&#xff1a;1、 增强信息意识&#xff0c;了解信息技术的发展变化及其对工作和社会的影响。2、 初步了解计算机基本工作原理&#xff0c;学会使用与学习和实际生活直…

JavaFX 2.0布局窗格– FlowPane和TilePane

FlowPanes和TilePanes是不错的布局窗格&#xff0c;如果您想一个接一个地连续地水平或垂直地布局子级&#xff0c;则可以。 它们彼此非常相似&#xff0c;因为它们都将子级布置成列&#xff08;在水平Flow / TilePane的情况下&#xff09;并按其宽度或行&#xff08;在垂直Flow…

EasyRMS录播管理服务器项目实战:windows上开机自启动NodeJS服务

本文转自EasyDarwin开源团队成员Penggy的博客&#xff1a;http://www.jianshu.com/p/ef840505ae06 近期在EasyDarwin开源团队开发一款基于EasyDarwin在录播服务器EasyRMS过程中,我采用node作为EasyRMS录播服务器录播管理服务器的开发平台,基于node开发关于设备管理,录像计划,录…

windows10搭建django1.10.3+Apache2.4

很多教程都是在linux上搭建&#xff0c;windows上似乎天生不太适合&#xff0c;但是我还是愿意试试这个坑。 首先 交代一下自己的环境 python3.5.2 64位 django 1.10.3 apache 2.4 64位 windows 10 重点在apache上。 python 和django 相信有兴趣看这篇文章的基本上也都已经了解…

深入理解计算机系统 视频教程,深入理解计算机系统1

第一章 计算机系统漫游代码段的生命周期hello.c#include int main(){printf("hello world!\n");return 0;}1.1 前序源程序(源文件)实际上就是一个由0和1组成的位(又成比特bit)序列,8个位被组组成一组,称为字节。每个字节表示程序中的某些文本字符(大部分的现代计算机…

Java与iOS对话:Java对象与Apple plist序列化

我很高兴地宣布我的第一个开源项目java-plist-serializer可以帮助您将Java&#xff08;尤其是基于Spring的应用程序&#xff09;与iOS应用程序集成。 背景 我正在将Java Webapp作为后端并且客户端是iOS设备的项目。 最近&#xff0c;我收到了创建Web服务的任务&#xff0c;该服…

web.cofing(新手必看)

花了点时间整理了一下ASP.NET Web.config配置文件的基本使用方法。很适合新手参看&#xff0c;由于Web.config在使用很灵活&#xff0c;可以自定义一些节点。所以这里只介绍一些比较常用的节点。 <?xml version"1.0"?> <!--注意: 除了手动编辑此文件以外&…

MIPS下CPU和RAM的数据流动情况详解

这是计算机硬件间的数据路径&#xff08;即数据流动的路径&#xff09;&#xff0c;下面将较详细分析此图&#xff1a; PC&#xff08;program counter&#xff0c; 程序计数器&#xff09;是一个用于记录当前计算机正在执行的指令的地址的寄存器&#xff08;register&#xff…

计算机乱程序怎么办,我的电脑程序乱了怎么办

我的电脑程序乱了&#xff0c;想用光盘恢复一下系统的修复安装方法第一种方法&#xff1a;1、点击“开始”菜单&#xff0c;点击“运行”2、输入CMD回车3、输入命令SFC/SCANNOW4、插入系统光盘系统会自动将硬盘中的系统文件于系统盘中的文件比较并进行修复如果不行&#xff0c;…

【计算机网络】网络层——IP协议

目录 一. 基本概念 二. 协议报文格式 三. 网段划分 1. 第一次划分 2. CIDR方案 3. 特殊的IP地址 四. IP地址不足 1. 私有IP和公网IP 2. DHCP协议 3. 路由器 4. NAT技术 内网穿透(NAT穿透) 五. 路由转发 路由表生成算法 结束语 一. 基本概念 IP指网络互连协议…

完整的Web应用程序Tomcat JSF Primefaces JPA Hibernate –第2部分

托管豆 这篇文章是本教程第1部分的继续。 在“ com.mb”包中&#xff0c;您将需要创建以下类&#xff1a; package com.mb;import org.primefaces.context.RequestContext;import com.util.JSFMessageUtil;public class AbstractMB {private static final String KEEP_DIALOG…

P1014 Cantor表

洛谷 p1014 题目描述 现代数学的著名证明之一是Georg Cantor证明了有理数是可枚举的。他是用下面这一张表来证明这一命题的&#xff1a; 1/1 1/2 1/3 1/4 1/5 … 2/1 2/2 2/3 2/4 … 3/1 3/2 3/3 … 4/1 4/2 … 5/1 … … 我们以Z字形给上表的每一项编号。第一项是1/1&#xff…

dvd管理系统

>>>>>>>>>>>>>>>>>>>> 语言&#xff1a;java 工具&#xff1a;eclipse 时间&#xff1a;2016.12.1 >>>>>>>>>>>>>>>>>>>> 一代代码&#xff1a; 1 …

佳能2900打印机与win10不兼容_佳能RF 1.4、RF 2增倍镜与RF 100500mm L IS USM并不完全兼容...

据佳能官方透露&#xff0c;佳能RF 1.4、RF 2增倍镜与RF 100-500mm F4.5-7.1 L IS USM镜头并不完全兼容。在安装使用两款增倍镜时&#xff0c;用户需将RF 100-500mm镜头变焦环的变焦位置移动到超过300mm的远摄区域。而在搭配增倍镜后&#xff0c;镜头变焦范围将限定在300-500mm…