错误:C栈使用接近极限

117

我正在尝试在R中运行一些相当深层次的递归代码,但它一直给我这个错误:

Error: C stack usage is too close to the limit

CStack_info() 的输出是:

Cstack_info()
    size    current  direction eval_depth 
67108864       8120          1          2 

我的机器内存很充足,我只是想找出如何增加 R 的 CStack。

编辑:有人要求提供可重现的示例。以下是一些引起问题的基本示例代码。运行 f(1,1) 几次,您就会得到错误。请注意,我已经设置了 --max-ppsize=500000 和 options(expressions=500000),因此如果您没有设置这些选项,可能会出现与其中一个选项相关的错误。正如您所看到的,递归深度可以相当深,我不知道该如何使其始终正常工作。谢谢。

f <- function(root=1,lambda=1) {
    x <- c(0,1);
    prob <- c(1/(lambda+1),lambda/(lambda+1));
        repeat {
      if(root == 0) {
        break;
      }
      else {
        child <- sample(x,2,replace=TRUE,prob);
        if(child[1] == 0 && child[2] == 0) {
          break;
        }
        if(child[1] == 1) {
          child[1] <- f(root=child[1],lambda);
        }
        if(child[2] == 1 && child[1] == 0) {
          child[2] <- f(root=child[2],lambda);
        }
      }
      if(child[1] == 0 && child[2] == 0) {
        break;
      }
      if(child[1] == 1 || child[2] == 1) {
        root <- sample(x,1,replace=TRUE,prob);
      }
        }
    return(root)
}

1
这个问题建议可能使用options(expressions = somethinglarge) - mnel
@mnel 表达式嵌套深度、指针保护栈和 C 栈是三个独立但相关的事物。 - zwol
我的答案适用于任何Unix变体(其中Linux和OSX是当今最常见的),但是...是的,我不知道Windows的等效物是什么。 - zwol
2
通过谷歌搜索错误信息,显示过去通常是用户代码出错,因此您应该将问题简化为一个简单的可重现示例,并在此处发布。 - Martin Morgan
2
我不确定代码中是否存在错误。这只是一种理论上可能导致无限递归的概率情况。f(1,1)基本上是抛硬币。它可能永远都是正面朝上。对于递归级别未知且无界的条件,最好使用更迭代的方法,使用先前sample()结果的记忆化来指导未来操作。然后你唯一需要担心的就是耗尽向量内存或磁盘,这取决于你在哪里存储你的结果积压。递归可能很昂贵且脆弱。 - Robert Casey
显示剩余3条评论
17个回答

66

堆栈大小是操作系统的参数,可按进程进行调整(参见setrlimit(2))。据我所知,您无法从R内部进行调整,但您可以使用ulimit命令在启动R之前从Shell进行调整。它的使用方式如下:

$ ulimit -s # print default
8192
$ R --slave -e 'Cstack_info()["size"]'
   size 
8388608

8388608 = 1024 * 8192;R打印的值与ulimit -s相同,只不过以字节为单位而不是以千字节为单位。

$ ulimit -s 16384 # enlarge stack limit to 16 megs
$ R --slave -e 'Cstack_info()["size"]'
    size 
16777216 
要对此设置进行永久性调整,请将 ulimit 命令添加到您的 shell 启动文件中,这样每次登录时都会执行它。我无法给出更具体的说明,因为这取决于您使用的 shell 类型以及其他一些因素。如果您没有在终端窗口内运行 R,则还需要知道如何在图形环境中登录来完成此操作。

17
或者将其设置为“无限制”。 - Paul Hiemstra
2
RAppArmor 提供了一个与 setrlimit(2) 交互的接口。这个功能可能会在 ulimit 包中某个时刻变得可用。 - krlmlr
2
这个函数在 RAppArmor 包中已经不存在了。有任何想法它去哪了吗? - CoderGuy123
6
Windows的修复方法是什么? - S.Perera
2
更改限制并不能解决这个问题。递归函数会一直运行,直到达到更高的限制。 - Tom Kelly
显示剩余4条评论

30

我怀疑无论堆栈限制如何,递归深度都会太深。例如,当lambda为Inf时,f(1)会立即无限递归。递归的深度似乎是一次随机游走,有概率r进一步递归,概率1-r结束当前递归。当你达到堆栈限制时,你已经进行了大量的“更深”步骤。这意味着r > 1/2,绝大多数时间你将继续递归。

此外,即使在无限递归的情况下,似乎也几乎可以推导出解析或至少数值解。可以将p定义为f(1)==1的概率,编写单次迭代后的“子”状态的隐式表达式,并将其与p相等并求解。然后,p可以作为从二项分布中进行一次成功抽取的机会。


4
这里实际上隐藏着正确的答案 - 确保你不要在递归中深入太多... - Kamil S Jaron
1
在我的情况下,错误是由于在我的项目中多次引用相同的R脚本(即在多个R脚本中)引起的。 - Good Will

