mclapply随机返回NULL

15

当我使用mclapply时,偶尔(真的很随机)会出现错误的结果。这个问题在互联网上的其他帖子中被很彻底地描述了,例如(http://r.789695.n4.nabble.com/Bug-in-mclapply-td4652743.html)。然而,还没有提供解决方案。有谁知道如何解决这个问题吗?谢谢!


2
当分叉的进程需要很长时间才能返回时,mclapply会返回null;我预计有一种内置超时机制会在一定时间后导致该进程死亡,但我无法在源代码中找到它的任何信息。 - James King
用户报告这个问题的另一个链接:http://r.789695.n4.nabble.com/Problem-with-mclapply-losing-output-data-td3395746.html - James King
Kudzu,Steve Weston建议内存不足的杀手可能会终止mclapply进程,这正是我遇到的nulls的原因。您能验证oom_killer是否也导致了您的nulls吗? - James King
2个回答

9

您提到的Winston Chang报告的问题似乎已在R 2.15.3中修复。在mccollect中存在一个错误,当将工人结果分配给结果列表时会发生此错误:

if (is.raw(r)) res[[which(pid == pids)]] <- unserialize(r)

如果unserialize(r)返回NULL,则此方法将失败,因为以这种方式将NULL赋值给列表会删除列表的相应元素。R 2.15.3中对此进行了更改:

if (is.raw(r)) # unserialize(r) might be null
    res[which(pid == pids)] <- list(unserialize(r))

这是一种将未知值赋给列表的安全方式。
如果您使用的是R <= 2.15.2,解决方法是升级到R >= 2.15.3。如果您在使用R >= 2.15.3时遇到问题,则很可能不同于Winston Chang报告的问题。
我还阅读了Elizabeth Purdom启动的R-help线程中讨论的问题。没有具体的测试用例,我的猜测是问题是由mclapply中的错误引起的,因为我可以使用以下函数重现相同的症状:
work <- function(i, poison) {
  if (i == poison) quit(save='no')
  i
}

如果一个通过mclapply开始执行任务的工作进程因任何原因(接收信号、段错误、退出)而死亡,那么mclapply将为所有分配给该工作进程的任务返回NULL:
> library(parallel)
> mclapply(1:4, work, 3, mc.cores=2)
[[1]]
NULL

[[2]]
[1] 2

[[3]]
NULL

[[4]]
[1] 4

在这种情况下,由于预调度,任务1和3返回了NULL值,尽管只有任务3实际上失败了。
如果使用parLapply或clusterApply等函数时,一个工作进程死亡,会报告一个错误:
> cl <- makePSOCKcluster(3)
> parLapply(cl, 1:4, work, 3)
Error in unserialize(node$con) : error reading from connection

我看过许多这样的报告,我认为它们往往发生在使用大量难以转化为可重复测试用例的软件包的大型程序中。
当然,在此示例中,使用lapply时也会出现错误,尽管该错误不像mclapply那样被隐藏。如果在使用lapply时似乎没有出现问题,那么可能是因为问题很少发生,所以只会在使用mclapply并行执行非常大的运行时才会发生。但也有可能错误发生,并不是因为任务是并行执行的,而是因为它们是由分叉进程执行的。例如,各种图形操作在分叉进程中执行时会失败。

1
我在R 3.0.1上遇到了问题,所以我认为问题出在其他地方。这也不是函数返回null并删除列表元素的问题。相反,当使用lapply运行时,函数返回一个非null值,但在并行运行时,生成的列表包含null。我正在努力提供一个示例,以便其他人可以重现此问题。 - James King
2
@user3114046 在Linux系统上,如果由于许多工作进程而导致内存不足,则内存杀手可能会杀死一些工作进程,导致mclapply返回NULL。这在我们的集群上发生了,在那里我们有限的交换空间。 - Steve Weston
1
@Steve Weston 太棒了!R调用oom-killer:gfp_mask=0x200da,order=0,oom_score_adj=0。看起来有足够的内存可用,但也许进程被某种方式限制了。所以我想知道是否有一种方法可以让mclapply通知用户如果一个进程被杀死了?我已经检查了代码,但我不清楚如何做到这一点。 - James King
1
@user3114046 我有点惊讶,它在这种情况下没有发出任何警告或消息。将此作为合理的功能请求提交给R核心团队是非常合适的。 - Steve Weston
我可能会提交一个请求。如果我手动杀死其中一个工作进程,同样的事情也会发生——答案中会出现空值,并没有警告表明该进程被终止。 - James King
显示剩余2条评论

5
我添加这个答案是为了让其他遇到同样问题的人不必浏览冗长的评论线程(我是赏金提供者,但不是原帖作者)。 mclapply 最初使用 NULLS 填充它创建的列表。当工作进程返回值时,这些值将覆盖 NULLS。如果一个进程在没有返回值的情况下死亡,则 mclapply 将返回一个 NULL。
当内存变得不足时,Linux 内存杀手 (oom killer) 开始默默地杀死进程。它不会打印任何东西到控制台来告诉你它在做什么,尽管 oom killer 的活动会显示在系统日志中。在这种情况下,mclapply 的输出似乎已经随机地受到 NULLS 的污染。 https://lwn.net/Articles/317814/

我现在正在处理的问题...你是如何停止oom killer的? - gannawag
1
@gannawag 我运行了较少的进程,以避免内存耗尽。 - James King

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接