我在R中编写了一个递归代码 . 在调用R之前,我在shell中将堆栈大小设置为96 MB:
ulimit -s 96000
我调用R,最大保护指针堆栈大小为500000:
R --max-ppsize 500000
我将最大递归深度更改为500000:
options(expression = 500000)
我在Arch Linux存储库中使用了二进制R包(没有内存分析),也使用了内存分析选项编译的二进制文件 . 两者都是版本3.4.2
我使用了两个版本的代码,有和没有gc() .
问题是R退出代码时出现“节点堆栈溢出”错误,而使用的总可用堆栈只有16 MB,深度仅低于5e5表达式选项的1%:
size current direction eval_depth
93388800 16284704 1 4958
Error: node stack overflow
最后两次迭代之间的当前堆栈使用量变化大约为10K . 唯一传递和保存的对象是19个项目的数字向量 .
代码的递归部分如下:
network_recursive <- function(called)
{
print(Cstack_info())
callers <- list_caller[[called + 1]] # get the callers of the called
callers <- callers[!bool[callers + 1]] # subset for nofriends - new friends
new_friend_no <- length(callers) # number of new friends
print(list(called, callers) )
if (new_friend_no > 0) # if1 still new friends
{
friends <<- friends + new_friend_no # increment friend no
print(friends)
bool[callers + 1] <<- T # toggle friends
sapply(callers, network_recursive) # recurse network control
} # close if1
print("end of recursion")
}
堆栈溢出的原因是什么?
关于R源代码的一些注意事项,与问题有关 .
触发错误的代码部分是来自src / main / eval.c的第5987-5988行:
5975 #ifdef USE_BINDING_CACHE
5976 if (useCache) {
5977 R_len_t n = LENGTH(constants);
5978 # ifdef CACHE_MAX
5979 if (n > CACHE_MAX) {
5980 n = CACHE_MAX;
5981 smallcache = FALSE;
5982 }
5983 # endif
5984 # ifdef CACHE_ON_STACK
5985 /* initialize binding cache on the stack */
5986 vcache = R_BCNodeStackTop;
5987 if (R_BCNodeStackTop + n > R_BCNodeStackEnd)
5988 nodeStackOverflow();
5989 while (n > 0) {
5990 SETSTACK(0, R_NilValue);
5991 R_BCNodeStackTop++;
5992 n--;
5993 }
5994 # else
5995 /* allocate binding cache and protect on stack */
5996 vcache = allocVector(VECSXP, n);
5997 BCNPUSH(vcache);
5998 # endif
5999 }
6000 #endif
2 回答
节点堆栈有自己的限制,这是固定的(在Defn.h中定义,
R_BCNODESTACKSIZE
) . 如果您有一个限制太小的真实示例,请提交错误报告,我们可以增加它或者为它添加命令行选项 . "node stack"由字节码解释器使用,它解释字节码编译器产生的字节码 .Cstack_info()
不显示节点堆栈使用情况 . 节点堆栈未在C堆栈上分配 .无论如何,基于深度递归的程序在R中都会非常慢,因为函数调用非常昂贵 . 出于实际目的,当达到与递归深度相关的限制时,重写程序以避免递归而不是增加限制可能更好 .
就像一个实验一样,人们可能会禁用即时编译器,从而减少节点堆栈上的压力 . 它不会被完全消除,因为默认情况下已经在安装时编译了一些软件包,包括基础软件包和推荐的软件包,例如,
sapply
已编译 . 另外,这可能会增加递归消除表达式的压力,程序运行速度会更慢 .在我的脑海中,我看到你使用了选项(表达式= 500000),但是“options()”返回的列表中的字段被称为“表达式”(带有s) . 如果您按照问题中描述的方式键入,则“表达式”字段保持为5000,而不是您打算将其设置为500000 . 所以这可能就是为什么你只使用你认为是堆栈深度的1%时最大化的原因 .