10

我试图通过并行化在大约 1000 个项目的列表上执行的昂贵操作来利用四核机器。

我目前正在使用 R 的 parallel::mclapply 函数:

res = rbind.fill(parallel::mclapply(lst, fun, mc.cores=3, mc.preschedule=T))

哪个有效。问题是,任何额外的子进程都必须分配一大块内存:

在此处输入图像描述

理想情况下,我希望每个核心都能从父 R 进程访问共享内存,这样当我增加 mclapply 中使用的核心数量时,我不会在核心限制之前达到 RAM 限制。

我目前不知道如何调试这个问题。每个进程访问的所有大型数据结构都是全局的(当前)。这是某种程度上的问题吗?

我确实将操作系统的共享内存最大设置增加到 20 GB(可用 RAM):

$ cat /etc/sysctl.conf 
kern.sysv.shmmax=21474836480
kern.sysv.shmall=5242880
kern.sysv.shmmin=1
kern.sysv.shmmni=32
kern.sysv.shmseg=8
kern.maxprocperuid=512
kern.maxproc=2048

我认为这会解决问题,但问题仍然存在。

还有其他想法吗?

4

3 回答 3

8

只是提示R-devel Digest,第 149 卷,第 22 期可能发生的事情

Radford Neal 2015 年 7 月 26 日的回答:

当 mclapply fork 启动一个新进程时,内存最初是与父进程共享的。但是,每当任何一个进程写入内存页时,都必须复制内存页。不幸的是,R 的垃圾收集器写入每个对象以标记和取消标记它,只要完成完整的垃圾收集,因此每个 R 对象很可能在每个进程中都被复制,即使它们中的许多实际上并没有改变(从这一点来看R 程序的视图)。

于 2017-03-20T08:39:36.280 回答
5

Linux和macosx在fork的时候都有copy-on-write机制,这意味着内存的pages实际上并没有被复制,而是共享到第一次写入。 mclapply基于 fork(),因此可能(除非您写入大共享数据),您看到的进程列表报告的内存不是实际内存。

但是,在收集结果时,主进程必须为 mclapply 的每个返回结果分配内存。

为了进一步帮助您,我们需要更多地了解您的有趣功能。

于 2013-07-03T09:10:13.467 回答
1

我想我会认为这不会因为写时复制功能而使用额外的内存。我认为列表的元素很大?也许当 R 将元素传递给 fun() 时,它实际上是在复制列表项,而不是在写入时使用复制。如果是这样,以下可能会更好:

fun <- function(itemNumber){
  myitem <- lst[[itemNumber]]
  # now do your computations
}
res = rbind.fill(parallel::mclapply(1:length(lst), fun, mc.cores=3, mc.preschedule=T))

或者lst[[itemNumber]]直接在你的函数中使用。如果 R/Linux/macos 不够聪明,无法在您编写函数时使用写时复制,则可能使用这种修改后的方法。

编辑:我假设您没有修改列表中的项目。如果这样做,R 将复制数据。

于 2014-02-06T18:35:58.177 回答