对 TeX 的各种操作进行基准测试

对 TeX 的各种操作进行基准测试

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的效果。\numsiunitx

\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}

相关内容