21

我收到了名义上的错误:

mcfork(): Unable to fork: Cannot allocate memory

在尝试使用 mcapply 运行函数后,但top说我在 51%

这是在 EC2 实例上,但我确实有最新的 R。

有谁知道还有什么可能导致此错误?

谢谢,

-N

4

5 回答 5

17

问题可能正是错误消息所暗示的:没有足够的内存来分叉和创建并行进程。

R 本质上需要为每个单独的进程创建内存中所有内容的副本(据我所知,它不使用共享内存)。如果您已经在单个进程中使用了 51% 的 RAM,那么您没有足够的内存来创建第二个进程,因为这总共需要 102% 的 RAM。

尝试:

  1. 使用更少的内核- 如果您尝试使用 4 个内核,您可能有足够的 RAM 来支持 3 个并行线程,但不是 4 个registerDoMC(2)。例如,将并行线程数设置为 2(如果您使用的是doMC并行后端)。
  2. 使用更少的内存- 没有看到你的代码的其余部分,很难提出实现这一目标的方法。可能有帮助的一件事是确定哪些 R 对象占用了所有内存(确定对象的内存使用情况?),然后从内存中删除不需要的任何对象(rm(my_big_object)
  3. 添加更多 RAM - 如果所有其他方法都失败,请将硬件投入其中,以便您拥有更多容量。
  4. 坚持单线程 - R 中的多线程处理是 CPU 和内存的权衡。听起来在这种情况下你可能没有足够的内存来支持你拥有的 CPU 能力,所以最好的做法可能是只坚持一个核心。
于 2015-08-23T21:45:19.183 回答
5

R 函数mcfork只是系统调用的一个包装器fork(顺便说一句,手册页说,这个调用本身就是一个包装器clone

我创建了一个简单的 C++ 程序来测试fork的行为:

#include <stdio.h>
#include <unistd.h>

#include<vector>

int main(int argc, char **argv)
{
    printf("--beginning of program\n");

    std::vector<std::vector<int> > l(50000, std::vector<int>(50000, 0));

//    while (true) {}

    int counter = 0;
    pid_t pid = fork();
    pid = fork();
    pid = fork();


    if (pid == 0)
    {
        // child process
        int i = 0;
        for (; i < 5; ++i)
        {
            printf("child process: counter=%d\n", ++counter);
        }
    }
    else if (pid > 0)
    {
        // parent process
        int j = 0;
        for (; j < 5; ++j)
        {
            printf("parent process: counter=%d\n", ++counter);
        }
    }
    else
    {
        // fork failed
        printf("fork() failed!\n");
        return 1;
    }

    printf("--end of program--\n");
    while (true) {}
    return 0;
}

首先,程序在堆上分配了大约 8GB 的​​数据。然后,它通过 fork 调用产生 2^2^2 = 8 个孩子并等待被用户杀死,然后进入无限循环以便在任务管理器上轻松发现。

以下是我的观察:

  1. 要使 fork 成功,您需要在我的系统上至少有 51% 的可用内存,但这包括 swap。您可以通过编辑/proc/sys/vm/overcommit_*proc 文件来更改它。
  2. 正如预期的那样,没有一个子进程占用更多内存,因此这 51% 的空闲内存在整个程序过程中保持空闲,并且所有后续分叉也不会失败。
  3. 内存在分叉之间共享,因此只有在您杀死最后一个孩子后才会回收。

内存碎片问题

您不应该担心与 fork 相关的任何内存碎片层。R 的内存碎片在这里不适用,因为 fork 是在虚拟内存上操作的。您不必担心物理内存的碎片,因为几乎所有现代操作系统都使用虚拟内存(因此它们可以使用交换)。唯一可能存在问题的内存碎片是虚拟内存空间的碎片,但 Linux 虚拟内存空间上的 AFAIK 是 2^47,这非常大,几十年来,您在寻找连续的区域时应该没有任何问题任何实用尺寸。

概括:

确保你有比物理内存更多的交换空间,只要你的计算实际上不需要比 RAM 更多的内存,你就可以mcfork随心所欲地使用它们。

或者,如果您愿意冒整个系统的稳定性(内存不足)的风险,请echo 1 >/proc/sys/vm/overcommit_memory在 linux 上以 root 身份尝试。

或者更好:(更安全)

echo 2 >/proc/sys/vm/overcommit_memory
echo 100 >/proc/sys/vm/overcommit_ratio

您可以在此处阅读有关过度使用的更多信息:https ://www.win.tue.nl/~aeb/linux/lk/lk-9.html

于 2016-05-20T09:35:31.917 回答
4

给那些想要使用 RStudio 等 GUI 的人的注意事项。
如果您想利用并行处理,建议不要使用 GUI,因为这会中断您的代码和 GUI 程序之间的多线程进程。registerDoMC以下是R 包帮助手册的摘录:

最初由 Simon Urbanek 编写并包含在 R 2.14.0 中的并行包中的多核功能提供了用于在具有多个内核或处理器的机器上并行执行 R 代码的功能,使用系统 fork 调用来生成当前进程的副本。

多核功能,因此 registerDoMC,不应该在 GUI 环境中使用,因为多个进程共享同一个 GUI。

registerDoMC(cores = n)我通过在使用 RStudio 运行我的程序时禁用来解决了 OP 遇到的类似错误。多处理最适合使用基础 R。希望这会有所帮助。

于 2016-11-30T08:39:34.427 回答
1

我有同样的错误,同时使用插入符号在具有 64 GB 内存的系统上训练 rpart 模型,在 7 核机器上使用 6 核进行并行处理。换成5核,问题就出来了。

library(doMC)
registerDoMC(5)
于 2014-12-31T14:31:04.043 回答
1

我现在遇到了类似的问题。我不会声称知道正确答案。上述两个答案都提出了可能有效的行动方案,特别是如果您的分叉同时对内存产生额外的写入需求。但是,我一直在想,其他可能是困难的根源。内存碎片。有关以下内容的讨论,请参阅https://raspberrypi.stackexchange.com/questions/7856/log-says-i-cant-allocate-memory-but-i-have-more-than-half-of-my-memory-free类似 Unix 的用户看到可用内存但由于内存碎片而遇到内存不足错误的情况。这似乎是 R 的罪魁祸首,尤其是因为 R 喜欢连续的 RAM 块。也每?Memory-limits要求应该是关于地址空间而不是 RAM 本身 - 所以这可能是不正确的(尤其是在 64 位机器上)YMMV。

于 2016-04-10T05:33:06.210 回答