Java 使用多个物理内核并行化非IO任务的最佳方法:每个线程的工作量相等,还是最大线程数?
我们有各种各样的任务,它们可以分成相等的工作单元,不涉及磁盘或网络I/O 想象一下,我们正试图处理70幅图像。我们可以独立处理每个图像。过去我们会这样做:Java 使用多个物理内核并行化非IO任务的最佳方法:每个线程的工作量相等,还是最大线程数?,java,multithreading,Java,Multithreading,我们有各种各样的任务,它们可以分成相等的工作单元,不涉及磁盘或网络I/O 想象一下,我们正试图处理70幅图像。我们可以独立处理每个图像。过去我们会这样做: 工作单位数量:70 芯数:16 每根螺纹的工作=ceil(70/16)=5 螺纹数=70/5=14个螺纹 您会注意到,在如此高的内核数机器上,此算法似乎“浪费”了两个内核。因此,有人建议这样做可能更好: 螺纹数=芯数=16 每个螺纹的工作=5(对于6个螺纹)或4(对于10个螺纹) 进一步分析后,这似乎不太有利: 如果任务是CPU限制的
- 工作单位数量:70
- 芯数:16
- 每根螺纹的工作=ceil(70/16)=5
- 螺纹数=70/5=14个螺纹
- 螺纹数=芯数=16
- 每个螺纹的工作=5(对于6个螺纹)或4(对于10个螺纹)
- 是否有理由选择第二种算法而不是第一种算法?它们是什么
- 是否存在一种共识,即“平均而言”一个比另一个好?为什么?
我们用Java编程,但我相信这个问题适用于任何具有共享内存线程的语言。您考虑过一个新的解决方案吗?你的问题到底是关于什么还不是很清楚。不确定“工作单元”是什么,但听起来像“任务”,所以使用并调用
submit(task)
方法(Elliott Frisch提到的ForkJoinPool
是一个ExecutorService
)。我们通常不使用ForkJoinPool,因为任务数量有时会很大,任务创建的开销会成为一个问题。我讨论的两种算法都有内存分配O(线程),而不是O(工作单元)。例如,如果我们想要处理70个图像,我们可以告诉线程1处理列表中的索引[0,4]。ForkJoinPool有时在其他情况下很有用,在这些情况下,需要完成的工作量在单元之间并不相似。也许我遗漏了一些东西,但是如果每个图像都可以独立处理,并且您有70个图像,为什么不创建70个处理图像任务,将它们全部提交到线程池队列中(例如,执行器服务
),并让线程尽可能地处理任务?您提到了任务数量是一种开销;如果是这样的话,为什么不设置生产者/消费者设置,以便一次只创建这么多任务?这不是一次分配这么多任务,而是分配这么多任务。在本例中,它不是不要紧,但想象一下,如果我们处理数百万个16x16映像。在执行完成之前,将创建数百万个任务对象,并可能升级到旧一代。与上述两种算法不同,这两种算法只为每个线程分配一个可运行的和两个整数。我们使用固定大小的ThreadPoolExecutor来执行工作的实际线程执行。我将编辑问题以使其更清楚。您是否考虑了一个?您的问题到底是关于什么的还不是很清楚。不确定“工作单元”是什么,但听起来像“任务”,所以请使用并调用submit(task)
方法(Elliott Frisch提到的ForkJoinPool
是一个ExecutorService
)。我们通常不使用ForkJoinPool,因为任务的数量有时会很大,任务创建的开销会成为一个问题。我讨论的两种算法都有内存分配O(#线程),而不是O(#工作单元)例如,如果我们想要处理70个图像,我们可以告诉线程1处理索引[0,4]在列表中。ForkJoinPool有时在其他情况下很有用,在这些情况下,需要完成的工作量在各个单元之间并不相似。也许我遗漏了一些东西,但是如果每个图像都可以独立处理,并且您有70个图像,为什么不创建70个处理图像任务,将它们全部提交到线程池队列中(例如,执行器服务
),并让线程尽可能地处理任务?您提到了任务数量是一种开销;如果是这样的话,为什么不设置生产者/消费者设置,以便一次只创建这么多任务?这不是一次分配这么多任务,而是分配这么多任务。在本例中,它不是不要紧,但想象一下,如果我们处理数百万个16x16映像。在执行完成之前,将创建数百万个任务对象,并可能升级到旧一代。与上述两种算法不同,这两种算法只为每个线程分配一个可运行的和两个整数。我们使用固定大小的ThreadPoolExecutor来执行工作的实际线程执行。我将编辑问题以使其更清楚。