十秒钟速览

  1. 换用 Linux 系统
  2. 使用批处理模式,即编译时加上 -interaction=batchmode 参数
  3. 使用预编译技术,涉及到一个宏包(mylatexformat)和两条命令:
    1. etex -initialize -jobname="hello" "&pdflatex" "mylatexformat.ltx" "hello.tex"
    2. pdflatex -shell-escape "&hello" hello.tex
  4. 一些其他(作用较小的或者适用范围狭窄的)技巧

前言

“We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.”

“我们应该忽略小的开销,比方说97%的时间:过早优化是万恶之源。然而我们不应放弃那重要的3%的机会。”

——Donald E. Knuth. "Structured Programming with Goto Statements". Computing Surveys 6:4 (December 1974), pp. 268, §1.


不只一个人跟我抱怨过,“LaTeX 的编译实在是太慢了”。尤其是在入门阶段,用户需要不断重复编译和预览的过程,来验证代码的功能。

实际上,考虑到 TeX 系统在底层要做的大量工作,它的速度已经很快了。然而贪婪的用户只会怀念 Word 这样的文字处理软件,并且由衷地感叹,“为什么 TeX 不能像 Word 那样即改即得呢”。

我这里不会向你介绍 TeX 底层是多么辛苦,做了多少繁琐的工作,兼顾了多少复杂的情况。因为大部分用户并不在乎。

我也不必解释 TeX 的使用方式和 Word 相比是怎样的不同。因为你不应说服一个抡锤子砸墙的人去使用电钻。

这篇文章面向有一定基础并且面临同样问题的 TeX 用户,当然对于新手来说也并不难懂。如果你觉得用不上或者不会用,不必感到烦恼,等你真正需要用的时候自然就能理解了。


性能瓶颈分析

正如开篇所言,仅凭模糊猜测和反复试验去做优化是非常低效的,这是企图用战术上的勤奋(不断调试)来掩盖战略上的懒惰(不去探究背后的原理和机制)。

关于 TeX 程序的性能瓶颈,其实在 @李阿玲 的 这个回答 中已经说得很好了。

我这里摘抄核心部分如下:

任何TeX,影响它速度主要还是文件I/O,尤其是Windows下面的影响,非常显著。同等的硬件,换成Linux+ext4的话,效果会很不一样。

其次是解析上的操作,从源文件到token这一级别,中间有可能做编码转换之类。但是这一部分不会耗时太长。之所以能够影响速度是因为调用的包的子文件比较多,尤其是tikz之类的大包。

插图其实并不会怎么影响TeX的速度,之所以让人感觉插图会比较耗时,那是因为XeTeX调用了dvipdfmx插图的时候会读文件,会有压缩运算,这速度自然会慢一些。

总结起来就是 TeX 的性能瓶颈在于:文件 I/O

而文件 I/O 速度主要取决于操作系统和硬件。

这是因为在不同的操作系统下,TeX 系统的实现是有区别的,这也就造成了文件 I/O 速度上的差异。

经过粗略且不准确的测试,在相同硬件性能的情况下,同样的 TeX 代码,在 Ubuntu 18.04 虚拟机下的运行时间大概是 Windows 10 物理机的 40% ~ 70%。(@李阿玲 说如果采用 Linux 物理机性能提升会更加明显。由于手头没有配 Linux 的物理机,所以暂时无法验证。)

此外,一个值得注意的说法是,将机械硬盘换成固态硬盘效果更佳。然而我在测试当中,发现在 Windows 下,TeX 系统装在不同类型的硬盘中并没有明显的速度差别(甚至将 TeX 装到 RAMDisk 中也没有明显提升)。希望有大佬给出相关的测试数据,验证或否定我的测试结果(最好能否定),要是能说明原因就更好了。这也是我感到十分不解的一个地方。

除了存储设备,CPU 也有一定的影响,不过提升 CPU 性能获得的收益,并不如提升文件 I/O 速度。

当然,确实有人问过 LaTeX 编译性能最佳的硬件配置:

  • Tips for choosing hardware for best LaTeX compile performance

结论是:起决定性因素的是 CPU 的单线程计算能力。

这个贴子下的回答,也提到了和我上面的测试一致的结果:磁盘 I/O 速度对于 TeX 的文件 I/O 并没有太大影响。

