TeX 提供了许多方法来做某些事情,有时很难预测哪种方法最快。例如,如何
- 宏分配
- toks 作业
- 宏扩展
- toks 扩展
相互比较(当然取决于参数的长度以及参数的数量)。
有人告诉我\romannumeral-`\0\somelongexpansion
比 慢\somelongexpansion
。这是真的吗/我该如何测试它/它已经测试过了吗?
此外,expl3 源指出,要吞噬三个括号组,吞噬两个括号组会比吞噬一个括号组更快(相当于\@gobble
和\@gobbletwo
,而不是\long\def\@gobblethree#1#2#3{}
)。但初步测试\pdfelapsedtime
似乎指向相反的方向。
所以我想我的问题是,在对 TeX 的各个部分进行基准测试方面存在什么(我对胃特别感兴趣,但 TeX 的任何身体部位都值得赞赏)。
答案1
对于一般基准测试,我使用 David Kastrup 的宏\replicate
来迭代和两个宏\startTimer
,\stopTimer
基于\pdfsettimer
和。我将其用于数量级基准测试(使用 pdfLaTeX)。例如,这是一个测试程序,它计算和排版斐波那契数以比较使用pdfelapsedtime
的效果。\num
siunitx
\documentclass[a4paper]{article}
\usepackage{siunitx,fp}
\def\startTimer{\pdfresettimer}
\def\stopTimer{%
\the\pdfelapsedtime\,scaled seconds
\FPdiv\result{\the\pdfelapsedtime}{65536}
% \FPmul\result{\result}{1000000} %microseconds
\FPround\result{\result}{6}
\result\thinspace s}
\newcount\numbertimes
\newcount\numone
\newcount\numtwo
\newcount\savenumone
\def\fibonacci#1{0,
\numbertimes=2\numone=0
\numtwo=1
\loop
\advance\numone by \numtwo
%\num{\the\numone},
\the\numone,
\savenumone=\the\numone \numone=\numtwo \numtwo=\savenumone
\advance\numbertimes by 1 \ifnum \numbertimes<#1
\repeat
%\ifnum\numbertimes=#1 \advance\numone by \numtwo\fi and \num{\the\numone}.\par
\ifnum\numbertimes=#1 \advance\numone by \numtwo\fi and \the\numone.\par
}
\begin{document}
\startTimer
\def\replicate#1#2{\ifnum#1>0 #2%
\expandafter\replicate\expandafter{\number\numexpr#1-1}{#2}\fi}
\replicate{4900}{\fibonacci{30}\fibonacci{30}\fibonacci{30}}
\vskip12pt
\stopTimer
\end{document}
如果你用 格式化它,num
你将得到比使用纯数字至少高两个数量级的结果。这对于识别代码中的瓶颈很有用。
这种方法的缺陷很多可以在以下文章中找到:https://tex.stackexchange.com/questions/tagged/profiling
另请参阅下面 Bruno 的评论并根据您的喜好修改代码。
答案2
这是吞噬比较的结果。此测试运行了 1,000,000 次相同的命令。它在此测试中仅吞噬空参数。另请参阅我在问题下方的评论。
结果是:
总次数:
real 0m5.422s
user 0m5.360s
sys 0m0.050s
结果(以秒为单位,1/65536 秒)
\expandafter \@gobbletwo \@gobble : 95638
\expandafter \@gobble \@gobbletwo : 93726
\@gobbletwothenone : 56081
\@gobbleonethentwo : 57910
\@gobblethree : 45746
似乎\expandafter
需要相当多的时间。如果三个参数被两个宏吞噬,则顺序似乎并不重要。\@gobblethree
宏是最快的。
当要吞噬的参数包含一些实际文本时,读取这些标记的时间比吞噬它的时间要多,因此所有时间都非常接近。
基准文件:
\documentclass{minimal}
\makeatletter
\long\def\@gobblethree#1#2#3{}
\long\def\@gobbleonethentwo#1{\@gobbletwo}
\long\def\@gobbletwothenone#1#2{\@gobbleone}
\begin{document}
\input{a1}
\input{a2}
\input{a3}
\input{a4}
\input{a5}
\end{document}
输入文件为:
% a1.tex
\def\name{\expandafter\@gobbletwo\@gobble}%
\@onelevel@sanitize\name
\pdfresettimer
\expandafter\@gobbletwo\@gobble{}{}{}%
% ...
\typeout{\name: \the\pdfelapsedtime}
% a2.tex
\def\name{\expandafter\@gobble\@gobbletwo}%
\@onelevel@sanitize\name
\pdfresettimer
\expandafter\@gobble\@gobbletwo{}{}{}%
% ...
\typeout{\name: \the\pdfelapsedtime}
% a3.tex
\def\name{\@gobbletwothenone}%
\@onelevel@sanitize\name
\pdfresettimer
\@gobbletwothenone{}{}{}%
% ...
\typeout{\name: \the\pdfelapsedtime}
% a4.tex
\def\name{\@gobbleonethentwo}%
\@onelevel@sanitize\name
\pdfresettimer
\@gobbleonethentwo{}{}{}%
% ...
\typeout{\name: \the\pdfelapsedtime}
% a5.tex
\def\name{\@gobblethree}%
\@onelevel@sanitize\name
\pdfresettimer
\@gobblethree{}{}{}%
% ...
\typeout{\name: \the\pdfelapsedtime}