30

这个错误不是由于内存引起的,而是由于递归引起的。函数在调用自身。仅仅检查一个函数的定义并不总是很明显。为了阐明这一点,这里有一个相互调用的两个函数的最小示例:

change_to_factor <- function(x){
  x <- change_to_character(x)
  as.factor(x)
} 

change_to_character <- function(x){
  x <- change_to_factor(x)
  as.character(x)
}

change_to_character("1")

错误:C栈使用量7971600太接近限制

这些函数将继续相互递归调用,理论上永远不会完成,即使您增加了限制也会超过限制。只有系统内部的检查才能防止这种情况无限期地发生并消耗计算机的所有资源。您需要修改这些函数,以确保它们不会无限期地相互递归调用。


1
拒绝递归会限制计算机解决问题的范围。我建议在每个递归调用的函数中使用所谓的终止器。终止器的作用是有条件地停止进一步的递归调用,最好的方法是计算递归深度并在达到给定限制时立即停止(在系统错误发生之前)。 - cineS.

13

这件事情发生在我身上,但原因完全不同。当我合并两列时,无意中创建了一个超长字符串:

output_table_subset = mutate(big_data_frame,
     combined_table = paste0(first_part, second_part, col = "_"))

而不是

output_table_subset = mutate(big_data_frame,
     combined_table = paste0(first_part, second_part, sep = "_"))

我花了很长时间才弄清楚,因为我从没想到黏贴会引起问题。


我也是,但我正在做一个总结。我像这样写:summarize( states = paste0(state,collapse=', ') )。但我应该这样做: summarize( states = paste0(sort(unique(state)),collapse=', ') )。目标是获得每个子组可用的唯一州的逗号分隔列表。 - Richard DiSalvo

5
我遇到了同样的问题,收到了“C堆栈使用接近极限”的错误提示(虽然这是针对比上面提到的应用程序)。我尝试了zwol的建议,但没有成功。令我惊讶的是,我通过安装最新版本的OS X的R(目前是版本3.2.3)以及最新版本的OS X的R Studio(目前为0.99.840),解决了这个问题,因为我正在使用R Studio。 希望这也能对您有所帮助。

3
我升级了 R 的版本。虽然它曾经可用过一次,但现在错误再次出现并且持续存在。请求帮助! - murphy1310

3
我的情况可能更为特殊,但可以帮助那些遇到同样问题的人:
我的情况与空间使用无关,但R却报出了以下错误:
C stack usage is too close to the limit
我定义了一个函数,它是基础函数的升级版本:

saveRDS()

但是,这个自定义函数被命名为 saveRDS(),而不是 safe_saveRDS()
因此,在该定义之后,当代码到达实际使用 saveRDS(...) 的行(调用原始基础版本而不是升级版本)时,就会出现以上错误并崩溃。
所以,如果在调用某个保存函数时出现了这个错误,请检查是否不小心重复定义了该函数。

2

这里可能存在一个问题,即您在函数内部调用了f

plop <- function(a = 2){
  pouet <- sample(a)
  plop(pouet)
}
plop()
Erreur : évaluations trop profondément imbriquées : récursion infinie / options(expressions=) ?
Erreur pendant l'emballage (wrapup) : évaluations trop profondément imbriquées : récursion infinie / options(expressions=) ?

2
我经常在R脚本的顶部包含一个已注释的source("path/to/file/thefile.R")行,例如thefile.R,这样我就可以轻松地将其复制粘贴到终端中运行。如果我忘记注释该行,则会出现此错误,因为运行文件会运行文件,会运行文件,会运行文件,......

如果这是原因,解决方法很简单:注释掉该行。


2
在Linux上,我通过以下方式永久增加了堆栈和内存锁定内存的大小:
sudo vi /etc/security/limits.conf 

接下来,在文件末尾添加以下行。

* soft memlock unlimited
* hard memlock unlimited

* soft stack unlimited
* hard stack unlimited

你知道要重启哪个服务才能避免重新启动吗? - jay.sf

1

我也遇到了同样的问题。重新安装R或Rstudio,或增加堆栈大小都无法解决这个问题。以下是一个可以解决这个问题的方法 -

如果你在a.R中引用了b.R,在同时又在b.R中引用了a.R,那么堆栈会非常快地填满。

问题

这是第一个文件 a.R,其中引用了b.R

#---- a.R File -----
source("/b.R")
...
...
#--------------------

这是第二个文件b.R,其中引用了a.R

#---- b.R File -----
source("/a.R")
...
...
#--------------------

解决方案:只源代码文件,避免文件彼此之间的递归调用。

#---- a.R File -----
source("/b.R")
...
...
#--------------------

#---- b.R File -----
...
...
#--------------------

OR

#---- a.R File -----
...
...
...
#--------------------

#---- b.R File -----
source("/a.R")
...
...
#--------------------

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