3

I want to implement automatic benchmarking for the packages I load (some of which are my own). Right now my preamble takes 15 seconds to load, much longer than the rest of my document seems to take to compile.

For this I wanted to implement a remark from this answer, which recommended using etoolbox's patchcmmd to patch usepackage. As most of my package loading happens inside of my own packages, I wanted to use this approach on RequirePackage. I opted however to use xpatch's utilities, as I had read that the normal patchcmd can't deal with commands with optional arguments.

My best try so far is this (please don't mind the ugly automatic output via AfterEndPreamble):

\RequirePackage{fp}
\RequirePackage{xpatch}
\newcount\timer

\xpretocmd{\RequirePackage}{\pdfresettimer}{}{} \xapptocmd{\RequirePackage}{ \timer=\pdfelapsedtime \FPdiv\temp{\the\timer}{65.536} \FPtrunc\temp\temp{3} \AfterEndPreamble{\par{#1:} \temp} }{}{}

% Testing \RequirePackage{tikz} \RequirePackage{mathtools}

\documentclass{article} \begin{document}

\end{document}

But sadly this produces an Error:

! LaTeX Error: File `\timer.sty' not found.

My best guess is that this is because ProvideCommand has a second optional argument after the normal one and xpatchcmd can't handle that, causing ProvideCommand to consume the appended text as it's main argument. But I'm way out of my comfort zone here, and I have not managed to understand ProvideCommand via \show so far (and I assume there must be a better way of just patching a command with surrounding code anyway, without knowing its internal structure). Any help is greatly appreciated.

r0uv3n
  • 33

2 Answers2

4

You could use (with a new latex) the package hooks to define a list of packages that should be measured (the calculation is stolen from egregs answer):

\ExplSyntaxOn
\newcommand{\BenchmarkPackages}[1]
 {%
  \clist_map_inline:nn{#1}
   {
    \AddToHook{package/before/##1}{\pdfresettimer}
    \AddToHook{package/after/##1}{\typeout{##1: \fp_eval:n{round(\pdfelapsedtime/65.536,3)}}}
   } 
 }
\ExplSyntaxOff
\BenchmarkPackages{tikz,mathtools,scrbase}

\documentclass{scrartcl} \usepackage{tikz} \begin{document} alblb \end{document}

This will then write something like tikz:1085.999 in the log (one could naturally also store the value and print it later).

Ulrike Fischer
  • 327,261
2

Asking latexdef RequirePackage yields

% latex.ltx, line 10034:
\def\RequirePackage{%
  \@fileswithoptions\@pkgextension}

Oh, well, now we need to do latexdef @fileswithoptions

% latex.ltx, line 10097:
\def\@fileswithoptions#1{%
  \@ifnextchar[%]
    {\@fileswith@ptions#1}%
    {\@fileswith@ptions#1[]}}

which means that the macro absorbs one token or one braced argument and tests if a [ comes along. If you add those tokens at the end of \RequirePackage, the argument passed to \@fileswithoptions will be \@pkgextension and the test for [ will return false, so we'd end up with

\@fileswith@ptions\@pkgextension\timer=\pdfelapsedtime

and this will fail as you see, because

\def\@fileswith@ptions#1[#2]#3{%
  \@ifnextchar[%]
  {\@fileswith@pti@ns#1[{#2}]#3}%
  {\@fileswith@pti@ns#1[{#2}]#3[]}}

so argument #3 will be \timer instead of a list of file names (without extensions).

Sorry, but this is not going to work in any sensible way, because of nested \RequirePackage done by the packages you load.

\RequirePackage{fp}
\newcount\timer

\newcommand{\TimeRequirePackage}[1]{% \pdfresettimer \RequirePackage{#1}% \timer=\pdfelapsedtime \FPdiv\temp{\the\timer}{65.536}% \FPtrunc\temp\temp{3}% \printresult{#1}% } \newcommand{\printresult}[1]{\expandafter\printresultaux\expandafter{\temp}{#1}} \newcommand{\printresultaux}[2]{\AtBeginDocument{\par#2: #1}}

% Testing \TimeRequirePackage{tikz} \TimeRequirePackage{mathtools}

\documentclass{article} \begin{document} \end{document}

enter image description here

Note that you need to expand \temp before passing it to \AtBeginDocument (not \AtEndPreamble).

With xfp

\RequirePackage{xfp}

\newcommand{\TimeRequirePackage}[1]{% \pdfresettimer \RequirePackage{#1}% \expanded{\noexpand\AtBeginDocument{\par#1: \fpeval{round(\pdfelapsedtime/65.536,3)}}} }

% Testing \TimeRequirePackage{tikz} \TimeRequirePackage{mathtools}

\documentclass{article} \begin{document} \end{document}

enter image description here

egreg
  • 1,121,712
  • Ah, I had hoped there would be a more general solution. Is there an easy way to define this command \TimeRequirePackage in a way that it can deal with the \RequirePackage[options]{package}[date] commands? \IfValueTF deals well with the \RequirePackage[options]{package} case. I have a few of those \RequirePackage commands with date in my files. In fact, I think this was the reason I initially tried the approach of patching the command via xpatch. – r0uv3n Apr 01 '21 at 15:37
  • Also, regarding the nested \RequirePackages, couldn't I just use \csname #1time \endcsname (with appropriate \expandafters) instead of \temp and then use differences in \pdfelapsedtime instead of resetting the pdf timer each time the command is invoked? Or is there some more fundamental obstacle to this? – r0uv3n Apr 01 '21 at 16:02