我有一本 800 多页的书,在 texlive 2015 下使用 xelatex 和 forest 1.05 编译得很好。我正在迁移到 texlive 2016,因为我的翻译器适用于 forest 2.0。现在我得到:
! TeX capacity exceeded, sorry [pool size=6143546].
\safeiterate@4 ...@countc y\endcsname )\endcsname
\forest@inpath \forest@tem...
l.770 }
如果我删除森林代码,稍后就会收到错误。如果我删除较小的部分,一切都会正常。所以我猜这确实是内存问题。
问题:除了修改一些 tex 配置文件外,我还能做什么?代码应该为多个用户运行,修改内存变量会很糟糕。
抱歉,这里没有最小示例,但我可以将代码发布在 github 或其他地方。
编辑:好的。它一定是 forest 2.0,因为当我在运行 texlive 2016 时加载 forest 1.05 时,一切都正常。
答案1
这确实是一个森林问题,但令人惊讶的是,从软件包的第一个版本开始就存在了。Stefan 只是第一个创建如此长的、充满树状结构的文档的人……而从 v1 切换到 v2 只会将问题推向边缘。
在解释出了什么问题之前:我刚刚将修复版本(v2.1.4)发布到了 CTAN。
问题正是 Ulrike Fischer 在上面的评论中提到的。Forest 的打包算法需要(暂时)存储一些有关坐标(和坐标对)的信息。此外,给定一个坐标(或一对坐标),它需要快速检索有关它的信息。显而易见的解决方案是将信息存储在字典(关联数组)中,坐标是查找键,因此使用 TeX 的控制序列似乎是一个完美的想法,我天真地这样做了(基本上是从我的概念验证 Python 实现中复制粘贴的):
\csdef{forest@(\the\pgf@x,\the\pgf@y)}{...}
乃至
\csdef{forest@(\the\pgf@xa,\the\pgf@ya)--(\the\pgf@xb,\the\pgf@yb)}{...}
没有意识到虽然定义是本地的,但条目将永远保留在 TeX 的哈希表中。这种方法很容易耗尽几千字节的字符串池空间每棵树!
v2.1.4 通过将所有信息存储在单个 toks 寄存器中重新实现了有问题的字典,其内容如下所示(仅显示上述第一个问题):
...(x1,y1){...}(x2,y2){...}...
在这样的结构中搜索特定坐标很容易(尽管比\csname
方法慢):
\def\forest@breakpath@getfromtoks#1#2#3#4{%
% #1=cache toks register, #2=receiving cs, (#3,#4)=point;
% we rely on the fact that the point we're looking up should always be present
\def\forest@breakpath@getfromtoks@##1(#3,#4)##2##3\forest@END{##2}%
\edef#2{\expandafter\forest@breakpath@getfromtoks@\the#1\forest@END}%
(许多软件包都使用这样的系统,例如参见 PGFs \pgfutil@in@
。)
新系统大约慢了 10%,但是:在 Stefan 的 800 多页书中,版本 v2.1.3 超过了 600 万个字符串池限制,而 v2.1.4(以及所有其他加载的软件包)仅使用了 200 万个字符串池限制。对于打包算法的内存消耗,文档长度实际上不再重要。
Stefan,谢谢你发现这个问题,并在过去一周里一直支持我!(提示:经过这几年重新审视打包算法,我相信它也可以变得更快!)