【问题标题】:What is the fastest way to write a large amount of data from memory to a file?将大量数据从内存写入文件的最快方法是什么?
【发布时间】:2012-04-22 00:40:11
【问题描述】:

我有一个程序可以生成大量数据并将其放入队列中以进行写入,但问题是它生成数据的速度比我当前正在写入的速度快(导致它达到最大内存并开始变慢)。顺序无关紧要,因为我打算稍后解析文件。

我环顾四周,发现了一些帮助我设计当前流程的问题(但我仍然觉得它很慢)。到目前为止,这是我的代码:

//...background multi-threaded process keeps building the queue..
FileWriter writer = new FileWriter("foo.txt",true);
        BufferedWriter bufferWritter = new BufferedWriter(writer);
        while(!queue_of_stuff_to_write.isEmpty()) {
            String data = solutions.poll().data;
            bufferWritter.newLine();
            bufferWritter.write(data);
        }
        bufferWritter.close();

我对编程很陌生,所以我可能会评估这个错误(可能是我使用 EC2 时的硬件问题),但是是否可以非常快速地将队列结果转储到文件中,或者如果我的方法可以我以某种方式改进它?由于顺序无关紧要,写入多个驱动器上的多个文件是否更有意义?线程会使它更快吗?等等。我不确定最好的方法,任何建议都会很棒。我的目标是保存队列的结果(抱歉没有输出到 /dev/null :-) 并让我的应用程序的内存消耗尽可能低(我不是 100% 确定,但队列填满了 15gig,所以我'我假设它将是一个 15gig+ 的文件)。

Fastest way to write huge data in text file Java(意识到我应该使用缓冲写入器) Concurrent file write in Java on Windows(让我看到多线程写入可能不是一个好主意)

【问题讨论】:

  • 我了解cpu速度>硬盘速度,所以写入可能总是输给处理,我只是想弄清楚如何帮助高清速度更接近处理它。
  • 很大程度上取决于你的瓶颈是什么。我怀疑如果您最大化磁盘 IO 的带宽(这似乎是您的问题),您也可以最大化您的帐户(就成本而言)我同意多线程写入不会有太大帮助。
  • 粗略计算,15 GB 每次花费 4 美元。
  • @PeterLawrey 临时存储没有成本(它包含在实例中,但不是持久的),我的可用空间不到 TB。
  • 如果您不关心成本,我会先看看尽可能快地写入文件需要多长时间。例如从命令行使用dd。或者您可以在大块中使用 NIO,例如32-256KB。

标签: java file io


【解决方案1】:

我猜只要您通过计算生成数据并且不从其他数据源加载数据,写入总是比生成数据慢。

您可以尝试在多个线程中将数据写入多个文件(不是在同一个文件中 -> 由于同步问题)(但我想这不会解决您的问题)。

您是否可以等待应用程序的编写部分完成其操作并继续计算?

另一种方法是: 你清空你的队列吗? Solutions.poll() 是否会减少您的解决方案队列?

【讨论】:

    【解决方案2】:

    使用多个线程写入不同的文件是个好主意。此外,您应该考虑设置 BufferedWriters 缓冲区大小,您可以从构造函数中执行此操作。尝试使用 10 Mb 缓冲区进行初始化,看看是否有帮助

    【讨论】:

    • 是吗?将两个文件并行写入同一个机械硬盘比先写入一个,然后再写入另一个花费的时间要长得多。
    【解决方案3】:

    查看该代码,我想到的一件事是字符编码。您正在编写字符串,但最终,它是流向流的字节。引擎盖下的写入器字符到字节编码,它在处理写入的同一线程中进行。这可能意味着有时间用于延迟写入的编码,这可能会降低写入数据的速率。

    一个简单的改变是使用byte[]而不是String的队列,在推送到队列的线程中进行编码,并让IO代码使用BufferedOutputStream而不是BufferedWriter .

    如果编码文本平均每个字符占用少于两个字节,这也可以减少内存消耗。对于拉丁文本和 UTF-8 编码,这通常是正确的。

    但是,我怀疑您生成数据的速度可能比您的 IO 子系统处理它的速度快。您将需要使您的 IO 子系统更快 - 通过使用更快的子系统(如果您在 EC2 上,可能租用更快的实例,或者写入不同的后端 - SQS、EBS 和本地磁盘等),或者通过组合几个 IO 子系统以某种方式并行在一起。

    【讨论】:

      【解决方案4】:

      是的,在多个驱动器上写入多个文件应该会有所帮助,如果没有其他东西同时写入这些驱动器,性能应该随着驱动器数量线性扩展,直到 I/O 不再是瓶颈。您还可以尝试其他一些优化来进一步提高性能。

      如果您正在生成巨大的文件而磁盘根本跟不上,您可以使用 GZIPOutputStream 来缩小输出,这反过来又会减少磁盘 I/O 的数量。对于非随机文本,您通常可以期望压缩比至少为 2x-10x。

          //...background multi-threaded process keeps building the queue..
          OutputStream out = new FileOutputStream("foo.txt",true);
          OutputStreamWriter writer = new OutputStreamWriter(new GZIPOutputStream(out));
          BufferedWriter bufferWriter = new BufferedWriter(writer);
          while(!queue_of_stuff_to_write.isEmpty()) {
              String data = solutions.poll().data;
              bufferWriter.newLine();
              bufferWriter.write(data);
          }
          bufferWriter.close();
      

      如果您要输出常规(即重复)数据,您可能还需要考虑切换到不同的输出格式,例如数据的二进制编码。根据数据的结构,将其存储在数据库中可能更有效。如果您正在输出 XML 并且真的想坚持使用 XML,您应该查看Binary XML 格式,例如 EXI 或 Fast InfoSet。

      【讨论】:

        猜你喜欢
        • 2010-10-12
        • 2023-03-21
        • 1970-01-01
        • 2015-02-07
        • 1970-01-01
        • 1970-01-01
        • 2020-09-01
        • 2010-11-06
        相关资源
        最近更新 更多