我需要在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)似乎是最快的。
选择的数量和一些相互矛盾的信息使我感到困惑,所以有人能澄清这些疑惑吗?
我希望我写下所有必要的信息,否则只是要求澄清。
提前致谢。
答案 0 :(得分:1)
案例5:DataOutputStream
写信给ByteArrayOutputStream.
亲:它已经完成了;它和你在这里提到的任何其他东西一样快;所有原语都已实现。反过来是从ByteArrayInputStream读取的DataInputStream。
骗局:我无法想到。