2

我必须对 Java 序列化提出疑问。

我只是使用 FileOutputStream 和 BufferedOutputStream 结合 Dataoutputstream 将 10 个大小为 int[] array = new int[2^28] 的数组写入我的硬盘(我知道这有点大,但我需要它) . 在每次序列化之前,我创建一个新的 FileOutputstream 和所有其他流,然后我关闭并刷新我的流。

问题:第一次序列化大约需要 2 秒,之后它会增加 tp 17 秒并保持在这个级别。这里有什么问题?如果我进入代码,我可以看到 FileOutputStreams 为 writeByte(...) 花费了大量时间。这是由于硬盘缓存(完整)吗?我怎样才能避免这种情况?我可以清除它吗?

这是我的简单代码:

    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) throws IOException {

    if (count + need > buf.length) {
        out.write(buf, 0, count);
        count = 0;
    }
}`

BufferedDataOutputStream 扩展了 BufferedOutputStream 与 fit 框架一起提供。它只是将 BufferedOutputStream 与 DataOutputStream 结合起来,以减少编写大数组时的方法调用次数(这使其速度更快......最多 10 倍......)。

这是输出:

起始基准

开始运行 0

2.001972271

开始运行 1

1.986544604

开始运行 2

15.663881232

开始运行 3

17.652161328

开始运行 4

18.020969301

开始运行 5

11.647542466

开始运行 6

为什么时间会增加这么多?

谢谢,

埃斯

4

2 回答 2

4

在这个程序中,我将 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 配置中使用多个驱动器也可以提高写入速度。

于 2011-09-13T16:53:24.953 回答
0

The first writes may just be filling up your hard drive's cache without actually writing to disk yet.

于 2011-09-13T17:03:38.633 回答