使用java.io高效地序列化原生java数组



我有一个关于Java序列化的问题。

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

问题:第一次序列化大约需要2秒,然后增加到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)抛出IOException {

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

BufferedDataOutputStream扩展了BufferedOutputStream随fits框架一起提供。它只是将BufferedOutputStream与DataOutputStream结合起来,以减少编写大数组时方法调用的数量(这使它更快……)。

输出如下:

基准开始

开始运行0

2.001972271

开始运行1

1.986544604

开始运行2

15.663881232

开始跑3

17.652161328

开始跑4

18.020969301

开始跑步5

11.647542466

开始运行6

为什么时间增加这么多?

谢谢你,

Eeth

在这个程序中,我将1gb填充为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硬盘驱动器。如果你得到比这更高的速度,那是因为数据并没有真正写入磁盘,实际上是在内存中。

如果你想让这个更快,你可以使用内存映射文件。这样做的好处是在后台写入磁盘,因为你正在填充"数组",也就是说,它可以完成写入几乎只要你完成设置的值。

另一个选择是得到一个更快的驱动器。单个250gb的SSD驱动器可以维持大约200mb/s的写速度。在RAID配置中使用多个驱动器还可以提高写速度。

第一次写操作可能只是填满了硬盘的缓存,而没有实际写入磁盘。

最新更新