4

我们目前正在尝试通过在我们的 java 应用程序中使用多线程来调整性能。我们有一个长时间运行的串行任务,我们希望将其拆分为多个 CPU 内核。

基本上我们有一个清单,让我们说 100.000 件/要做的事情。

我现在的问题是这样做更好:

选项 1(伪代码):

for(i = 0; i < 100000; i++){
  threadpool.submit(new MyCallable("1 thing to do"))
}

这会将 100000 个可运行/可调用对象添加到线程池的队列(当前 LinkedBlockingQueue)

还是这样做更好: 选项2(伪代码)

for(i = 0; i < 4; i++){
  threadpool.submit(new MyCallable("25000 things to do"))
}

我们已经尝试了选项 1,但我们没有注意到任何性能改进,尽管我们可以清楚地看到多个线程疯狂地工作并且使用了 4 个 CPU 内核。但我的感觉是,由于任务众多,选项 1 中存在一些开销。我们还没有尝试过选项 2,但我的感觉是,它可以加快速度,因为开销更少。我们基本上将列表分成 4 个更大的块,而不是 100000 个单项。

对此有什么想法吗?

谢谢

4

5 回答 5

3

您的分析是正确的:批处理项目的成本(内存、上下文切换和一般指令计数)会更少——至少,一般来说。

但是,随着单个任务变得越来越大,这变得越来越不重要——如果您已经将 99% 的时间用于工作,而不是线程池开销或对象创建,那么您只能以这种方式优化剩下的 1%。

于 2012-05-30T07:09:07.617 回答
3

重要的是您将上下文切换的数量降至最低,并最大限度地增加每个任务的计算量。实际上,如果您的任务是计算,那么超过物理 CPU 的数量将无济于事。如果您的任务实际上执行了很多 I/O 和 I/O 等待,那么您希望拥有其中的许多任务,以便在一个阻塞时总是有一堆“就绪”任务可用。

如果你真的有 25000 件事情要做,而且事情是计算,我可能会设置 32 个线程(比你拥有的 CPU 更多,但不会有太多额外的开销),并为每个线程分配 10-50 个工作单元如果这些单位相对较小。

于 2012-05-30T07:14:27.220 回答
1

好吧,这取决于您的用例。

性能方面,我认为拥有更大的工作块比更小的线程要好。上下文切换会更少,因此,您将能够节省 CPU 周期和 RAM。

当任务数量较少时,这可能无关紧要,但是是的,如果您有 10000 个线程,这确实很重要。

于 2012-05-30T07:11:02.187 回答
1

您的机器中有 N 个内核。您想利用所有内核,但开销最小。因此,如果任务大小相等,则任务的最小数量可能为 N。如果它们不相等,则拥有 M*N 任务可能会更好,因为这可能意味着即使某些任务相对较短,所有内核也同样繁忙。例如,一个核心执行一项长任务,而另一个核心执行三个短任务。对于大多数用例,我使用 2-4 的 M。

如果可以的话,您可以对运行时间较长的任务进行排序以首先启动以获得最佳平衡。即在添加任务之前将任务从最长到最短排序。

例如,如果您有 8 个内核,您可能会发现 8 个任务最适合 CPU 密集型处理。对于 IO 绑定处理或需要不同时间量的任务,2*8 到 4*8 任务可能是最佳的。

于 2012-05-30T07:22:11.557 回答
1

4个批次的问题可能是,如果其中一个在10分钟内完成,其中三个在20分钟内完成,则1个核心将在10分钟内不使用,而其他3个线程将在3个核心上处理项目。但是您对开销是正确的。但是验证的唯一方法是检查它,因为很多事情取决于您的数据。

于 2012-05-30T07:23:27.890 回答