所以我觉得这里所说的“文件 I/O 速度”,可能并不是单纯指将 .tex/.sty/.cfg/.def 这些文件导入到内存的速度,而是包括了 TeX 解析各种宏包再导入子文件时的速度,而不同操作系统下的差异就体现在这一步。

你问我能不能用 GPU 加速 LaTeX 编译?没错,也有人问过了:

  • Is it possible to use GPU acceleration on compiling large LaTeX documents?

结论是:几乎不可行。除非你能将 TeX 的 CPU 指令转换为 GPU 指令,并且让操作系统支持这些操作。

不同的引擎(pdfTeX、XeTeX、LuaTeX)的速度也会有些许差异。比如一般情况下 pdfTeX 速度要快于其他引擎,不过也有 ptex-ng 这种性能更高的引擎(是基于 C 语言的 TeX82 实现),我有幸尝试过一个私人版本,速度快得飞起。大家有兴趣可以邀请它的作者 @李阿玲 多科普科普这个引擎。

不同的编译模式也有细微的影响。经过测试,使用批处理模式(batchmode)速度要优于默认的模式(不加参数)和其他一些模式(比如 nonstopmode 和 scrollmode), 这是因为批处理模式在编译和执行阶段是静默的,不输出任何信息,因此要快上一些。


性能提升方法

上面扯了那么多瓶颈,那么下面谈一谈怎么改善这些瓶颈。

其实主要有三个办法:换用 Linux 、使用批处理模式和预编译。

TeX 在 Linux 下的表现确实是大大优于 Windows 的。不过这个方法对大多数人来说并不现实,毕竟大多数人习惯用的系统是 Windows。

使用批处理模式,就是编译时加上 -interaction=batchmode 参数,即 pdflatex -interaction=batchmode hello.tex。需要注意的是,批处理模式不会报错(虽然我用 Sublime Build System 时会提示返回 exit code 1)。因此如果需要检查代码的错误,那么不建议使用批处理模式。

预编译就是生成 .fmt 文件,把 .tex 文件序言区那些诸如 newcommand 和 usepackage 的东西打包起来。

大家都有经验,复制很多小文件花费的时间,要远大于复制单个大文件的时间。TeX 的预编译把各种小文件打包成一个大文件,那么之后的编译,只需要导入和解析这个大文件就可以,就不用挨个再索引和导入各种零碎的小文件了。它在 I/O 速度上的提升正源于此。

有一个 mylatexformat 宏包,可以帮助用户预编译文件序言区。在 mylatexformat 之前其实有另外一个同样功能的宏包,叫 mylatex 。不过我还是推荐使用 mylatexformat

当然还有人会提到一些其他的技巧,比如:

  1. 将 TikZ 绘图的代码提前编译好,直接 include 生成的 pdf 文件
  2. 避免使用 ctex 和 tikz 这样的庞大而零碎的宏包
  3. 对于 pdfTeX 引擎,可以加上 -draftmode 参数
  4. 对于 XeTeX 引擎,可以加上 -output-driver='xdvipdfmx -z0' 参数调整压缩级别,用文件大小换取编译速度

这些技巧的改善作用其实并不大,而且也有明显的适用范围限制。大家可以酌情尝试和选用。

虽然上面提到不同的引擎(pdfTeX、XeTeX、LuaTeX)有速度差异,不过并不明显,所以不必纠结。

下面着重介绍如何预编译 .tex 文件。


预编译流程

假设你有这样一个 hello.tex 文件:

\documentclass[border=5pt, tikz]{standalone}
\usepackage{tikz}
\usepackage[UTF8]{ctex}
\newcommand{\hello}{Hello 你好}\begin{document}
\begin{tikzpicture}
\node [draw, thick, minimum width=10em, minimum height=10em] {English 中文 \hello};
\end{tikzpicture}
\end{document}

那么预编译只需要在命令行中运行这条指令:

etex -initialize -jobname="hello" "&pdflatex" "mylatexformat.ltx" "hello.tex"

