[Lowerbounds, Upperbounds]

Algorithms are everywhere.

If you can save six seconds every time you run LaTeX, would you do it?

Indeed, I have been pondering on this issue for quite some time now. Every time I compile “a large document that shall not be named”, I notice that the first six seconds are used to process the code before \begin{document}. But this code—known as the preamble—hardly sees any changes between runs. The preamble for this particular document may be a bit longer than usual, but considering that I am usually working on a chapter at a time using the \includeonly facility (see a tutorial here), these six seconds actually represent over 50% of the running time. Computationally and environmentally, this is horribly inefficient.

But a few days ago, I have finally stumbled upon a solution—it’s called “custom format file”. However, let me call it precompiled preamble, borrowing the concept from C compilers that support precompiled headers. Assuming your source file is main.tex, here is how you may use this method:

  1. Rearrange your preamble so that its static part precedes any dynamic part. What in the preamble can be “dynamic”? For example, if you use \includeonly, you probably want to change its argument rather frequently and so it is not static. Another example is the svninfo package, which introduces a command \svnInfo whose arguments get updated every time you commit. While you can certainly load svninfo.sty in the precompiled preamble, you also want the \svnInfo to remain in main.tex. Finally, some packages read and write auxiliary files when you issue a command that you should put in the preamble (eg, nomencl uses \makenomenclature). Clearly, you have to run these commands in every run.
  2. Extract the static part of the preamble into, say, preamble.tex. At this point, main.tex no longer contains the static part. It should start with the dynamic part, or simply \begin{document} if you have nothing dynamic.
  3. Execute latex -ini -job-name="main" "&latex preamble.tex\dump". You will notice that a new file main.fmt has been generated in the same directory, along with other auxiliary files. PDF users can replace latex with pdflatex in both occurrences and the same applies to the steps below. Also, in some distributions, the options could be “-jobname”. Make sure you get main.fmt and not preamble.fmt in this step.
  4. Edit the first line of main.tex so that it starts with %&main. There can be other stuff on that line too, for example

    %&main -*- mode: latex; TeX-master: "main.tex"; -*-

    works for me. (See this post if you are not familiar with Emacs local variables.)

  5. (Drum beats) Execute latex main.tex as usual. You may get an error about the class file immediately, or you may notice that latex finishes as usual. But if you have paid attention to the output, or if you inspect the the log file, then you will see that the preamble gets processed in “no time” at all. At this point, cheers!
  6. If you happen to hit an error, you will need to run latex -parse-first-line main.tex instead. The extra option will force latex to parse the first line of main.tex where we have asked for main.fmt to be used instead of the default format. (In Linux, you can try to locate texmf.cnf and change the option parse_first_line from f to t.)

To make things slightly fancier, here is an modification of my own. First, add this to the end of preamble.tex:

\def\preambleloaded{Precompiled preamble loaded.}

Then, before the dynamic preamble (or \begin{document} if there is none) of main.tex, add:

\def\ifundefined#1{\expandafter\ifx\csname#1\endcsname\relax}
\ifundefined{preambleloaded}\input{preamble}\else\typeout{\preambleloaded}\fi

The idea is to define a macro in the precompiled preamble and use it to detect if the precompiled preamble has been loaded or not. If not, \input{preamble} will be used to pull the static preamble back in and you will not see any error at all.

Of course, the actual amount of time saved will depend on your computer and your preamble. But in my case of long preamble and short document, the saving has been truly tremendous. (Hence I took some time to write this post in the hope that it would help someone down the road.)

Updated 2007/11/04: Changed the format file name to main.fmt and expanded on the dynamic preamble note.

File 2007/11/11: Attached beamer preamble test files in a zip, as referred to in my comment.

Updated 2007/11/11: Added comments for Linux users, after resolving the issue raised by Suresh’s comment.

8 Comments

  1. AvatarSuresh
    3:13 on November 11th, 2007

    I was trying to do this for a talk that uses beamer (I’m not sure if all of this works with pdflatex).

    In any case, first of all, the file that gets created for me is preamble.fmt, not main.fmt. So I renamed it.

    But then, when I tried to latex main.tex, (or pdflatex it), I got all kinds of errors that didn’t appear in the original latex workflow

  2. The principle applies to pdflatex and I’ve just tried it successfully using conference-ornate-20min.en.tex from beamer and pdflatex. I am guessing it has something to do with your distribution.

    My main concern is that you do not get main.fmt at all. There was indeed an earlier revision of this article that gives you preamble.fmt. But since you know you should be getting main.fmt instead, I suppose you are already following the updated instructions.

    I have uploaded a zip file that records all my steps and all the files I get in my experiment using MiKTeX 2.6. See the updated post. The commands I have executed are in go.bat and you can look at transcript.txt for the output. The tex files are there so that you can run the experiment on your machine to find the discrepancy.

    Let me also take this chance to note the running time I observed. It takes about 7 seconds to get the preamble precompiled. Then each run takes 3 to 4 seconds. But without the precompiled preamble, each run takes 9 to 11 seconds. The time saving is very real.

    Finally, feel free to send me your directory including the log and aux files. I am sure you know my email address. :P

  3. AvatarJan-Henrik Horstmann
    11:01 on December 20th, 2007

    It works great - as long as I compile from the command prompt… Simply typing xelatex main.tex does the job.
    But when I run LaTeX (XeLaTeX) from GNU Emacs (w/ AUCTeX), then it doesn’t work and preamble.tex is used… The log file says: Running `LaTeX’ on `main’ with “xelatex -interaction=nonstopmode “\input” “main.tex””

    Does that have something to do with nonstopmode or input? How can I change that in Emacs?
    Thanks a lot for any hints! :-)

  4. You want to change your TeX-command-list variable. Here is one way you can do it:

    M-x customize-variable ENTER TeX-command-list ENTER

    In the customization screen, find the LaTeX entry. The command you would see is probably:
    %l -interaction=nonstopmode \input %t
    (This is my guess based on your log file quote.)

    You want to change the command to
    %l -interaction=nonstopmode %t
    (Basically, take out the \input.)

    Select “Save for Future Sessions” and restart Emacs.

  5. AvatarJan-Henrik Horstmann
    15:16 on December 29th, 2007

    Sorry for my late reply this time - the holidays were just a busy time… :-)
    So, I got to the customization page but I haven’t been able to find the “interaction” command. The LaTeX command is the following:

    %`%l%(mode)%’

    Does this tell you anything? Changing the command to just %t doesn’t work… :-(

    Thanks again for the help!

    Kind regards,

    Jan-Henrik

  6. It’s still the \input problem, and it’s hardcoded in the expansion of the %'. Assuming that you don’t have spaces in your file name (and path name), try this:

    %l%(mode) %t

  7. AvatarJan-Henrik Horstmann
    12:44 on January 14th, 2008

    That actually works… :) Thank you! :)

  8. I don’t think the “-job-name=”main” ” in the command below is necessary
    latex -ini -job-name=”main” “&latex preamble.tex\dump”
    Exec-ing, instead
    latex -ini “&latex preamble \dump”
    generates preamble.fmt. Also note that i’ve left out the file extension in preamble.tex. (La)TeX knows what file types to look for.
    I tried both variants, with identical results. The latter makes more sense to me however; since we’re (pre-)loading the contents of preamble into main.
    Thanks for your post, which inspired me to start my own blog today :)