ConTeXt 提供了一个宏\testfeatureonce
来对性能进行基准测试。语法是
\testfeatureonce{n}{...}
它运行第二个参数 times 中的代码n
(n
假定为整数),并将经过的时间(以秒为单位)存储在宏中\elapasedtime
。终端上还会打印以下诊断消息:
system > starting feature test (n=1) system > 1 feature tests done (1.353s)
下面是一个最简单的 ConTeXt 文档,展示了它的用法:
\starttext
\testfeatureonce{1000}
{\setbox0\hbox
{\startMPcode draw (0,0) -- (1cm, 1cm); \stopMPcode}}
\elapsedtime
\stoptext
LaTeX 中用于基准测试性能的功能等效宏是什么?
答案1
据我所知,LaTeX2e 基础中没有这样的宏(也许 LaTeX3 有等效的宏?)。但我们可以\testfeatureonce
在 ConTeXt 源代码中查找定义。相关定义位于syst-aux
模块。
该实现分别利用 eTeX 基元\pdfresettimer
和\pdfelapsedtime
重置内部计时器并获取自上次重置以来经过的时间。我没有找到合适的来源,但似乎 的返回值中的 65536 等于一秒\pdfelapsedtime
。
纯 LaTeX 代码的重新实现可能如下所示:
\documentclass{article}
\usepackage{tikz}
\makeatletter
\let\resettimer=\pdfresettimer
\let\elapsedtime=\pdfelapsedtime
\newcount\c@syst@helpers@test@feature@n
\newcount\c@syst@helpers@test@feature@m
\newcommand\testfeature[2]{%
\c@syst@helpers@test@feature@m=#1\relax
\def\syst@helpers@test@feature@step{%
\advance\c@syst@helpers@test@feature@n by 1\relax
\ifnum\c@syst@helpers@test@feature@n>\c@syst@helpers@test@feature@m\else
#2\expandafter\syst@helpers@test@feature@step
\fi
}%
\retestfeature
}
\newcommand\retestfeature{
\bgroup
\ifcase\interactionmode \let\wait\relax \fi
\resettimer
\c@syst@helpers@test@feature@n=0\relax
\syst@helpers@test@feature@step
\wait
\egroup
}
\newcommand\testfeatureonce[2]{%
\begingroup
\let\wait\relax
\testfeature{#1}{#2}%
\endgroup
}
\makeatother
\begin{document}
\testfeatureonce{1000}
{\setbox0\hbox
{\tikz{ \draw (0,0) -- (1cm, 1cm); }}}
\the\elapsedtime
\end{document}
答案2
您expl3
可以使用\tex_resettimer:D
和\tex_elapsedtime:D
,它们既适用于 pdfTeX 也适用于自 TL 2019 以来的 XeTeX(它们是原语\(pdf)resettimer
并\(pdf)elapsedtime
已重命名)。对于 LuaTeX,您可以使用 中的 Lua 函数l3kernel
。其中大部分是软件包中内容的基本版本l3benchmark
(请参阅 Joseph 的回答),在提出此问题后不久发布。
\documentclass{article}
\usepackage{xparse}
\usepackage{tikz}
\ExplSyntaxOn
\sys_if_engine_luatex:TF
{
\cs_new:Npn \__tfo_start_timer: { \lua_now:n { l3kernel.resettimer() } }
\cs_new:Npn \__tfo_elapsed_time: { \lua_now:n { l3kernel.elapsedtime() } }
}
{
\cs_new_eq:NN \__tfo_start_timer: \tex_resettimer:D
\cs_new_eq:NN \__tfo_elapsed_time: \tex_elapsedtime:D
}
\cs_new:Npn \__tfo_output:n #1
{
\iow_term:x
{
>~#1~feature~tests~done~
(\fp_eval:n { round ( \__tfo_elapsed_time: / 65536 , 3 ) }s)
}
}
\cs_new:Npn \__tfo_run_n_times:nn #1 #2
{
\if_int_compare:w #1 > 0 \scan_stop:
\exp_args:No \__tfo_run_n_times:nnw { \int_value:w \__int_eval:w #1 - 1 } { #2 }
\fi:
}
\cs_new:Npn \__tfo_run_n_times:nnw #1 #2 \fi:
{
\fi:
#2
\__tfo_run_n_times:nn { #1 } { #2 }
}
\NewDocumentCommand\TestFeatureOnce
{ m m }
{
\__tfo_start_timer:
\__tfo_run_n_times:nn { #1 } { #2 }
\__tfo_output:n { #1 }
}
\ExplSyntaxOff
\begin{document}
\TestFeatureOnce{10000}
{\setbox0\hbox
{\tikz \draw (0,0) -- (1cm, 1cm);}}
\end{document}
编辑:错误修复。大量的测试(~5000)会因为堆积大量的\fi:
s 而超出 TeX 的输入堆栈大小,因此我添加了一个辅助宏来确保所有内容都在外部执行,\if...\fi
以便可以进行任意数量的运行。
使用 jfbu 转换缩放秒的想法,甚至可以在纯 (pdf、Xe 或 Lua)TeX 中完成此操作:
\input tikz.tex
\begingroup% pdfTeX
\expandafter\ifx\csname pdfresettimer\endcsname\relax
\else
\global\let\resettimer\pdfresettimer
\global\let\elapsedtime\pdfelapsedtime
\fi
\endgroup
\begingroup% LuaTeX
\expandafter\ifx\csname directlua\endcsname\relax
\else
\gdef\resettimer{\directlua{pdfelapsedtimer_basetime = os.clock()}}
\gdef\elapsedtime{\directlua{tex.print((os.clock()-pdfelapsedtimer_basetime)*65536)}}
\fi
\endgroup
% For XeTeX the primitives are already called \resettimer and \elapsedtime
\catcode`@=11
% Stolen from latex.ltx
\begingroup
\catcode`P=12
\catcode`T=12
\lowercase{%
\def\x{\def\rem@pt##1.##2PT{##1\ifnum##2>\z@.##2\fi}}}%
\expandafter\endgroup\x
\def\strip@pt{\expandafter\rem@pt\the}%
%
\let\TFO@start@timer\resettimer
\let\TFO@elapsed@time\elapsedtime
\def\TFO@output#1{%
\immediate\write17{%
> #1 feature tests done (\strip@pt\dimexpr\TFO@elapsed@time sp\relax s)
}%
}
\long\def\TFO@runNtimes@nn#1#2{%
\ifnum#1>0\relax
\expandafter\TFO@runNtimes@nnw\expandafter{\number\numexpr#1-1}{#2}%
\fi
}
\long\def\TFO@runNtimes@nnw#1#2\fi{%
\fi
#2%
\TFO@runNtimes@nn{#1}{#2}%
}
\long\def\TestFeatureOnce#1#2{%
\TFO@start@timer
\TFO@runNtimes@nn{#1}{#2}%
\TFO@output{#1}%
}
\catcode`@=12
\TestFeatureOnce{1000}
{\setbox0\hbox
{\tikz \draw (0,0) -- (1cm, 1cm);}}
\bye
答案3
使用新l3benchmark
软件包的代码(于 2018-10-26 发布至 CTAN):
\documentclass{article}
\usepackage{l3benchmark}
\ExplSyntaxOn
\cs_new_protected:Npn \testfeatureonce #1#2
{
\benchmark_once:n
{ \prg_replicate:nn {#1} {#2} }
}
\ExplSyntaxOff
\usepackage{tikz}
\begin{document}
\testfeatureonce{1000}
{\setbox0\hbox
{\begin{tikzpicture} draw (0,0) -- (1cm, 1cm); \end{tikzpicture}}}
\end{document}
基准测试代码还可以自动确定要执行的循环数:
\documentclass{article}
\usepackage{l3benchmark}
\ExplSyntaxOn
\cs_new_protected:Npn \testfeatureonce #1#2
{ \benchmark:n {#2} }
\ExplSyntaxOff
\usepackage{tikz}
\begin{document}
\testfeatureonce{1000}
{\setbox0\hbox
{\begin{tikzpicture} draw (0,0) -- (1cm, 1cm); \end{tikzpicture}}}
\end{document}
(代码本身当然与这里的其他答案非常相似。)
答案4
一个原始的基准测试实用程序:
\documentclass{article}
\usepackage{xintkernel}
\makeatletter
\def\testfeatureonce#1#2{\pdfresettimer
\romannumeral\xintreplicate{#1}{#2}%
\edef\theelapsedtime{\strip@pt\dimexpr\pdfelapsedtime sp\relax s}%
% \theelapsedtime % commented-out
}
\makeatother
\usepackage{tikz}
\begin{document}
\testfeatureonce{1000}
{\setbox0\hbox
{\tikz{ \draw (0,0) -- (1cm, 1cm); }}}
\typeout{Previous test took \theelapsedtime}
\end{document}
结果被存储(但已转换为秒)以供重复使用。
....
(/usr/local/texlive/2018/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg))
Previous test took 0.92303s
[1{/usr/local/texlive/2018/texmf-var/fonts/map/pdftex/updmap/pdftex.map}]
....
该包xintkernel
是非常轻量的包(也提供 \xintUniformDeviate
),它本身不具备任何计算功能xint
。
这\xintreplicate
基本上与 l3kernel 中的原始代码相同(抄袭)。
欲了解更高级的基准测试,请参阅l3benchmark
。
以上内容未计算扩展的开销\xintreplicate
(这里只是 tikz 的一小部分)。如果您想要 1000000 个复制,最好嵌套两个\romannumeral\xintreplicate{1000}
。