【问题标题】:Java file i/o throughput declineJava 文件 i/o 吞吐量下降
【发布时间】:2010-07-06 21:05:08
【问题描述】:

我有一个程序,其中每个线程一次从文件中读取多行文件,处理这些行,然后将这些行写到不同的文件中。四个线程在其中拆分要处理的文件列表。我在两种情况下遇到了奇怪的性能问题:

  • 四个文件,每个文件 50,000 行
    • 处理量从 700 行/秒开始,下降到 ~100 行/秒
  • 30,000 个文件,每个文件 12 行
    • 吞吐量开始时约为 800 行/秒并保持稳定

这是我正在开发的内部软件,很遗憾我无法分享任何源代码,但该程序的主要步骤是:

  1. 在四个工作线程之间拆分文件列表
  2. 启动所有线程。
  3. 线程一次最多读取 100 行并存储在 String[] 数组中。
  4. 线程将转换应用于数组中的所有行。
  5. 线程将行写入文件(与输入文件不同)。
  6. 每个线程重复 3-5 次,直到所有文件完全处理完毕。

我不明白为什么每个 12 行的 30k 个文件比每个有很多行的几个文件给我更好的性能。我本来希望打开和关闭文件的开销大于读取单个文件的开销。此外,前一种情况的性能下降是指数级的。

我已将最大堆大小设置为 1024 MB,而且它似乎最多使用 100 MB,因此负担过重的 GC 不是问题。您还有其他想法吗?

【问题讨论】:

    标签: java performance multithreading file-io


    【解决方案1】:

    从你的数字来看,我猜 GC 可能不是问题所在。我怀疑这是磁盘的正常行为,被许多并发线程操作。当文件很大时,磁盘必须在线程之间多次切换上下文(产生大量磁盘seek time),开销是显而易见的。对于小文件,可能它们被作为一个单独的块读取,没有额外的寻道时间,因此线程之间不会过多地相互干扰。

    在使用单个标准磁盘时,串行 IO 通常比并行 IO 更好。

    【讨论】:

    • 我会尝试重新编码,让主线程一次读取多行,允许多个工作线程处理,然后主线程再次将结果全部写出。谢谢!
    【解决方案2】:

    我假设文件位于同一个磁盘上,在这种情况下,您可能会在多个线程同时读取和同时写入的情况下破坏磁盘(或使磁盘\操作系统缓存无效)。更好的模式可能是有一个专用的读取器\写入器线程来处理 IO,然后更改您的模式,以便转换工作(听起来很昂贵)由多个线程处理。当结果可用时,您的 IO 线程可以使用转换操作获取和重叠写入。这应该会停止磁盘抖动,并平衡模式的 IO 和 CPU 方面。

    【讨论】:

      【解决方案3】:

      您是否尝试过运行 Java 分析器?这将指出您的代码的哪些部分运行最慢。从this discussion 看来,Netbeans profiler 是个不错的选择。

      【讨论】:

      • 我使用 Eclipse 的 MAT 插件查看了堆转储,但它并不是特别有用。在第一个案例中它告诉我的只是我存储了很多Strings,我知道。我会看看 Netbeans 的。
      • 我对堆上存储的内容并不真正感兴趣(立即)。相反,我想知道在这两种情况下哪些语句完成时间最长。这至少会告诉你是内存压力(创建字符串需要永远),还是文件 I/O(读取需要永远),文件访问(打开需要永远),还是其他的完全!
      【解决方案4】:

      您的线程很可能在缓冲的 String[] 上停留的时间过长。即使您的堆比您需要的大得多,吞吐量也可能会因垃圾收集而受到影响。看看你坚持了多久。

      您也可能在 vm 分配更多内存时等待 - 请求 Xmx1024m 不会立即分配那么多,它会在需要更多内存时获取所需的内容。您也可以尝试 -Xms1024m -Xmx1024m (即在开始时分配所有内存)来测试是否是这种情况。

      【讨论】:

      • 我确实启用了这两个选项。除了每次读取一行时都会分配新的字符串之外,相同的数组一直被重用,所以我假设任何引用被覆盖,可以说可以由 GC 立即收集。我应该在写出引用时将引用显式设置为 null 吗?
      【解决方案5】:

      您的线程可能会出现停止和锁定条件(一个线程将 100 行读入内存并保持锁定直到完成处理,而不是在完成从文件读取时放弃它)。我不是 Java 线程方面的专家,但值得考虑。

      【讨论】:

      • 嗯,每个线程都有自己的 Reader 和 Writer,没有两个线程接触过同一个文件。还会有锁定问题吗?
      • 我的猜测是,如果线程之间没有共享,就没有锁定问题。我想我最喜欢你选择的答案。
      【解决方案6】:

      我会审查这个过程。如果您使用 BufferedReader 和 BufferedWriter 一次读取和处理 100 行是没有优势的。它只是增加了复杂性和潜在错误的另一个来源。一次做一件,简化您的生活。

      【讨论】:

        猜你喜欢
        • 2013-07-05
        • 1970-01-01
        • 2020-05-18
        • 2010-09-25
        • 2013-10-26
        • 2017-09-29
        • 2012-09-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多