Java:序列化为字节缓冲区的最快方法

时间:2014-09-06 21:51:31

标签: java arrays performance memory serialization

我需要在Java中使用必须尽可能快的序列化库。我们的想法是创建各种方法,这些方法将序列化指定的值及其关联的键,并将它们放在字节缓冲区中。必须创建包含此缓冲区的多个对象,因为需要序列化的对象可能很多。

考虑: 我知道每个JVM中可能没有实现Unsafe类,但它不是问题。 过早优化:这个库必须快速,这个序列化是它唯一必须做的事情。 一旦序列化的对象非常小(小于10k)但它们很多并且它们可以高达2Gb。 底层缓冲区可以扩展/缩小,但我会跳过实现细节,该方法类似于ArrayList实现中使用的方法。

澄清我的情况:我有各种方法,如

public void putByte(short key, byte value);
public void putInt(short key, int value);
public void putFloat(short key, float value);

... and so on...

这些方法将键和值附加到字节流中,所以如果我调用putInt(-1,1234567890),我的缓冲区将如下所示:(流是大端)

     key       the integer value  
[0xFF, 0xFF, 0x49, 0x96, 0x02, 0xD2]

最后,必须调用 toBytes()之类的方法来返回一个字节数组,该数组是基础缓冲区的修剪版(如果需要)。

现在,我的问题是:在java中执行此操作的最快方法是什么?

我用谷歌搜索并偶然发现了各种各样的页面(其中一些是关于这样的)我也做了一些基准测试(但我在基准测试中并没有真正的经验,这也是我'请求有经验的程序员帮助解决这个问题。)

我提出了以下解决方案:

1-最直接的:一个字节数组

如果我必须序列化一个int,它将如下所示:

public void putInt(short key, int value)
{
    array[index]   = (byte)(key >> 8);
    array[index+1] = (byte) key;
    array[index+2] = (byte)(value >> 24);
    array[index+3] = (byte)(value >> 16);
    array[index+4] = (byte)(value >> 8);
    array[index+5] = (byte) value;
}

2- A ByteBuffer (无论是直接还是字节数组包装器)

putInt方法如下所示

public void putInt(short key, int value)
{
   byteBuff.put(key).put(value);
}

3-通过不安全的本机内存分配

使用Unsafe类我会在本机内存上分配缓冲区,因此putInt看起来像:

public void putInt(short key, int value)
{
  Unsafe.putShort(address, key);
  Unsafe.putInt(address+2, value);
}
通过新字节[] 进行4-分配,通过不安全

进行访问

我在用java编写的lz4压缩库中看到了这个方法。基本上,一旦实例化了一个字节数组,我就按以下方式写字节:

public void putInt(short key, int value)
{
   Unsafe.putShort(byteArray, BYTE_ARRAY_OFFSET + 0, key);
   Unsafe.putInt(byteArray, BYTE_ARRAY_OFFSET + 2, value);
}

这里的方法是简化的,但基本思路是显示的,我还必须实现getter方法。现在,自从我开始从事这项工作后,我学到了以下几点:

1-如果JVM安全(在for循环中,例如计数器必须小于数组的长度),JVM可以删除数组边界检查 2-跨越JVM内存边界(从/向本机内存读取/写入)需要付出代价。 3-调用本机方法可能会产生成本。 4-不安全的推杆和吸气剂不能在本机存储器中进行边界检查,也不能在常规阵列上进行边界检查。 5- ByteBuffers包装一个字节数组(非直接)或一个普通的本机存储区(直接),所以case 2在内部看起来像是case 1或3.

我运行了一些基准测试(但正如我所说,我希望得到其他开发人员的意见/经验)并且看起来案例4在阅读时略微(几乎等于)案例1并且写作速度提高了约3倍。似乎带有Unsafe读写的for循环(案例4)将数组复制到另一个(在时间复制8个字节)比System.arraycopy更快。

长篇故事简短(对不起长篇文章):

案例1 似乎很快,但是这样我每次都要写一个字节+屏蔽操作,这让我觉得可能不安全,即使它调用了本机代码可能更快。

案例2 类似于案例1和案例3,所以我可以跳过它(如果我错过了某些内容,请纠正我)

案例3 似乎是最慢的(至少从我的基准测试中),而且,我需要从本机内存复制到字节数组,因为它必须是输出。但here这位程序员声称它是迄今为止最快的方式。如果我理解正确,我错过了什么?

案例4 (支持here)似乎是最快的。

选择的数量和一些相互矛盾的信息使我感到困惑,所以有人能澄清这些疑惑吗?

我希望我写下所有必要的信息,否则只是要求澄清。

提前致谢。

1 个答案:

答案 0 :(得分:1)

案例5:DataOutputStream写信给ByteArrayOutputStream.

亲:它已经完成了;它和你在这里提到的任何其他东西一样快;所有原语都已实现。反过来是从ByteArrayInputStream读取的DataInputStream。

骗局:我无法想到。