【问题标题】:Java - Using multiple threads to read/write to memory mapped buffers (MappedByteBuffer)Java - 使用多个线程读取/写入内存映射缓冲区 (MappedByteBuffer)
【发布时间】:2014-12-28 18:29:29
【问题描述】:

我有一个应用程序,其中发生了很多文件 I/O(读取和写入)。我知道使用多个线程来执行文件 I/O 不是一个好的解决方案,因为它会降低性能(我无法控制所使用的磁盘类型)。所以我最终为所有文件 I/O 指定了一个线程。 MappedByteBuffer 在我的情况下可以使用吗?我知道 MappedByteBuffer 是一个由操作系统映射到文件的内存区域,我可以利用多个线程有效地对不同的内存映射缓冲区进行 I/O 操作吗?当多个线程将不同的文件映射到不同的内存缓冲区时,磁盘磁头寻道时间是否仍然重要?在这种情况下是否保证一致性?是否有适用于此类情况的基准测试结果?提前谢谢大家。

【问题讨论】:

  • “我知道使用多线程来做文件I/O不是一个好的解决方案,因为它会降低性能”这是基于什么?
  • 磁盘的磁头需要不断寻找下一个要读取的位置,当多个线程这样做时,磁头会在不同的磁盘区域之间低效地弹跳。
  • @skytreader 感谢您指出这一点,我应该在描述中使用“MappedByteBuffer”。
  • 我可能会找到很多说它更好的东西,比如这个stackoverflow.com/a/1239987/360211 关键是不要接受任何人的说法和个人资料。个人经验是,它提高了整体吞吐量,不仅仅是磁头寻道时间,还涉及其他延迟,这些延迟在并行完成时会被最小化。
  • 唯一可以肯定地说的是“这取决于”。内存映射文件在某些​​情况下确实有一些优势——在其他情况下它们会花费你的时间。在某种程度上也相关:ibm.com/developerworks/java/library/j-zerocopy 关于较少上下文切换的部分也应该适用于内存映射文件。如果您想为您的用例找到正确的答案,请实施所有方法、配置文件和优化,直到获得最佳答案。

标签: java multithreading file-io memory-mapped-files


【解决方案1】:

只要您不尝试在给定时间让多个线程写入同一个文件,从不同线程执行文件 I/O 就没有问题。使用 NIO,FileSystem 实现在管理磁盘写入和资源方面比您希望的要好得多。默认情况下,磁盘写入在 Java 中是缓冲和异步的,因此不需要做像让单个线程完成所有 I/O 并写入内存缓冲区那样复杂的事情——这几乎正是 OutputStreams 写入磁盘所做的事情,但是原生 JVM 会比你更有效地做到这一点。

事实上,文件 I/O 操作可以从多线程中受益匪浅。不同的线程可以在其他线程正在读取的同时处理读取信息,有时并行读取或写入几个文件甚至比顺序读取或写入更快。

【讨论】:

  • “不同线程可以在其他线程正在读取的同时处理读取信息”我担心“不同线程读取/写入磁盘”的性能比单个线程执行此操作要差。如果您可以分享任何基准测试结果来支持这一点,将会很有帮助。
【解决方案2】:

MappedByteBuffer 对我来说有什么用吗?

参考JavaDoc,与 ByteBuffer 相比,MappedByteBuffer 不会给您带来任何性能优势。您甚至可能在运行时遇到一些意想不到的变化

映射字节缓冲区的内容可以随时更改,例如 例如,如果映射文件的相应区域的内容 被此程序或其他程序更改。


我知道 MappedByteBuffer 是一个由操作系统映射到文件的内存区域,我可以利用多个线程有效地对不同的内存映射缓冲区进行 I/O 操作吗?

除非您比您的操作系统或虚拟机更了解如何有效地读取和写入数据,否则情况并非如此。


多线程时磁盘磁头寻道时间是否仍然重要? 将不同的文件映射到不同的内存缓冲区?

头部仍然需要寻找它的位置。除非你有不同的磁盘并且你只做磁盘 IO,否则拥有多个线程是没有用的。如果您有一些冗余读取数据,多线程应该很有用,因为您的操作系统将缓存“热”数据。


在这种情况下是否保证一致性?

不太确定,你的意思是什么,但你必须确保访问你的 ByteBuffer 是同步的,因为它不是线程安全的数据结构。


是否有适用于此类情况的基准测试结果?

去年我做了一些基准测试,使用多个缓冲区。长话短说,这实际上取决于用例、操作系统和您的硬件。根据这有多重要,我建议您进行自己的基准测试。我记得的唯一不变的是,您在写入磁盘段大小的数据块时获得了最佳性能……这在某种程度上是显而易见的;-)

【讨论】:

  • 感谢您的回答,关于从多个线程访问的文件系统的主题仍然存在很多困惑,这里的一些相互冲突的答案也对我没有帮助。就内存映射缓冲区而言,我会自己做一些基准测试,如果您也可以分享您的结果将会很有帮助。
【解决方案3】:

如果您建议要将同一文件的不同区域映射到不同的 MappedByteBuffers,并希望将这种方式写入文件与单线程、阻塞、无缓冲写入同一文件进行比较,我很好确保您对性能方面的结果感到非常满意。

您应该记住,在写入 MemoryMappedBuffers 时,当您请求执行写入时,您不一定要写入磁盘。操作系统负责决定哪些 MemoryMappedBuffers 对应于 RAM,以及该 RAM 何时写回磁盘;通常这意味着在写入时,该文件或文件的一部分保存在 RAM 中,并且文件由操作系统自行决定写回磁盘,这可能意味着它一直保存在内存中,直到看起来你完成了写入它,然后移动到磁盘,或者将它保存在 RAM 中,直到它占用的 RAM 需要用于其他用途,除非您 force() 将它写入磁盘。

我认为,从性能的角度来看,这在很大程度上取决于您的目标:您是否希望执行写入的算法更快地完成,在这种情况下,内存映射区域可能是一个不错的选择,因为算法可以在文件完成写入磁盘之前完成,或者您是否希望将文件更快地复制到磁盘,在这种情况下很难说:如果您能够将文件分解成可以有效写入的大块磁盘,并且如果操作系统能够识别您何时完成一个区域并且在此过程中只将每个区域写回磁盘一次,那么它可能会更有效。

另一方面,如果您当前的实现非常高效地写入磁盘,即,如果您成功地有效地安排了对文件的写入,那么几乎不需要查找(如果使用硬盘),并且写入是适当缓冲,这样您就不会强迫操作系统在允许它拥有文件的下一位之前将文件的一小部分一直写入磁盘,或者随机写入字节(即使是固态驱动器也不喜欢,因为它们必须写入一定大小的区域,并且不能单独写入单个字节),那么您当前的策略完全有可能更快地完成将文件写入磁盘 - 假设尽可能快地将文件写入物理磁盘是目标。

如果您想知道还有多少改进空间,您可以将您的速度与系统上的硬盘性能测试速度进行比较,这应该能够对您的磁盘吞吐量限制进行基准测试;如果这比您当前的实现快得多,那么您的编写策略还有改进的空间,或者它正在生成数据而不是编写数据,这需要时间。

要测试后者,您可以尝试将算法写入未映射内存的 ByteBuffer;在没有文件 I/O 的情况下,您可以独立于磁盘对算法的速度进行基准测试。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多