通过快速Java和文件序列化加快速度
從Java的第一個(gè)版本開(kāi)始,每天都有許多開(kāi)發(fā)人員試圖至少達(dá)到與C / C ++一樣好的性能。 JVM供應(yīng)商正在通過(guò)實(shí)現(xiàn)一些新的JIT算法來(lái)盡力而為,但仍有許多工作要做,尤其是在我們?nèi)绾问褂肑ava方面。
例如,對(duì)象<->文件序列化有很多優(yōu)勢(shì),尤其是在易于存儲(chǔ)的對(duì)象的讀寫(xiě)中。 我將嘗試闡明該主題。
所有測(cè)試都在下面顯示的簡(jiǎn)單對(duì)象上執(zhí)行:
為了更簡(jiǎn)潔,我將僅顯示write方法(盡管另一種方法也非常相似)。 完整的源代碼可在我的GitHub(http://github.com/jkubrynski/serialization-tests)上找到。
最標(biāo)準(zhǔn)的Java序列化(我們都從這里開(kāi)始)如下所示:
public void testWriteBuffered(TestObject test, String fileName) throws IOException {ObjectOutputStream objectOutputStream = null;try {FileOutputStream fos = new FileOutputStream(fileName);BufferedOutputStream bos = new BufferedOutputStream(fos);objectOutputStream = new ObjectOutputStream(bos);objectOutputStream.writeObject(test);} finally {if (objectOutputStream != null) {objectOutputStream.close();}} }加快標(biāo)準(zhǔn)序列化的最簡(jiǎn)單方法是使用RandomAccessFile對(duì)象:
public void testWriteBuffered(TestObject test, String fileName) throws IOException {ObjectOutputStream objectOutputStream = null;try {RandomAccessFile raf = new RandomAccessFile(fileName, "rw");FileOutputStream fos = new FileOutputStream(raf.getFD());objectOutputStream = new ObjectOutputStream(fos);objectOutputStream.writeObject(test);} finally {if (objectOutputStream != null) {objectOutputStream.close();} }更復(fù)雜的技術(shù)是使用Kryo框架。 新舊版本之間的差異很大。 我都檢查了。 因?yàn)樾阅鼙容^沒(méi)有發(fā)現(xiàn)任何明顯的不同,所以我將重點(diǎn)介紹第二個(gè)版本,因?yàn)樗佑脩粲押?#xff0c;甚至更快。
private static Kryo kryo = new Kryo(); // version 2.xpublic void testWriteBuffered(TestObject test, String fileName) throws IOException {Output output = null;try {RandomAccessFile raf = new RandomAccessFile(fileName, "rw");output = new Output(new FileOutputStream(raf.getFD()), MAX_BUFFER_SIZE);kryo.writeObject(output, test);} finally {if (output != null) {output.close();}} }最后一個(gè)選擇是受Martin Thompson文章啟發(fā)的解決方案。 它顯示了如何以C ++方式和Java處理內(nèi)存
public void testWriteBuffered(TestObject test, String fileName) throws IOException {RandomAccessFile raf = null;try {MemoryBuffer memoryBuffer = new MemoryBuffer(MAX_BUFFER_SIZE);raf = new RandomAccessFile(fileName, "rw");test.write(memoryBuffer);raf.write(memoryBuffer.getBuffer());} catch (IOException e) {if (raf != null) {raf.close();}} }TestObject的寫(xiě)入方法如下所示:
public void write(MemoryBuffer unsafeBuffer) {unsafeBuffer.putLong(longVariable);unsafeBuffer.putLongArray(longArray);// we support nullsboolean objectExists = stringObject != null;unsafeBuffer.putBoolean(objectExists);if (objectExists) {unsafeBuffer.putCharArray(stringObject.toCharArray());}objectExists = secondStringObject != null;unsafeBuffer.putBoolean(objectExists);if (objectExists) {unsafeBuffer.putCharArray(secondStringObject.toCharArray());} }直接內(nèi)存緩沖區(qū)類(lèi)(簡(jiǎn)稱(chēng),只是為了展示這個(gè)主意):
public class MemoryBuffer {// getting Unsafe by reflectionpublic static final Unsafe unsafe = UnsafeUtil.getUnsafe();private final byte[] buffer;private static final long byteArrayOffset = unsafe.arrayBaseOffset(byte[].class);private static final long longArrayOffset = unsafe.arrayBaseOffset(long[].class);// other offsets private static final int SIZE_OF_LONG = 8;// other sizes private long pos = 0;public MemoryBuffer(int bufferSize) {this.buffer = new byte[bufferSize];}public final byte[] getBuffer() {return buffer;}public final void putLong(long value) {unsafe.putLong(buffer, byteArrayOffset + pos, value);pos += SIZE_OF_LONG;}public final long getLong() {long result = unsafe.getLong(buffer, byteArrayOffset + pos);pos += SIZE_OF_LONG;return result;}public final void putLongArray(final long[] values) {putInt(values.length);long bytesToCopy = values.length << 3;unsafe.copyMemory(values, longArrayOffset, buffer, byteArrayOffset + pos, bytesToCopy);pos += bytesToCopy;}public final 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;}/* other methods */ }卡尺運(yùn)行多個(gè)小時(shí)的結(jié)果如下所示:
| 標(biāo)準(zhǔn) | 207307 | 2362 |
| 英國(guó)皇家空軍的標(biāo)準(zhǔn) | 42661 | 733 |
| KRYO 1.x | 12027 | 112 |
| KRYO 2.x | 11479 | 259 |
| 不安全 | 8554 | 91 |
最后我們可以得出一些結(jié)論:
- 不安全的序列化比java.io.Serializable的標(biāo)準(zhǔn)用法快23倍以上
- 使用RandomAccessFile可以將標(biāo)準(zhǔn)緩沖序列化速度提高近4倍
- Kryo動(dòng)態(tài)序列化比手工實(shí)現(xiàn)的直接緩沖區(qū)慢約35%。
最后,我們可以看到,仍然沒(méi)有金錘。 對(duì)于我們很多人來(lái)說(shuō),獲得3000 ns(0.003ms)的值不值得為我們要與文件序列化的每個(gè)對(duì)象編寫(xiě)自定義實(shí)現(xiàn)。 對(duì)于標(biāo)準(zhǔn)解決方案,我們主要選擇Kryo。 然而,在低延遲系統(tǒng)中,100ns似乎是永恒的,選擇將完全不同。
翻譯自: https://www.javacodegeeks.com/2013/09/speed-up-with-fast-java-and-file-serialization.html
總結(jié)
以上是生活随笔為你收集整理的通过快速Java和文件序列化加快速度的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Java中的Memento设计模式-示例
- 下一篇: 使用Spring 3.2的Deferre