我正在尝试将大量 sexp 从文件读取到内存中,对于较小的输入,它似乎工作得很好,但在更深的嵌套输入上,sbcl 会因堆栈耗尽而崩溃。似乎有一个硬递归限制(深度为 1000 个函数), sbcl 根本无法超过(奇怪的是,即使它的堆栈大小增加了)。示例(代码在此处):make check-c
有效,但make check-cpp
耗尽了堆栈,如下所示:
INFO: Control stack guard page unprotected
Control stack guard page temporarily disabled: proceed with caution
Unhandled SB-KERNEL::CONTROL-STACK-EXHAUSTED in thread #<SB-THREAD:THREAD
"main thread" RUNNING
{10034E6DE3}>:
Control stack exhausted (no more space for function call frames).
This is probably due to heavily nested or infinitely recursive function
calls, or a tail call that SBCL cannot or has not optimized away.
PROCEED WITH CAUTION.
Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {10034E6DE3}>
0: ((LAMBDA NIL :IN SB-DEBUG::FUNCALL-WITH-DEBUG-IO-SYNTAX))
1: (SB-IMPL::CALL-WITH-SANE-IO-SYNTAX #<CLOSURE (LAMBDA NIL :IN SB-DEBUG::FUNCALL-WITH-DEBUG-IO-SYNTAX) {100FC9006B}>)
2: (SB-IMPL::%WITH-STANDARD-IO-SYNTAX #<CLOSURE (LAMBDA NIL :IN SB-DEBUG::FUNCALL-WITH-DEBUG-IO-SYNTAX) {100FC9003B}>)
...
那我为什么要使用递归呢?实际上,我不是,但不幸的是内置(read)
函数使用递归,这就是发生堆栈溢出的地方。另一种选择(我已经开始研究)是编写一个迭代版本,read
它依赖于我从一个单独的程序中输入的更有限的语法,以避免重新实现读取的复杂性(我的(目前损坏)尝试在lisp
上述存储库的分支中)。
但是,我更喜欢更规范的解决方案。是否有read
可以通过避免递归来解析深层嵌套结构的内置替代方案?
编辑:这似乎是 sbcl 本身的一个不可克服的问题,而不是输入数据。举个简单的例子,尝试运行:
(for i in $(seq 1 2000); do
echo -n "("
done; echo -n "2"; for i in $(seq 1 2000); do
echo -n ")"
done; echo) > file
然后在 sbcl 中:
(with-open-file (file "file" :direction :input) (read file))
发生同样的故障。
编辑:在 上询问#sbcl
,显然控制堆栈大小确实仅适用于新线程,并且主线程的堆栈大小也受到许多其他因素的影响。所以我尝试将读取放在单独的线程中。仍然没有工作。如果您有兴趣,请查看此 repo并运行。make check