我们目前正在尝试通过在我们的Java应用程序中使用多线程来调整性能。 我们有一个长期运行的串行任务,我们希望将其拆分为多CPU内核。
基本上我们有列表,让我们说100.000项/事要做。
我现在的问题是做得更好:
选项1 (伪代码):
for(i = 0; i < 100000; i++){
threadpool.submit(new MyCallable("1 thing to do"))
}
这会将100000个runnables / callables添加到线程池的队列中(当前的LinkedBlockingQueue)
或者做得更好: 选项2 (伪代码)
for(i = 0; i < 4; i++){
threadpool.submit(new MyCallable("25000 things to do"))
}
我们已经尝试过选项1,虽然我们可以清楚地看到多个线程正在疯狂地工作并且还使用了4个CPU核心,但我们没有注意到任何性能提升。但我的感觉是,由于许多任务,选项1中存在一些开销。我们还没有尝试过选项2,但我的感觉是,它可以加快速度,因为开销较少。我们基本上将列表拆分为4个更大的块而不是100000个单个项。
对此有何想法?
由于
答案 0 :(得分:3)
您的分析是正确的:在批量处理项目时会有更少的成本(内存,上下文切换和一般指令计数) - 至少,一般来说。
随着单个任务变得越来越大,这变得越来越不相关 - 如果你已经花99%的时间做工作,而不是线程池开销或对象创建,那么你只能通过这种方式优化1%。 / p>
答案 1 :(得分:3)
重要的是,您最大限度地减少了上下文切换的数量,并最大限度地增加了计算所花费的每项任务的工作量。实际上,如果您的任务是计算,超过物理CPU的数量将无济于事。如果您的任务实际上执行了大量的I / O和I / O等待,那么您希望拥有其中的许多任务,以便在一个阻塞时始终有一堆“就绪”任务可用。
如果你真的有25000件事要做,而事情就是计算,我可能会设置32个线程(比你有更多的CPU,但没有太多的额外开销)并且包含10-50个单位的工作如果这些单位相对较小,则为每一个。
答案 2 :(得分:1)
嗯,这取决于你的用例。
性能方面,我认为拥有更大的工作块比更小的线程更好。上下文切换会更少,因此,您可以节省CPU周期和RAM。
当任务数量较小时,这可能并不重要,但是,如果你有10000个线程,它确实很重要。
答案 3 :(得分:1)
您的计算机中有N个核心。您希望充分利用所有核心,但开销最小。因此,如果任务大小相等,则最小任务数可能为N.如果他们不相等M * N任务可以更好,因为它可能意味着所有核心同样繁忙,即使某些任务相对较短。例如一个核心执行一项长任务,而另一个执行三个简短任务。在我的大多数用例中,我使用的是2-4的M。
如果可以,您可以先对要运行的较长时间的任务进行排序,以获得最佳平衡。即在添加任务之前将任务从最长到最短排序。
e.g。如果您有8个内核,您可能会发现8个任务最适合CPU绑定处理。对于IO绑定处理或任务,需要不同的时间2 * 8到4 * 8任务可能是最佳的。
答案 4 :(得分:1)
4个批次的问题可能是,如果其中一个在10分钟内完成,其中三个在20分钟内完成,则1个核心将不会在10分钟内使用,而其他3个线程将处理3个核心的项目。但你是正确的开销。但验证的唯一方法是检查它,因为很多事情取决于你的数据。