多线程 ByteBuffers 比顺序慢?

Posted

技术标签:

【中文标题】多线程 ByteBuffers 比顺序慢?【英文标题】:Multi-threaded ByteBuffers slower than sequential? 【发布时间】:2016-06-03 20:01:45 【问题描述】:

我有一个巨大的字节数组需要处理。从理论上讲,应该可以将工作分成均匀的部分并将它们分配给不同的线程,以提高多核机器上的性能。

我为每个线程分配了一个ByteBuffer,并分别处理了部分数据。即使我有 8 个逻辑处理器,最终性能也比单线程慢。也是非常不协调的。有时,相同的输入处理速度会慢一倍或更多。这是为什么?数据首先加载到内存中,因此不再执行IO 操作。

我使用MappedByteBuffer 分配我的ByteBuffers,因为它比ByteBuffer.wrap() 快:

public ByteBuffer getByteBuffer() throws IOException

    File binaryFile = new File("...");
    FileChannel binaryFileChannel = new RandomAccessFile(binaryFile, "r").getChannel();

    return binaryFileChannel.map(FileChannel.MapMode.READ_ONLY, 0, binaryFileChannel.size());

我使用Executors 进行并发处理:

int threadsCount = Runtime.getRuntime().availableProcessors();
ExecutorService executorService = Executors.newFixedThreadPool(threadsCount);
ExecutorCompletionService<String> completionService = new ExecutorCompletionService<>(executorService);

for (ByteBufferRange byteBufferRange : byteBufferRanges)

    Callable<String> task = () ->
    
        performTask(byteBufferRange);

        return null;
    ;

    completionService.submit(task);


// Wait for all tasks to finish
for (ByteBufferRange ignored : byteBufferRanges)

    completionService.take().get();


executorService.shutdown();

并发任务performTask() 使用自己的ByteBuffer 实例从缓冲区读取内存,进行计算等。它们不同步、不写入或相互影响。任何想法出了什么问题,或者这不是并行化的好例子吗?

ByteBuffer.wrap()MappedByteBuffer 存在同样的问题。

【问题讨论】:

你说这个数组有多大? 映射缓冲区并不是真正加载到内存中的文件。当您读取文件内容时,操作系统会将文件内容的块(页面)动态映射到内存中,并在您在不同位置读取时将数据交换为其他数据。意味着您使用的实际内存非常少,而它看起来好像您的内存中有 TB。但也意味着跳转可能需要从磁盘重新读取。 @LoganKulinski:几个 100MB 这不是关于并行化 ByteBuffers 的真正问题。这是一个关于并行读取单个文件的问题。我希望文件数据的相邻部分更有可能在媒体上彼此相邻,因此按顺序访问比在文件中的各个位置查找要快一些。 没有什么特别的理由为什么多线程应该让它更快。磁盘不是多线程的。 【参考方案1】:

正如@EJP 提到的,磁盘并不是真正的多线程,尽管 SSD 可能会有所帮助。映射缓冲区的目的是让您不必自己管理内存;让操作系统来做吧,因为它的虚拟内存管理器和文件系统缓存会比将其移动到 Java 堆中更快,并且可能比您编写的任何内存管理代码都快。

如果处理确实可以并行化,最好让一个线程读取整个文件,将其分成块(可能以某种中间数据格式),然后让执行程序处理这些块。文件读取线程可以与其他线程并发运行,因此您无需读取整个文件即可开始处理。

您可能想尝试将执行程序的数量设置为cores - 1,这样您就不会饿死文件读取线程。这将使操作系统有机会在没有上下文切换的情况下保持文件读取线程在单个内核上运行,因此您将获得良好的 IO 性能,同时使用其他内核执行 CPU 密集型工作。

仅供参考,这就是构建 Apache Spark 的目的。如果您需要处理更大的文件或需要比单个系统更快的处理速度,您可能需要查看它。

【讨论】:

以上是关于多线程 ByteBuffers 比顺序慢?的主要内容,如果未能解决你的问题,请参考以下文章

多线程文件复制比多核CPU上的单个线程慢得多

多线程比单线程慢

C++11 多线程比单线程慢

由于 CPU 类型,C++ Boost 多线程比单线程慢?

多线程

并行执行比顺序执行慢,即使代码“很重”