就能在文件夹中看到 hello.fmt 文件,这个文件里包含了你在 hello.tex 序言区定义的宏和导入的宏包。

  • etex 是原生 TeX 的一个扩展版本,源于 NTS (New Typesetting System)项目,是开发 LaTeX2e 时用的引擎,后续的引擎比如 pdfTeX 和 XeTeX 都以它为基础或者参照。

  • -initialize 参数就是表明要将文件转储(dump)到一个中间文件,这里也就是生成 .fmt 文件

  • -jobname="hello" 是指定生成的 .fmt 文件名为 hello.fmt

  • &pdflatex 指定生成的 .fmt 文件对应的引擎是 pdfLaTeX

  • "mylatexformat.ltx" 则是我们上面提到的宏包,用于对序言区进行处理,方便 .fmt 文件的转储

  • hello.tex 自然就是源文件了,写在序言区的语句会被预编译(更高级地,可以用 \endofdump 这样的语句指定哪些部分要预编译,哪些不要预编译)

预编译时输出的信息是这样的:

This is pdfTeX, Version 3.14159265-2.6-1.40.19 (MiKTeX 2.9.6930 64-bit) (INITEX)
entering extended mode% 各种导入的文件信息(C:\Users\xxx\AppData\Roaming\MiKTeX\2.9\tex/latex/mylatexformat\mylatexforma
t.ltx
LaTeX2e <2018-12-01>
) (hello.tex
(C:\Users\xxx\AppData\Roaming\MiKTeX\2.9\tex/latex/standalone\standalone.cls
Document Class: standalone 2018/03/26 v1.3a Class to compile TeX sub-files stan
dalone
(D:\MiKTeX\tex/latex/tools\shellesc.sty)
(D:\MiKTeX\tex/generic/oberdiek\ifluatex.sty)
(D:\MiKTeX\tex/generic/oberdiek\ifpdf.sty)
(D:\MiKTeX\tex/generic/ifxetex\ifxetex.sty)
...
(C:\Users\xxx\AppData\Roaming\MiKTeX\2.9\tex/generic/ctex\zhwindowsfonts.tex{
C:/Users/xxx/AppData/Local/MiKTeX/2.9/pdftex/config/pdftex.map}{UGBK.sfd}{Uni
code.sfd}))))
(C:\Users\xxx\AppData\Roaming\MiKTeX\2.9\tex/latex/ctex/config\ctex.cfg) )% 接下来就是将上面导入的子文件和字体 dump 到 hello.fileBeginning to dump on file hello.fmt(preloaded format=hello 2019.1.16)
30612 strings of total length 571285
464867 memory locations dumped; current usage is 329&459222
26854 multiletter control sequences% 一些字体相关的配置\font\nullfont=nullfont
\font\OMX/cmex/m/n/5=cmex10
\font\tenln=line10
\font\tenlnw=linew10
\font\tencirc=lcircle10
...
\font\OT1/cmr/bx/it/9.03374=cmbxti10 at 9.03374pt
\font\OT1/cmr/bx/it/7.52812=cmbxti10 at 7.52812pt
\font\OT1/cmr/bx/it/5.52061=cmbxti10 at 5.52061pt
537653 words of font info for 41 preloaded fonts% 字符连接的信息1141 hyphenation exceptions
Hyphenation trie of length 362741 has 8547 ops out of 35111143 for language 74377 for language 73110 for language 72
...97 for language 2137 for language 1181 for language 0% 告诉你它完事了0 words of pdfTeX memory
3 indirect objects
No pages of output.
Transcript written on hello.log.

然后编译原文件。在命令行运行:

pdflatex -shell-escape "&hello" hello.tex

或者:

latex -shell-escape --output-format pdf "&hello" hello.tex

  • 此时我们加上 &hello 参数,也就是用上了 hello.fmt 文件
  • 不要忘记 -shell-escape 参数

更多 mylatexformat 宏包使用相关的细节可以参考它的文档:CTAN: Package mylatexformat。

