我有关于 Java 序列化的问题。
我只是结合使用 FileOutputStream 和 BufferedOutputStream 将 10 个大小为 int[] array = new int[2^28] 的数组写入我的 Harddik (我知道这有点大,但我需要这样)带有数据输出流。在每次序列化之前,我创建一个新的 FileOutputstream 和所有其他流,然后关闭并刷新我的流。
问题: 第一次序列化大约需要 2 秒,之后增加到 17 秒并保持在这个水平。这里有什么问题?如果我进入代码,我可以看到 FileOutputStreams 花费大量时间来 writeByte(...)。这是由于 HDD 缓存(已满)吗?我怎样才能避免这种情况?可以清除吗?
这是我的简单代码:
public static void main(String[] args) throws IOException {
System.out.println("### Starting test");
for (int k = 0; k < 10; k++) {
System.out.println("### Run nr ... " + k);
// Creating the test array....
int[] testArray = new int[(int) Math.pow(2, 28)];
for (int i = 0; i < testArray.length; i++) {
if (i % 2 == 0) {
testArray[i] = i;
}
}
BufferedDataOutputStream dataOut = new BufferedDataOutputStream(
new FileOutputStream("e:\\test" + k + "_" + 28 + ".dat"));
// Serializing...
long start = System.nanoTime();
dataOut.write(testArray);
System.out.println((System.nanoTime() - start) / 1000000000.0
+ " s");
dataOut.flush();
dataOut.close();
}
}
其中 dataOut.write(int[], 0, end)
public void write(int[] i, int start, int len) throws IOException {
for (int ii = start; ii < start + len; ii += 1) {
if (count + 4 > buf.length) {
checkBuf(4);
}
buf[count++] = (byte) (i[ii] >>> 24);
buf[count++] = (byte) (i[ii] >>> 16);
buf[count++] = (byte) (i[ii] >>> 8);
buf[count++] = (byte) (i[ii]);
}
}
并且 `protected void checkBuf(int need) 抛出 IOException {
if (count + need > buf.length) {
out.write(buf, 0, count);
count = 0;
}
}`
BufferedDataOutputStream 扩展了 Fits 框架附带的 BufferedOutputStream。它只是将 BufferedOutputStream 与 DataOutputStream 结合起来,以减少写入大数组时的方法调用次数(这使其速度更快......最多 10 倍......)。
这是输出:
Starting benchmark
STARTING RUN 0
2.001972271
STARTING RUN 1
1.986544604
STARTING RUN 2
15.663881232
STARTING RUN 3
17.652161328
STARTING RUN 4
18.020969301
STARTING RUN 5
11.647542466
STARTING RUN 6
为什么时间增加这么多?
谢谢,
以太
最佳答案
在此程序中,我将 1 GB 填充为 int 值,并“强制”将它们写入磁盘。
String dir = args[0];
for (int i = 0; i < 24; i++) {
long start = System.nanoTime();
File tmp = new File(dir, "deleteme." + i);
tmp.deleteOnExit();
RandomAccessFile raf = new RandomAccessFile(tmp, "rw");
final MappedByteBuffer map = raf.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 1 << 30);
IntBuffer array = map.order(ByteOrder.nativeOrder()).asIntBuffer();
for (int n = 0; n < array.capacity(); n++)
array.put(n, n);
map.force();
((DirectBuffer) map).cleaner().clean();
raf.close();
long time = System.nanoTime() - start;
System.out.printf("Took %.1f seconds to write 1 GB%n", time / 1e9);
}
每个文件都强制写入磁盘,每个文件花费的时间大约相同。
Took 7.7 seconds to write 1 GB
Took 7.5 seconds to write 1 GB
Took 7.7 seconds to write 1 GB
Took 7.9 seconds to write 1 GB
Took 7.6 seconds to write 1 GB
Took 7.7 seconds to write 1 GB
但是,如果我注释掉 map.force();
,我就会看到此配置文件。
Took 0.8 seconds to write 1 GB
Took 1.0 seconds to write 1 GB
Took 4.9 seconds to write 1 GB
Took 7.2 seconds to write 1 GB
Took 7.0 seconds to write 1 GB
Took 7.2 seconds to write 1 GB
Took 7.2 seconds to write 1 GB
看起来它会缓冲大约 2.5 GB,大约是我主内存的 10%,然后速度就会变慢。
您可以通过等待之前的写入完成来清除缓存。
基本上,您有 1 GB 数据,磁盘的持续写入速度约为 60 MB/s,这对于 SATA 硬盘来说是合理的。如果您获得的速度高于此速度,那是因为数据尚未真正写入磁盘,而是实际上位于内存中。
如果您希望速度更快,可以使用内存映射文件。这样做的好处是,当您填充“数组”时,可以在后台写入磁盘,即几乎在您完成设置值后就可以完成写入。
另一个选择是获得更快的驱动器。单个 250 GB SSD 驱动器可维持约 200 MB/s 的写入速度。在 RAID 配置中使用多个驱动器也可以提高写入速度。
关于java - 使用 java.io 对 native java 数组进行高效序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7405468/