Writing TeX is an integral part of my studies. I usually have Vim and zathura open side-by-side and a simple script recompile the TeX source file on changes. With plain TeX and several dozen custom macros, each compile takes about 160 milliseconds.
When battery-powered, however, my laptop does some weird powersave thing that doubles the compile times, and over 300 milliseconds of latency is very distracting. How can I speed up pdfTeX?
I started off with its
source code, which only made me confused. The pdfTeX binary on my system is
stripped, and I know very little about gdb
or profiling, so that
is not gonna work either.
This stayed in the back of my mind for a few weeks, until I realized that,
well, pdfTeX is not a very fancy program: it simply reads a bunch of files,
does some macro and layout stuff, and produces a PDF file. My copy of pdfTeX
happened to be dynamically linked, so I could trace fopen
and
fclose
calls with LD_PRELOAD
.
#include <dlfcn.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> static struct node { struct node *next; char *path; FILE *fptr; } *open_files; static unsigned int time_ns(void) { struct timespec t; clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t); return t.tv_nsec; } FILE *fopen(char const *path, char const *mode) { static FILE *(*foo)(char const *, char const *); if (!foo) foo = dlsym(RTLD_NEXT, "fopen"); fprintf(stderr, "%09u fopen %s, %s\n", time_ns(), path, mode); FILE *ret = foo(path, mode); struct node *head = malloc(sizeof(*head)); head->next = open_files; head->path = strdup(path); head->fptr = ret; open_files = head; return ret; } int fclose(FILE *stream) { static int (*foo)(FILE *); if (!foo) foo = dlsym(RTLD_NEXT, "fclose"); char *path = "unknown"; for (struct node *p = open_files; p; p = p->next) { if (p->fptr == stream) { path = p->path; break; } } fprintf(stderr, "%09u fclose %s\n", time_ns(), path); return foo(stream); }
With the above in preload.c
and gentle.tex
from here:
$ gcc -fPIC -shared -O2 preload.c -o preload.so $ LD_PRELOAD=./preload.so pdftex gentle.tex >/dev/null 001377884 fopen /path/to/texlive/texmf.cnf, r 001413741 fclose /path/to/texlive/texmf.cnf 001418630 fopen /path/to/texlive/texmf-dist/web2c/texmf.cnf, r 001642500 fclose /path/to/texlive/texmf-dist/web2c/texmf.cnf 002040245 fopen /path/to/texlive/texmf-config/ls-R, r 002054983 fclose /path/to/texlive/texmf-config/ls-R 002058449 fopen /path/to/texlive/texmf-var/ls-R, r 002113944 fclose /path/to/texlive/texmf-var/ls-R 002117160 fopen /path/to/texlive/texmf-dist/ls-R, r 064456512 fclose /path/to/texlive/texmf-dist/ls-R 064595393 fopen ./gentle.tex, r 064602586 fclose ./gentle.tex 064902187 fopen /path/to/texlive/texmf-var/web2c/pdftex/pdftex.fmt, rb 080705319 fopen gentle.tex, rb 080718764 fopen gentle.log, wb 081032953 fopen /path/to/texlive/texmf-dist/fonts/map/fontname/texfonts.map, r 081059403 fclose /path/to/texlive/texmf-dist/fonts/map/fontname/texfonts.map 081101492 fopen /path/to/texlive/texmf-dist/fonts/tfm/public/cm/cmr10.tfm, rb 081113404 fclose /path/to/texlive/texmf-dist/fonts/tfm/public/cm/cmr10.tfm 081146336 fopen /path/to/texlive/texmf-dist/fonts/tfm/public/cm/cmr10.tfm, rb 081155824 fclose /path/to/texlive/texmf-dist/fonts/tfm/public/cm/cmr10.tfm 081186211 fopen /path/to/texlive/texmf-dist/fonts/tfm/public/cm/cmr10.tfm, rb 081194035 fclose /path/to/texlive/texmf-dist/fonts/tfm/public/cm/cmr10.tfm 081223000 fopen /path/to/texlive/texmf-dist/fonts/tfm/public/cm/cmr10.tfm, rb 081230253 fclose /path/to/texlive/texmf-dist/fonts/tfm/public/cm/cmr10.tfm 081262313 fopen /path/to/texlive/texmf-dist/fonts/tfm/public/cm/cmr10.tfm, rb 081271701 fclose /path/to/texlive/texmf-dist/fonts/tfm/public/cm/cmr10.tfm 081301226 fopen /path/to/texlive/texmf-dist/fonts/tfm/public/cm/cmr10.tfm, rb 081310173 fclose /path/to/texlive/texmf-dist/fonts/tfm/public/cm/cmr10.tfm 081344848 fopen /path/to/texlive/texmf-dist/fonts/tfm/public/cm/cmbx10.tfm, rb 081352542 fclose /path/to/texlive/texmf-dist/fonts/tfm/public/cm/cmbx10.tfm 081380695 fopen /path/to/texlive/texmf-dist/fonts/tfm/public/cm/cmbx10.tfm, rb 081388219 fclose /path/to/texlive/texmf-dist/fonts/tfm/public/cm/cmbx10.tfm 081542408 fopen gentle.pdf, wb 081701166 fopen /path/to/texlive/texmf-var/fonts/map/pdftex/updmap/pdftex.map, rb 133196806 fclose /path/to/texlive/texmf-var/fonts/map/pdftex/updmap/pdftex.map 159032589 fclose gentle.tex 159423773 fopen /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmbx10.pfb, rb 159910675 fclose /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmbx10.pfb 160242698 fopen /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmcsc10.pfb, rb 160534054 fclose /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmcsc10.pfb 160655762 fopen /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmex10.pfb, rb 161011600 fclose /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmex10.pfb 161197247 fopen /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi10.pfb, rb 161623598 fclose /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi10.pfb 161926666 fopen /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi5.pfb, rb 162242328 fclose /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi5.pfb 162363384 fopen /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi7.pfb, rb 162691489 fclose /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi7.pfb 162835419 fopen /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb, rb 163397643 fclose /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb 163874386 fopen /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr5.pfb, rb 164163889 fclose /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr5.pfb 164284325 fopen /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr7.pfb, rb 164634781 fclose /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr7.pfb 164820740 fopen /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmsl10.pfb, rb 165204609 fclose /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmsl10.pfb 165432115 fopen /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmss10.pfb, rb 165704135 fclose /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmss10.pfb 165842334 fopen /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy10.pfb, rb 166237475 fclose /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy10.pfb 166515936 fopen /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy7.pfb, rb 166811911 fclose /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy7.pfb 166934270 fopen /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmti10.pfb, rb 167316396 fclose /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmti10.pfb 167510219 fopen /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmtt10.pfb, rb 167944233 fclose /path/to/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmtt10.pfb 172672187 fclose gentle.pdf 172772595 fclose gentle.log
Just look at this. Noticed something? It took curiously long to process
texmf-dist/ls-R
(62 ms) and
texmf-var/fonts/map/pdftex/updmap/pdftex.map
(51 ms).
Together, that is over 60% of the total runtime!
But why? Well, they are text files, each over 5 MiB in size. Like, what the fuck, pdfTeX parses ten megabytes of text when it compiles my dozen-kilobyte homework?
So how useful are they? Well, ls-R
is a recursive list of all
files in texmf-dist
, and pdftex.map
is a list of all
fonts that pdfTeX knows about.
Most of which never gets used at all.
Now, the fix should become obvious. Just create a dummy ls-R
and a dummy pdftex.map
that only contains what I actually use, and
tell pdfTeX to read these instead. You can use \pdfmapfile
to make
pdfTeX use some other map file than pdftex.map
, but you are out of
luck for ls-R
. There is a simple way though — just check and
modify the argument to fopen
:
if (!strcmp(path, "/path/to/texlive/texmf-dist/ls-R")) path = "/path/to/dummy/ls-R"; if (!strcmp(path, "/path/to/texlive/texmf-var/fonts/map/pdftex/updmap/pdftex.map")) path = "/path/to/dummy/pdftex.map";
This makes pdfTeX run ~3x faster, or ~5x for a few pages of homework.
This is of course still not ideal. Startup overhead still dominates the runtime for short documents, and I should probably figure out why my laptop runs at half speed on battery. But this is good enough for me, for now.
It is rather dumb once you figure it out.
Note: I was running Fedora Linux 40 with many texlive packages installed
from the package manager when I wrote the original post. In the meantime I
switched (back) to Arch Linux and installed texlive-basic
and
texlive-fontsrecommended
only, which made pdfTeX way snappier. The
results above were updated with tests with a portable full install of TeX Live,
where the problem turned out to be worse.