这里有几点说明:

  • 预编译对 pdfTeX 的支持是比较好的,也支持大多数宏包。
  • 一旦涉及到某些字体相关的事情,预编译就会出现问题。这是因为 .fmt 格式本身就是上古时期的产物,没有兼顾到很多字体方面的事情。比如虽然文档中给出了 XeTeX 编译时的用法(使用 xetex 引擎和 &xelatex 参数),然而实际运行时总是会出现 ! Can't \dump a format with native fonts or font-mappings. 的错误。所以不建议在预编译时使用 XeTeX 引擎。
  • 虽然 ctex 这样处理 CJK 字符的宏包还能用,也可以使用 \kaishu 和 \lishu 这样的宏,但是相关的 CJK 字体并不是无级缩放的,也就是放大到一定程度之后会出现锯齿。此外,\setCJKmainfont 这类更加复杂的宏也是无法使用的。
  • 如果需要频繁修改序言区,那么可以先不预编译,以节省时间(虽然预编译也没有多花多少时间)。等到序言区的内容基本固定,只需要一次预编译,之后就可以只运行 pdflatex -shell-escape "&hello" hello.tex 这一条编译命令了。
  • 输出最后的成品时,还是建议直接使用 pdflatex hello.tex 或者 xelatex hello.tex,以消除预编译导致的一些格式和字体上的差异。

如果你用的编辑器是 Sublime,还可以自己写一个 Build System 的脚本,将预编译这个选项加进去,这样就不用每次都切到命令行界面运行了。

我自己用的配置如下,供有需要的人参考:

mylatex.sublime-build

{"selector": "text.tex","variants":[{   "name": "pdflatex - precompile + batchmode + .fmt","shell_cmd": "etex -initialize -jobname=\"$file_base_name\" \"&pdflatex\" \"mylatexformat.ltx\" $file_base_name.tex & pdflatex -shell-escape -interaction=batchmode -aux-directory=latex-temp \"&$file_base_name\" $file_base_name.tex",},{   "name": "pdflatex - batch mode + .fmt","shell_cmd": "pdflatex -shell-escape -interaction=batchmode -aux-directory=latex-temp \"&$file_base_name\" $file_base_name.tex",},{   "name": "pdflatex - precompile","shell_cmd": "etex -initialize -jobname=\"$file_base_name\" \"&pdflatex\" \"mylatexformat.ltx\" $file_base_name.tex",},{   "name": "pdflatex - batch mode","shell_cmd": "pdflatex -shell-escape -interaction=batchmode -aux-directory=latex-temp $file_base_name.tex",},{   "name": "pdflatex - normal mode + .fmt","shell_cmd": "pdflatex -shell-escape -aux-directory=latex-temp \"&$file_base_name\" $file_base_name.tex",},{   "name": "pdflatex - normal mode","shell_cmd": "pdflatex -shell-escape -aux-directory=latex-temp $file_base_name.tex",},{   "name": "xelatex - normal mode","shell_cmd": "xelatex -aux-directory=latex-temp $file_base_name.tex",},{   "name": "xelatex - batch mode","shell_cmd": "xelatex -aux-directory=latex-temp -interaction=batchmode $file_base_name.tex",},{   "name": "lualatex - normal mode","shell_cmd": "lualatex -aux-directory=latex-temp $file_base_name.tex",},{   "name": "lualatex - batch mode","shell_cmd": "lualatex -aux-directory=latex-temp -interaction=batchmode $file_base_name.tex",},]
}

相关资料

我在 TeX.SE 的 这个问题 下已经罗列了大部分加速 TeX 编译的相关问题链接:

  • Speeding up LaTeX compilation
  • How to speed up LaTeX compilation with several TikZ pictures?
  • Ultrafast PDFLaTeX with precompiling
  • Externalizing tikz with precompiled preamble
  • Precompile header with xelatex
  • How to speed up pdflatex for a very large document on MacOS X?
  • Speeding up compilation using precompiled preamble with LuaTeX

以及:

  • Precompiled Preamble for LaTeX
  • LaTeX: Speed up Latex compilation by precompiling the preamble part
  • Precompileing parts of a document other than the header
  • Example of Using a Pre-Compiled LateX Preamble
  • LaTeXDaemon
  • How to create a new format file

