多线程字节缓冲区比顺序缓冲区慢



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

我为每个线程分配了一个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实例从缓冲区中读取内存、进行计算等,它们不同步、不写入,也不相互影响。有什么问题吗?或者这不是并行化的好例子吗?

CCD_ 8和CCD_。

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

如果处理真的可以并行化,那么最好让一个线程读取整个文件,将其分成块(可能是一些中间数据格式),然后让执行器处理这些块。文件读取线程可以与其他线程并行运行,因此不需要读取整个文件即可开始处理。

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

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

最新更新