java - 使用 java.io 对 native java 数组进行高效序列化

标签 java performance serialization fileoutputstream hard-drive

我有关于 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/

相关文章:

java - 记录来自 apache httpClient 的响应

java - java web 应用程序运行时在命令提示符中显示进度

sql-server - Pivoting 脚本的性能改进

sql - 稀疏连接中的 PostgreSQL 慢 WHERE 子句

java - 如何在没有双 for 循环的情况下使用 HashMap 填充二维数组

PHP 更新序列化数据

java.io.NotSerializableException : java. util.WeakHashMap

java - hibernate:mappedBy引用oneToMany关系中的未知目标实体属性

java - 在 java .spring 中创建一个临时数据持有者。 hibernate

python - 如何以 xml 节点的有序方式序列化 python ElementTree?