如何加速 LaTeX 编译相关推荐

  1. 加速LaTeX编译速度:使用VScode远程工具Remote-SSH在Linux系统上编译LaTeX

    由于在Windows中编译LaTeX的速度要远低于在LInux中编译速度,但我们可能经常需要在Windows系统上办公,因此有没有一种方法可以在Windows中用LaTeX写文章同时享受Linux的编 ...

  2. Latex编译过程中遇到的奇奇怪怪的问题及解决方案

    标签(空格分隔): 杂七杂八的问题 有必要写一个博文记录自己在Latex编译时遇到的各种问题,希望可以帮到遇到同样错误的亲故.讲真,一直没有系统的学习Latex,都是投哪个会直接拿那个会的模板来套,然 ...

  3. Latex编译pdf后的字体嵌入问题

    最近在提交论文的camera ready版本的时候遇到了一些问题.最主要的问题就是,在IEEE express中检测不通过的问题,总是提示一些字体没有embedded. 网上找了一下解决办法,发现检测 ...

  4. Elsevier系统LaTex编译不成功,无法生成PDF的解决方案

    问题描述 Elsevier的LaTex模板上传到系统后,系统并没有正确的生成pdf,而是生成了报错信息. 如果报错信息是 File 'xxx' not found,那么大概率是不兼容文件夹的问题,按照 ...

  5. Latex 编译报错: Misplaced omit.

    Latex 编译报错 Misplaced \omit. 这是标题 问题描述 这是标题 今天Latex编译一直出现这个错误,看了好久,原来是一个简单的顺序问题.话说TeXstudio报错的位置偏得有点远 ...

  6. 参考文献在Latex编译后的文章中无法显示

    参考文献在Latex编译后的文章中无法显示 1.Latex下所遇的问题与解决方法 2.LaTex编译与BibTex编译过程 3.编译后生成文件介绍 1.Latex下所遇的问题与解决方法 Latex因其 ...

  7. Latex编译成功但是无法输出到PDF

    文章目录 1 问题描述 2 参考文献 1 问题描述   最近在写小论文,latex编译得好好的,但是有时候加一两行就不能输出到PDF了,真是打脑壳.后查阅资料,得知原因是超链接跨页,因此一个笨办法是在 ...

  8. LaTex 编译中文

    LaTex 编译中文 可以采用直接引入宏包xeCJK的方式 \usepackage{xeCJK} Ref: 如何配置 MacTeX 的中文支持?

  9. latex编译时报错:runaway argument?

    latex编译时报错:runaway argument? 今天使用WinEdt排版时出现 runaway argument?的错误提示,查阅资料发现是table多次定义,最后删除文件夹里除了Tex之外 ...

最新文章

  1. 利用Bash给Linux服务器增添色彩
  2. 让Maven项目使用Nexus作为远程仓库的settings.xml配置
  3. 数据人必须会的技能,用手机查看数据报告真香
  4. 真的了解js生成随机数吗
  5. 浏览器从输入URL到页面渲染过程 ——页面渲染流程
  6. Python学习笔记之函数(二)
  7. 那些很厉害的人,是如何度过职场迷茫的?
  8. “21天好习惯“第一期-4
  9. Atitit  atiMail atiDns新特性 v2  q39
  10. 【黑客免杀攻防】读书笔记15 - 源码免杀、C++壳的编写
  11. java入门到精通第六版_java从入门到精通-第6章.pdf
  12. jsp+servlet+mysql 学生选课系统
  13. TCP socket 中的长连接与短连接的区别
  14. NYOJ033蛇形填数
  15. 条码打印软件及条码打印机中如何设置纸张大小
  16. 大数据 客户标签体系_基于大数据的用户标签体系建设思路和应用
  17. 《中国聚合支付行业发展报告2018》发布 深度分析未来八大趋势
  18. 网络安全期末复习 - 20190625
  19. 再一次100%通过华为中级认证考试,5G网优工程师高薪稳了!
  20. 【独家】华为OD机试 - 英文输入法(C 语言解题)

热门文章

  1. 北斗导航 | 城市环境下,结合ARAIM和3D城市模型用于多星座GNSS伪距观测的完好性监测(论文分析)
  2. Gartner2017年度的十一大信息安全技术
  3. ENVI5.1新增波谱库及波谱曲线工具
  4. 什么是图灵完备语言?
  5. 自监督学习 | (1) Self-supervised Learning入门
  6. php百度快照劫持,最新wordpress程序被挂马,百度快照被劫持的解决方法 | linux系统运维...
  7. 证实了,百度没有快照了
  8. github上传代码报错remote: Support for password authentication was removed on August 13, 2021. Please use a
  9. CC3 多少个点位于同一直线
  10. 从事硬件低工资高门槛?你和高薪究竟差了哪些东西