【问题标题】:Why is writing data to disk as fast as keeping it in-memory?为什么将数据写入磁盘与将数据保存在内存中一样快?
【发布时间】:2014-07-31 06:36:29
【问题描述】:

我有以下 10000000x2 矩阵:

0        0
1        1
2        2
..       ..
10000000 10000000

现在我想把这个矩阵保存到int[][]数组:

import com.google.common.base.Stopwatch;

static void memory(int size) throws Exception {
    System.out.println("Memory");

    Stopwatch s = Stopwatch.createStarted();

    int[][] l = new int[size][2];
    for (int i = 0; i < size; i++) {
        l[i][0] = i;
        l[i][1] = i;
    }

    System.out.println("Keeping " + size + " rows in-memory: " + s.stop());
}

public static void main(String[] args) throws Exception {
    int size = 10000000;
    memory(size);
    memory(size);
    memory(size);
    memory(size);
    memory(size);
}

输出:

Keeping 10000000 rows in-memory: 2,945 s
Keeping 10000000 rows in-memory: 408,1 ms
Keeping 10000000 rows in-memory: 761,5 ms
Keeping 10000000 rows in-memory: 543,7 ms
Keeping 10000000 rows in-memory: 408,2 ms

现在我想把这个矩阵保存到磁盘:

import com.google.common.base.Stopwatch;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;

static void file(int size, int fileIndex) throws Exception {
    Stopwatch s = Stopwatch.createStarted();

    FileOutputStream outputStream = new FileOutputStream("D:\\file" + fileIndex);
    BufferedOutputStream buf = new BufferedOutputStream(outputStream);
    for (int i = 0; i < size; i++) {
        buf.write(bytes(i));
        buf.write(bytes(i));
    }

    buf.close();
    outputStream.close();

    System.out.println("Writing " + size + " rows: " + s.stop());
}

public static void main(String[] args) throws Exception {
    int size = 10000000;
    file(size, 1);
    file(size, 2);
    file(size, 3);
    file(size, 4);
    file(size, 5);
}

输出:

Writing 10000000 rows: 715,8 ms
Writing 10000000 rows: 636,6 ms
Writing 10000000 rows: 614,6 ms
Writing 10000000 rows: 598,0 ms
Writing 10000000 rows: 611,9 ms

不应该更快地保存到内存吗?

【问题讨论】:

  • 您没有考虑到所有现代操作系统都有缓存,因此当您写入文件时,并不一定意味着会立即触及物理磁盘
  • OutputStream 是缓冲的,因此它只会写入内存,直到缓冲区已满再将其写入磁盘...您可以尝试在每次迭代时刷新缓冲区或完全摆脱它。 ..
  • 您没有直接写入 文件。您正在写入内存中的流。然后它将被异步写入硬盘。
  • @MadProgrammer 默认缓冲区大小仅为 8K。当然,OP 写的远不止这些。
  • 这里的 cmets 是正确的,BufferedOutputStream 中的缓冲和操作系统缓冲区缓存都会掩盖物理磁盘写入的延迟。我将在写入之后添加调用buf.flush() 然后outputStream.getChannel().force(true) 将强制写入一直到物理磁盘。大多数应用程序不会这样做,但如果您需要持久写入,它会很有用。

标签: java io


【解决方案1】:

正如 cmets 中所说,您没有测量任何有用的东西。 JVM 将写入操作缓存在其内存中,然后将其刷新到操作系统,操作系统将其缓存在其内存中,然后最终在某个时刻将其写入磁盘。
但是您只是在测量 JVM 将其缓存在自己的内存中所花费的时间(这是您可以测量的全部)。

无论如何,你不应该为这样的微优化而烦恼。

【讨论】:

  • 优化大量数据写入磁盘并不完全是“微”。
  • @MarkoTopolnik 是的,但担心写入内存缓存是否旨在处理写入磁盘或构建自己的缓存是......
  • 但是file 测试怎么可能更快?! BufferedOutputStream 肯定比将 int 分配给数组槽多一点。这个答案没有解释结果。
  • @usr:因为内存测试需要更多内存,需要从操作系统分配,这可能涉及首先将其他内容交换到磁盘,具体取决于机器的状态。 anything 的第一次迭代只有在您非常小心地控制机器环境时才能提供可靠的结果,如果在后台运行其他任何东西(几乎所有非古代操作系统都是如此),挂钟时间测量很少可重现,
  • @GuntramBlohm 您的评论至少开始解释这种行为。并不是说这个答案没有任何帮助,但它没有让人了解正在发生的事情。
【解决方案2】:

您的硬盘驱动器和操作系统采用写入缓冲,以便您的系统可以在面对多个并发任务(例如,读取和写入磁盘的程序)时继续运行。如果台式机出现电源故障,这可能(有时确实)会导致数据丢失。服务器和笔记本电脑也可能遇到此问题(但通常采用称为电池的复杂技术来减少这种可能性)。无论如何,在 Linux 上你可能需要fsck,而在 Windows 上你可能需要chkdsk

【讨论】:

    猜你喜欢
    • 2010-11-29
    • 2011-12-24
    • 1970-01-01
    • 2012-03-24
    • 2014-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多