什么时候(如果有的话)我应该告诉 R parallel 不要使用所有内核?

When (if ever) should I tell R parallel to not use all cores?

我一直在使用这个代码:

library(parallel)
cl <- makeCluster( detectCores() - 1)
clusterCall(cl, function(){library(imager)})

然后我有一个看起来像这样的包装函数:

d <- matrix  #Loading a batch of data into a matrix
res <- parApply(cl, d, 1, FUN, ...)
# Upload `res` somewhere

我在笔记本上测试过,8核(4核,超线程)。当我 运行 它在一个 50,000 行、800 列的矩阵上时,需要 177.5 秒才能完成,并且大部分时间 7 个核心保持在接近 100%(根据顶部),然后它就坐在那里在最后 15 秒左右,我猜这是合并结果。根据system.time(),用户时间为14s,所以匹配。

现在我 运行正在使用 EC2,这是一个 36 核 c4.8xlarge,我看到它几乎所有时间都只用在一个 100% 的核心上。更准确地说:有一个大约 10-20 秒的突发,其中所有内核都被使用,然后大约 90 秒只有一个内核 100%(被 R 使用),然后大约 45 秒的其他东西(我保存结果和加载下一批数据)。我正在做 40,000 行、800 列的批处理。

根据 top,长期平均负载徘徊在 5.00 左右。

这看起来合理吗?或者是否存在 R 并行性在通信开销上花费更多时间的点,我应该限制在例如16 核。这里有什么经验法则吗?

参考:CPU spec 我正在使用 "Linux 4.4.5-15.26.amzn1.x86_64 (amd64)"。 R 版本 3.2.2 (2015-08-14)

更新: 我试过 16 个内核。对于最小的数据,运行-time 从 13.9s 增加到 18.3s。对于中型数据:

With 16 cores:
   user  system elapsed 
 30.424   0.580  60.034 

With 35 cores:
   user  system elapsed 
 30.220   0.604  54.395 

开销 部分花费了相同的时间,但并行位的内核较少,因此花费的时间更长,因此总体花费的时间更长。

我也按照评论中的建议尝试使用 mclapply()。它确实看起来快了一点(在我试过的特定测试数据上大约是 330 秒与 360 秒),但那是在我的笔记本上,其他过程或过热可能会影响结果。所以,我还没有就此得出任何结论。

没有有用的经验法则 — 并行任务最适合的核心数完全由所述任务决定。有关更一般性的讨论,请参阅 Gustafson’s law

您在代码中看到的高单核部分可能来自算法的结束阶段(“连接”阶段),其中并行结果被整理到单个数据结构中。由于这远远超过了并行计算阶段,这可能确实表明更少的内核可能是有益的。

我要补充一点,如果您不知道 R 中并行计算的这一精彩资源,您可能会发现阅读 Norman Matloff 的新书 Parallel Computing for Data Science: With Examples in R, C++ and CUDA 非常有帮助。我强烈推荐它(我学到了很多东西,而不是来自 CS 背景)。

本书深入解答了您的问题(特别是第 2 章)。该书对导致并行程序瓶颈的开销原因进行了高度概述。

引用第 2.1 节,隐含地部分回答了您的问题:

There are two main performance issues in parallel programming:

Communications overhead: Typically data must be transferred back and forth between processes. This takes time, which can take quite a toll on performance. In addition, the processes can get in each other’s way if they all try to access the same data at once. They can collide when trying to access the same communications channel, the same memory module, and so on. This is another sap on speed. The term granularity is used to refer, roughly, to the ratio of computa- tion to overhead. Large-grained or coarse-grained algorithms involve large enough chunks of computation that the overhead isn’t much of a problem. In fine-grained algorithms, we really need to avoid overhead as much as possible.

^ 当开销很高时,手头问题的核心数越少,总计算时间就越短。

Load balance: As noted in the last chapter, if we are not careful in the way in which we assign work to processes, we risk assigning much more work to some than to others. This compromises performance, as it leaves some processes unproductive at the end of the run, while there is still work to be done.

什么时候不使用所有内核?一个来自我个人经验的例子 运行 R 中的日常 cronjobs 在 RAM 中相当于 100-200GB 数据的数据上,其中多个内核是 运行 紧缩数据块,我确实发现 运行 说 32 个可用内核中的 6 个比使用 20-30 个内核要快。一个主要原因是子进程的内存需求(在一定数量的子进程运行后,内存使用率变高,速度明显变慢)。