我有一本 800 多頁的書,它可以在 texlive 2015 下使用 xelatex 和forest 1.05 編譯良好。現在我得到:
! TeX capacity exceeded, sorry [pool size=6143546].
\safeiterate@4 ...@countc y\endcsname )\endcsname
\forest@inpath \forest@tem...
l.770 }
如果我刪除森林代碼,稍後會收到錯誤訊息。如果我吃較小的部分,一切都很好。所以我猜這確實是一個記憶體問題。
Q:除了修改一些 tex 設定檔之外,我還能做些什麼嗎?該程式碼應該為多個用戶運行,並且必須修改記憶體變數會很糟糕。
抱歉,這裡沒有最小的範例,但我可以在 github 或其他地方提供程式碼。
編輯:好的。它必須是森林 2.0,因為當我在運行 texlive 2016 時加載森林 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,感謝您發現這個並在過去的一周裡對我的包容! (提示:幾年後重新審視打包演算法,我相信它也可以做得更快!)