加快Java的文件序列化速度
2012-10-12 16:31
162 查看
自从第一个Java版本开始,很多开发人员一直都在尝试让Java获得最少和C/C++一样的表现。JVM提供商尽他们最大的努力去实现一些新的JIT算法,但是还是有很多需要做的,特别是在我们使用Java的方法上。
例如,在对象<->文件序列化上就差距很大--尤其在读写内存对象上。我将就这个主题做一些解释和分享。
所有的测试都是在下面这个对象上执行的:
为了简单起见,我将只贴出写入方法(尽管读取类似),完整的源码在我的GitHub上可以找到(http://github.com/jkubrynski/serialization-tests)
最标准的java序列化(我们都是从这里学起的)是这样的:
提升标准序列化速度的最简单方法时使用RandomAccessFile对象:
更高深点的技术是使用Kryo框架,新旧版本的差距是很大的,我做过测试。因为性能比较上并没有体现出特别引人注意的差异,所以我将使用2.x版本,因为它对用户更友好而且更快些。
最后一个方案是在Martin Thompson的文章中提到的(Native C/C++ Like Performance
For Java Object Serialisation),介绍了怎样在Java中像C++那样和内存打交道。
TestObject写入方法如下:
直接内存缓冲区类(已简化了的,仅仅为了展示这个思想)
几个小时的Caliper测试结果如下:
在最后我们可以得出一些结论:
Unsafe序列化比标准的java.io.Serizlizable快了23倍
使用RandomAccessFile可以使标准的有缓冲序列化加速将近4倍
Kryo-dynamic序列化大约比手写实现的直接缓冲满了35%
最后,就像我们看到的那样,还是没有绝对的答案。对于我们中的大多数人来说,获得3000ns(0.003ms)的速度提升是不值得为每个需要序列化的对象来写单独实现的。在标准的方案中,我们大多数选择Kryo 。然而,在惜时如金的低延时系统中,这个选择将会是完全不同的。
OSCHINA编译,原文链接
例如,在对象<->文件序列化上就差距很大--尤其在读写内存对象上。我将就这个主题做一些解释和分享。
所有的测试都是在下面这个对象上执行的:
1 | public class TestObject implements Serializable { |
2 |
3 | private long longVariable; |
4 | private long [] longArray; |
5 | private String stringObject; |
6 | private String secondStringObject; //just for testing nulls |
7 |
8 | /* getters and setters */ |
9 | } |
最标准的java序列化(我们都是从这里学起的)是这样的:
01 | public void testWriteBuffered(TestObject test, String fileName) throws IOException { |
02 | ObjectOutputStream objectOutputStream = null ; |
03 | try { |
04 | FileOutputStream fos = new FileOutputStream(fileName); |
05 | BufferedOutputStream bos = new BufferedOutputStream(fos); |
06 | objectOutputStream = new ObjectOutputStream(bos); |
07 | objectOutputStream.writeObject(test); |
08 | } finally { |
09 | if (objectOutputStream != null ) { |
10 | objectOutputStream.close(); |
11 | } |
12 | } |
13 | } |
01 | public void testWriteBuffered(TestObject test, String fileName) throws IOException { |
02 | ObjectOutputStream objectOutputStream = null ; |
03 | try { |
04 | RandomAccessFile raf = new RandomAccessFile(fileName, "rw" ); |
05 | FileOutputStream fos = new FileOutputStream(raf.getFD()); |
06 | objectOutputStream = new ObjectOutputStream(fos); |
07 | objectOutputStream.writeObject(test); |
08 | } finally { |
09 | if (objectOutputStream != null ) { |
10 | objectOutputStream.close(); |
11 | } |
12 | } |
01 | private static Kryo kryo = new Kryo(); // version 2.x |
02 |
03 | public void testWriteBuffered(TestObject test, String fileName) throws IOException { |
04 | Output output = null ; |
05 | try { |
06 | RandomAccessFile raf = new RandomAccessFile(fileName, "rw" ); |
07 | output = new Output( new FileOutputStream(raf.getFD()), MAX_BUFFER_SIZE); |
08 | kryo.writeObject(output, test); |
09 | } finally { |
10 | if (output != null ) { |
11 | output.close(); |
12 | } |
13 | } |
14 | } |
For Java Object Serialisation),介绍了怎样在Java中像C++那样和内存打交道。
01 | public void testWriteBuffered(TestObject test, String fileName) throws IOException { |
02 | RandomAccessFile raf = null ; |
03 | try { |
04 | MemoryBuffer memoryBuffer = new MemoryBuffer(MAX_BUFFER_SIZE); |
05 | raf = new RandomAccessFile(fileName, "rw" ); |
06 | test.write(memoryBuffer); |
07 | raf.write(memoryBuffer.getBuffer()); |
08 | } catch (IOException e) { |
09 | if (raf != null ) { |
10 | raf.close(); |
11 | } |
12 | } |
13 | } |
01 | public void write(MemoryBuffer unsafeBuffer) { |
02 | unsafeBuffer.putLong(longVariable); |
03 | unsafeBuffer.putLongArray(longArray); |
04 | // we support nulls |
05 | boolean objectExists = stringObject != null ; |
06 | unsafeBuffer.putBoolean(objectExists); |
07 | if (objectExists) { |
08 | unsafeBuffer.putCharArray(stringObject.toCharArray()); |
09 | } |
10 | objectExists = secondStringObject != null ; |
11 | unsafeBuffer.putBoolean(objectExists); |
12 | if (objectExists) { |
13 | unsafeBuffer.putCharArray(secondStringObject.toCharArray()); |
14 | } |
15 | } |
01 | public class MemoryBuffer { |
02 | // getting Unsafe by reflection |
03 | public static final Unsafe unsafe = UnsafeUtil.getUnsafe(); |
04 |
05 | private final byte [] buffer; |
06 |
07 | private static final long byteArrayOffset = unsafe.arrayBaseOffset( byte []. class ); |
08 | private static final long longArrayOffset = unsafe.arrayBaseOffset( long []. class ); |
09 | /* other offsets */ |
10 |
11 | private static final int SIZE_OF_LONG = 8; |
12 | /* other sizes */ |
13 |
14 | private long pos = 0; |
15 |
16 | public MemoryBuffer(int bufferSize) { |
17 | this.buffer = new byte[bufferSize]; |
18 | } |
19 |
20 | public final byte[] getBuffer() { |
21 | return buffer; |
22 | } |
23 |
24 | public final void putLong(long value) { |
25 | unsafe.putLong(buffer, byteArrayOffset + pos, value); |
26 | pos += SIZE_OF_LONG; |
27 | } |
28 |
29 | public final long getLong() { |
30 | long result = unsafe.getLong(buffer, byteArrayOffset + pos); |
31 | pos += SIZE_OF_LONG; |
32 | return result; |
33 | } |
34 |
35 | public final void putLongArray(final long[] values) { |
36 | putInt(values.length); |
37 | long bytesToCopy = values.length << 3; |
38 | unsafe.copyMemory(values, longArrayOffset, buffer, byteArrayOffset + pos, bytesToCopy); |
39 | pos += bytesToCopy; |
40 | } |
41 |
42 |
43 | public final long[] getLongArray() { |
44 | int arraySize = getInt(); |
45 | long[] values = new long[arraySize]; |
46 | long bytesToCopy = values.length << 3; |
47 | unsafe.copyMemory(buffer, byteArrayOffset + pos, values, longArrayOffset, bytesToCopy); |
48 | pos += bytesToCopy; |
49 | return values; |
50 | } |
51 |
52 | /* other methods */ |
53 | } |
Full trip [ns] | Standard deviation [ns] | |
Standard | 207307 | 2362 |
Standard on RAF | 42661 | 733 |
KRYO 1.x | 12027 | 112 |
KRYO 2.x | 11479 | 259 |
Unsafe | 8554 | 91 |
Unsafe序列化比标准的java.io.Serizlizable快了23倍
使用RandomAccessFile可以使标准的有缓冲序列化加速将近4倍
Kryo-dynamic序列化大约比手写实现的直接缓冲满了35%
最后,就像我们看到的那样,还是没有绝对的答案。对于我们中的大多数人来说,获得3000ns(0.003ms)的速度提升是不值得为每个需要序列化的对象来写单独实现的。在标准的方案中,我们大多数选择Kryo 。然而,在惜时如金的低延时系统中,这个选择将会是完全不同的。
OSCHINA编译,原文链接
相关文章推荐
- 加快Java的文件序列化速度
- Java 文件读写最好是用buffer对于大文件可以加快速度
- Go_VS_Java_文件按行读写速度
- JAVA之旅(三十)——打印流PrintWriter,合并流,切割文件并且合并,对象的序列化Serializable,管道流,RandomAccessFile,IO其他类,字符编码
- 使用内存映射文件加快读取大文件的速度 .
- Java序列化与反序列化,文件操作
- JAVA IO - 将对象压缩到文件和将对象从压缩文件反序列化。
- 加快Windows Vista文件复制速度有高招
- JAVA之旅(三十)——打印流PrintWriter,合并流,切割文件并且合并,对象的序列化Serializable,管道流,RandomAccessFile,IO其他类,字符编码
- java高级---线程、网络、文件、流、序列化等代码示例
- ser文件与Java对象序列化
- 什么是序列化?一句话:就是将java对象固化成文件存起来,这样就java对象就被固话了,可以任意的存储和网络传输了,而java对象是暂时存在内存里的,是没办法传输的,因为是虚拟的,并不是实实在在的文件
- 加快打开XAML文件的速度
- 修改java阻塞数,加快他的运行速度
- 使用预编译头文件加快C++语言编译速度
- java流与文件——对象流和序列化
- 使用文件方式实现Java序列化
- HeadFirstJava——12_序列化和文件的输入/输出
- asp.net使用httphandler打包多CSS或JS文件以加快页面加载速度
- Java中用内存映射处理大文件可以显著提升速度